Update fmt to 10.0.0, lua to 5.4.6

This commit is contained in:
halx99 2023-05-16 15:47:55 +08:00
parent ef2cdd5bb1
commit 795522a078
83 changed files with 4293 additions and 6787 deletions

View File

@ -57,7 +57,7 @@
## {fmt} ## {fmt}
- [![Upstream](https://img.shields.io/github/v/release/fmtlib/fmt?label=Upstream)](https://github.com/fmtlib/fmt) - [![Upstream](https://img.shields.io/github/v/release/fmtlib/fmt?label=Upstream)](https://github.com/fmtlib/fmt)
- Version: 9.1.0 - Version: 10.0.0
- License: MIT - License: MIT
## FreeType ## FreeType
@ -108,7 +108,7 @@
## lua ## lua
- plainlua - plainlua
- [![Upstream](https://img.shields.io/github/v/release/lua/lua?label=Upstream)](https://github.com/lua/lua) http://www.lua.org/ - [![Upstream](https://img.shields.io/github/v/release/lua/lua?label=Upstream)](https://github.com/lua/lua) http://www.lua.org/
- Version: 5.4.4 - Version: 5.4.6
- License: MIT (http://www.lua.org/license.html) - License: MIT (http://www.lua.org/license.html)
- sol2 - sol2

View File

@ -57,7 +57,7 @@
## {fmt} ## {fmt}
- [![Upstream](https://img.shields.io/github/v/release/fmtlib/fmt?label=Upstream)](https://github.com/fmtlib/fmt) - [![Upstream](https://img.shields.io/github/v/release/fmtlib/fmt?label=Upstream)](https://github.com/fmtlib/fmt)
- Version: 9.1.0 - Version: 10.0.0
- License: MIT - License: MIT
## FreeType ## FreeType
@ -108,7 +108,7 @@
## lua ## lua
- plainlua - plainlua
- [![Upstream](https://img.shields.io/github/v/release/lua/lua?label=Upstream)](https://github.com/lua/lua) http://www.lua.org/ - [![Upstream](https://img.shields.io/github/v/release/lua/lua?label=Upstream)](https://github.com/lua/lua) http://www.lua.org/
- Version: 5.4.4 - Version: 5.4.6
- License: MIT (http://www.lua.org/license.html) - License: MIT (http://www.lua.org/license.html)
- sol2 - sol2

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.1...3.18) cmake_minimum_required(VERSION 3.8...3.26)
# Fallback for using newer policies on CMake <3.12. # Fallback for using newer policies on CMake <3.12.
if(${CMAKE_VERSION} VERSION_LESS 3.12) if(${CMAKE_VERSION} VERSION_LESS 3.12)
@ -24,15 +24,86 @@ function(join result_var)
set(${result_var} "${result}" PARENT_SCOPE) set(${result_var} "${result}" PARENT_SCOPE)
endfunction() endfunction()
# DEPRECATED! Should be merged into add_module_library.
function(enable_module target) function(enable_module target)
if (MSVC) if (MSVC)
set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc) set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
target_compile_options(${target} target_compile_options(${target}
PRIVATE /interface /ifcOutput ${BMI} PRIVATE /interface /ifcOutput ${BMI}
INTERFACE /reference fmt=${BMI}) INTERFACE /reference fmt=${BMI})
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
endif () endif ()
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI}) endfunction()
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
# Adds a library compiled with C++20 module support.
# `enabled` is a CMake variables that specifies if modules are enabled.
# If modules are disabled `add_module_library` falls back to creating a
# non-modular library.
#
# Usage:
# add_module_library(<name> [sources...] FALLBACK [sources...] [IF enabled])
function(add_module_library name)
cmake_parse_arguments(AML "" "IF" "FALLBACK" ${ARGN})
set(sources ${AML_UNPARSED_ARGUMENTS})
add_library(${name})
set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX)
if (NOT ${${AML_IF}})
# Create a non-modular library.
target_sources(${name} PRIVATE ${AML_FALLBACK})
return()
endif ()
# Modules require C++20.
target_compile_features(${name} PUBLIC cxx_std_20)
if (CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(${name} PUBLIC -fmodules-ts)
endif ()
# `std` is affected by CMake options and may be higher than C++20.
get_target_property(std ${name} CXX_STANDARD)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(pcms)
foreach (src ${sources})
get_filename_component(pcm ${src} NAME_WE)
set(pcm ${pcm}.pcm)
# Propagate -fmodule-file=*.pcm to targets that link with this library.
target_compile_options(
${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm})
# Use an absolute path to prevent target_link_libraries prepending -l
# to it.
set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm})
add_custom_command(
OUTPUT ${pcm}
COMMAND ${CMAKE_CXX_COMPILER}
-std=c++${std} -x c++-module --precompile -c
-o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src}
"-I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-I>"
# Required by the -I generator expression above.
COMMAND_EXPAND_LISTS
DEPENDS ${src})
endforeach ()
# Add .pcm files as sources to make sure they are built before the library.
set(sources)
foreach (pcm ${pcms})
get_filename_component(pcm_we ${pcm} NAME_WE)
set(obj ${pcm_we}.o)
# Use an absolute path to prevent target_link_libraries prepending -l.
set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj})
add_custom_command(
OUTPUT ${obj}
COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS>
-c -o ${obj} ${pcm}
DEPENDS ${pcm})
endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources})
endfunction() endfunction()
include(CMakeParseArguments) include(CMakeParseArguments)
@ -75,7 +146,7 @@ option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
# Options that control generation of various targets. # Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT}) option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT}) option(FMT_INSTALL "Generate the install target." ON)
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT}) option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF) option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
@ -83,16 +154,6 @@ option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
option(FMT_MODULE "Build a module instead of a traditional library." OFF) option(FMT_MODULE "Build a module instead of a traditional library." OFF)
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF) option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
set(FMT_CAN_MODULE OFF)
if (CMAKE_CXX_STANDARD GREATER 17 AND
# msvc 16.10-pre4
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
set(FMT_CAN_MODULE OFF)
endif ()
if (NOT FMT_CAN_MODULE)
set(FMT_MODULE OFF)
message(STATUS "Module support is disabled.")
endif ()
if (FMT_TEST AND FMT_MODULE) if (FMT_TEST AND FMT_MODULE)
# The tests require {fmt} to be compiled as traditional library # The tests require {fmt} to be compiled as traditional library
message(STATUS "Testing is incompatible with build mode 'module'.") message(STATUS "Testing is incompatible with build mode 'module'.")
@ -101,6 +162,10 @@ set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
if (FMT_SYSTEM_HEADERS) if (FMT_SYSTEM_HEADERS)
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
endif () endif ()
if(CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
set(FMT_TEST OFF)
message(STATUS "MSDOS is incompatible with gtest")
endif()
# Get version from core.h # Get version from core.h
file(READ include/fmt/core.h core_h) file(READ include/fmt/core.h core_h)
@ -118,23 +183,15 @@ message(STATUS "Version: ${FMT_VERSION}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
endif () endif ()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(cxx14) include(CheckCXXCompilerFlag)
include(JoinPaths) include(JoinPaths)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
if (${index} GREATER -1)
# Use cxx_variadic_templates instead of more appropriate cxx_std_11 for
# compatibility with older CMake versions.
set(FMT_REQUIRED_FEATURES cxx_variadic_templates)
endif ()
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET)
set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING
"Preset for the export of private symbols") "Preset for the export of private symbols")
@ -220,16 +277,18 @@ endfunction()
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
format-inl.h os.h ostream.h printf.h ranges.h std.h format-inl.h os.h ostream.h printf.h ranges.h std.h
xchar.h) xchar.h)
if (FMT_MODULE) set(FMT_SOURCES src/format.cc)
set(FMT_SOURCES src/fmt.cc) if (FMT_OS)
elseif (FMT_OS) set(FMT_SOURCES ${FMT_SOURCES} src/os.cc)
set(FMT_SOURCES src/format.cc src/os.cc)
else()
set(FMT_SOURCES src/format.cc)
endif () endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) add_module_library(fmt src/fmt.cc FALLBACK
${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst
IF FMT_MODULE)
add_library(fmt::fmt ALIAS fmt) add_library(fmt::fmt ALIAS fmt)
if (FMT_MODULE)
enable_module(fmt)
endif ()
if (FMT_WERROR) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
@ -237,11 +296,8 @@ endif ()
if (FMT_PEDANTIC) if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
if (FMT_MODULE)
enable_module(fmt)
endif ()
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES}) target_compile_features(fmt PUBLIC cxx_std_11)
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
@ -262,7 +318,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
endif () endif ()
if (BUILD_SHARED_LIBS) if (BUILD_SHARED_LIBS)
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED) target_compile_definitions(fmt PRIVATE FMT_LIB_EXPORT INTERFACE FMT_SHARED)
endif () endif ()
if (FMT_SAFE_DURATION_CAST) if (FMT_SAFE_DURATION_CAST)
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST) target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
@ -272,7 +328,7 @@ add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only) add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES}) target_compile_features(fmt-header-only INTERFACE cxx_std_11)
target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
@ -339,8 +395,6 @@ if (FMT_INSTALL)
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::) NAMESPACE fmt::)
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()

View File

@ -1,3 +1,434 @@
10.0.0 - 2023-05-09
-------------------
* Replaced Grisu with a new floating-point formatting algorithm for given
precision (`#3262 <https://github.com/fmtlib/fmt/issues/3262>`_,
`#2750 <https://github.com/fmtlib/fmt/issues/2750>`_,
`#3269 <https://github.com/fmtlib/fmt/pull/3269>`_,
`#3276 <https://github.com/fmtlib/fmt/pull/3276>`_).
The new algorithm is based on Dragonbox already used for the
shortest representation and gives substantial performance improvement:
.. image:: https://user-images.githubusercontent.com/33922675/
211956670-84891a09-6867-47d9-82fc-3230da7abe0f.png
* Red: new algorithm
* Green: new algorithm with ``FMT_USE_FULL_CACHE_DRAGONBOX`` defined to 1
* Blue: old algorithm
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
* Replaced ``snprintf``-based hex float formatter with an internal
implementation (`#3179 <https://github.com/fmtlib/fmt/pull/3179>`_,
`#3203 <https://github.com/fmtlib/fmt/pull/3203>`_).
This removes the last usage of ``s(n)printf`` in {fmt}.
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Fixed alignment of floating-point numbers with localization
(`#3263 <https://github.com/fmtlib/fmt/issues/3263>`_,
`#3272 <https://github.com/fmtlib/fmt/pull/3272>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Improved C++20 module support
(`#3134 <https://github.com/fmtlib/fmt/pull/3134>`_,
`#3254 <https://github.com/fmtlib/fmt/pull/3254>`_,
`#3386 <https://github.com/fmtlib/fmt/pull/3386>`_,
`#3387 <https://github.com/fmtlib/fmt/pull/3387>`_,
`#3388 <https://github.com/fmtlib/fmt/pull/3388>`_,
`#3392 <https://github.com/fmtlib/fmt/pull/3392>`_,
`#3397 <https://github.com/fmtlib/fmt/pull/3397>`_,
`#3399 <https://github.com/fmtlib/fmt/pull/3399>`_,
`#3400 <https://github.com/fmtlib/fmt/pull/3400>`_).
Thanks `@laitingsheng (Tinson Lai) <https://github.com/laitingsheng>`_,
`@Orvid (Orvid King) <https://github.com/Orvid>`_,
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
Switched to the `modules CMake library <https://github.com/vitaut/modules>`_
which allows building {fmt} as a C++20 module with clang::
CXX=clang++ cmake -DFMT_MODULE=ON .
make
* Made ``format_as`` work with any user-defined type and not just enums.
For example (`godbolt <https://godbolt.org/z/b7rqhq5Kh>`__):
.. code:: c++
#include <fmt/format.h>
struct floaty_mc_floatface {
double value;
};
auto format_as(floaty_mc_floatface f) { return f.value; }
int main() {
fmt::print("{:8}\n", floaty_mc_floatface{0.42}); // prints " 0.42"
}
* Removed deprecated implicit conversions for enums and conversions to primitive
types for compatibility with ``std::format`` and to prevent potential ODR
violations. Use ``format_as`` instead.
* Added support for fill, align and width to the time point formatter
(`#3237 <https://github.com/fmtlib/fmt/issues/3237>`_,
`#3260 <https://github.com/fmtlib/fmt/pull/3260>`_,
`#3275 <https://github.com/fmtlib/fmt/pull/3275>`_).
For example (`godbolt <https://godbolt.org/z/rKP6MGz6c>`__):
.. code:: c++
#include <fmt/chrono.h>
int main() {
// prints " 2023"
fmt::print("{:>8%Y}\n", std::chrono::system_clock::now());
}
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Implemented formatting of subseconds
(`#2207 <https://github.com/fmtlib/fmt/issues/2207>`_,
`#3117 <https://github.com/fmtlib/fmt/issues/3117>`_,
`#3115 <https://github.com/fmtlib/fmt/pull/3115>`_,
`#3143 <https://github.com/fmtlib/fmt/pull/3143>`_,
`#3144 <https://github.com/fmtlib/fmt/pull/3144>`_,
`#3349 <https://github.com/fmtlib/fmt/pull/3349>`_).
For example (`godbolt <https://godbolt.org/z/45738oGEo>`__):
.. code:: c++
#include <fmt/chrono.h>
int main() {
// prints 01.234567
fmt::print("{:%S}\n", std::chrono::microseconds(1234567));
}
Thanks `@patrickroocks (Patrick Roocks) <https://github.com/patrickroocks>`_
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Added precision support to ``%S``
(`#3148 <https://github.com/fmtlib/fmt/pull/3148>`_).
Thanks `@SappyJoy (Stepan Ponomaryov) <https://github.com/SappyJoy>`_
* Added support for ``std::utc_time``
(`#3098 <https://github.com/fmtlib/fmt/issues/3098>`_,
`#3110 <https://github.com/fmtlib/fmt/pull/3110>`_).
Thanks `@patrickroocks (Patrick Roocks) <https://github.com/patrickroocks>`_.
* Switched formatting of ``std::chrono::system_clock`` from local time to UTC
for compatibility with the standard
(`#3199 <https://github.com/fmtlib/fmt/issues/3199>`_,
`#3230 <https://github.com/fmtlib/fmt/pull/3230>`_).
Thanks `@ned14 (Niall Douglas) <https://github.com/ned14>`_.
* Added support for ``%Ez`` and ``%Oz`` to chrono formatters.
(`#3220 <https://github.com/fmtlib/fmt/issues/3220>`_,
`#3222 <https://github.com/fmtlib/fmt/pull/3222>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Improved validation of format specifiers for ``std::chrono::duration``
(`#3219 <https://github.com/fmtlib/fmt/issues/3219>`_,
`#3232 <https://github.com/fmtlib/fmt/pull/3232>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Fixed formatting of time points before the epoch
(`#3117 <https://github.com/fmtlib/fmt/issues/3117>`_,
`#3261 <https://github.com/fmtlib/fmt/pull/3261>`_).
For example (`godbolt <https://godbolt.org/z/f7bcznb3W>`__):
.. code:: c++
#include <fmt/chrono.h>
int main() {
auto t = std::chrono::system_clock::from_time_t(0) -
std::chrono::milliseconds(250);
fmt::print("{:%S}\n", t); // prints 59.750000000
}
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Experimental: implemented glibc extension for padding seconds, minutes and
hours (`#2959 <https://github.com/fmtlib/fmt/issues/2959>`_,
`#3271 <https://github.com/fmtlib/fmt/pull/3271>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Added a formatter for ``std::exception``
(`#2977 <https://github.com/fmtlib/fmt/issues/2977>`_,
`#3012 <https://github.com/fmtlib/fmt/issues/3012>`_,
`#3062 <https://github.com/fmtlib/fmt/pull/3062>`_,
`#3076 <https://github.com/fmtlib/fmt/pull/3076>`_,
`#3119 <https://github.com/fmtlib/fmt/pull/3119>`_).
For example (`godbolt <https://godbolt.org/z/8xoWGs9e4>`__):
.. code:: c++
#include <fmt/std.h>
#include <vector>
int main() {
try {
std::vector<bool>().at(0);
} catch(const std::exception& e) {
fmt::print("{}", e);
}
}
prints::
vector<bool>::_M_range_check: __n (which is 0) >= this->size() (which is 0)
on libstdc++.
Thanks `@zach2good (Zach Toogood) <https://github.com/zach2good>`_ and
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Moved ``std::error_code`` formatter from ``fmt/os.h`` to ``fmt/std.h``.
(`#3125 <https://github.com/fmtlib/fmt/pull/3125>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added formatters for standard container adapters: ``std::priority_queue``,
``std::queue`` and ``std::stack``
(`#3215 <https://github.com/fmtlib/fmt/issues/3215>`_,
`#3279 <https://github.com/fmtlib/fmt/pull/3279>`_).
For example (`godbolt <https://godbolt.org/z/74h1xY9qK>`__):
.. code:: c++
#include <fmt/ranges.h>
#include <stack>
#include <vector>
int main() {
auto s = std::stack<bool, std::vector<bool>>();
for (auto b: {true, false, true}) s.push(b);
fmt::print("{}\n", s); // prints [true, false, true]
}
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Added a formatter for ``std::optional`` to ``fmt/std.h``.
Thanks `@tom-huntington <https://github.com/tom-huntington>`_.
* Fixed formatting of valueless by exception variants
(`#3347 <https://github.com/fmtlib/fmt/pull/3347>`_).
Thanks `@TheOmegaCarrot <https://github.com/TheOmegaCarrot>`_.
* Made ``fmt::ptr`` accept ``unique_ptr`` with a custom deleter
(`#3177 <https://github.com/fmtlib/fmt/pull/3177>`_).
Thanks `@hmbj (Hans-Martin B. Jensen) <https://github.com/hmbj>`_.
* Fixed formatting of noncopyable ranges and nested ranges of chars
(`#3158 <https://github.com/fmtlib/fmt/pull/3158>`_
`#3286 <https://github.com/fmtlib/fmt/issues/3286>`_,
`#3290 <https://github.com/fmtlib/fmt/pull/3290>`_).
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Fixed issues with formatting of paths and ranges of paths
(`#3319 <https://github.com/fmtlib/fmt/issues/3319>`_,
`#3321 <https://github.com/fmtlib/fmt/pull/3321>`_
`#3322 <https://github.com/fmtlib/fmt/issues/3322>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Improved handling of invalid Unicode in paths.
* Enabled compile-time checks on Apple clang 14 and later
(`#3331 <https://github.com/fmtlib/fmt/pull/3331>`_).
Thanks `@cloyce (Cloyce D. Spradling) <https://github.com/cloyce>`_.
* Improved compile-time checks of named arguments
(`#3105 <https://github.com/fmtlib/fmt/issues/3105>`_,
`#3214 <https://github.com/fmtlib/fmt/pull/3214>`_).
Thanks `@rbrich (Radek Brich) <https://github.com/rbrich>`_.
* Fixed formatting when both alignment and ``0`` are given
(`#3236 <https://github.com/fmtlib/fmt/issues/3236>`_,
`#3248 <https://github.com/fmtlib/fmt/pull/3248>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Improved Unicode support in the experimental file API on Windows
(`#3234 <https://github.com/fmtlib/fmt/issues/3234>`_,
`#3293 <https://github.com/fmtlib/fmt/pull/3293>`_).
Thanks `@Fros1er (Froster) <https://github.com/Fros1er>`_.
* Unified UTF transcoding
(`#3416 <https://github.com/fmtlib/fmt/pull/3416>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added support for UTF-8 digit separators via an experimental locale facet
(`#1861 <https://github.com/fmtlib/fmt/issues/1861>`_).
For example (`godbolt <https://godbolt.org/z/f7bcznb3W>`__):
.. code:: c++
auto loc = std::locale(
std::locale(), new fmt::format_facet<std::locale>(""));
auto s = fmt::format(loc, "{:L}", 1000);
where ```` is U+2019 used as a digit separator in the de_CH locale.
* Added an overload of ``formatted_size`` that takes a locale
(`#3084 <https://github.com/fmtlib/fmt/issues/3084>`_,
`#3087 <https://github.com/fmtlib/fmt/pull/3087>`_).
Thanks `@gerboengels <https://github.com/gerboengels>`_.
* Removed the deprecated ``FMT_DEPRECATED_OSTREAM``.
* Fixed a UB when using a null ``std::string_view`` with ``fmt::to_string``
or format string compilation
(`#3241 <https://github.com/fmtlib/fmt/issues/3241>`_,
`#3244 <https://github.com/fmtlib/fmt/pull/3244>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added ``starts_with`` to the fallback ``string_view`` implementation
(`#3080 <https://github.com/fmtlib/fmt/pull/3080>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added ``fmt::basic_format_string::get()`` for compatibility with
``basic_format_string`` (`#3111 <https://github.com/fmtlib/fmt/pull/3111>`_).
Thanks `@huangqinjin <https://github.com/huangqinjin>`_.
* Added ``println`` for compatibility with C++23
(`#3267 <https://github.com/fmtlib/fmt/pull/3267>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Improved documentation
(`#3108 <https://github.com/fmtlib/fmt/issues/3108>`_,
`#3169 <https://github.com/fmtlib/fmt/issues/3169>`_,
`#3243 <https://github.com/fmtlib/fmt/pull/3243>`_).
`#3404 <https://github.com/fmtlib/fmt/pull/3404>`_).
Thanks `@Cleroth <https://github.com/Cleroth>`_ and
`@Vertexwahn <https://github.com/Vertexwahn>`_.
* Improved build configuration and tests
(`#3118 <https://github.com/fmtlib/fmt/pull/3118>`_,
`#3120 <https://github.com/fmtlib/fmt/pull/3120>`_,
`#3188 <https://github.com/fmtlib/fmt/pull/3188>`_,
`#3189 <https://github.com/fmtlib/fmt/issues/3189>`_,
`#3198 <https://github.com/fmtlib/fmt/pull/3198>`_,
`#3205 <https://github.com/fmtlib/fmt/pull/3205>`_,
`#3207 <https://github.com/fmtlib/fmt/pull/3207>`_,
`#3210 <https://github.com/fmtlib/fmt/pull/3210>`_,
`#3240 <https://github.com/fmtlib/fmt/pull/3240>`_,
`#3256 <https://github.com/fmtlib/fmt/pull/3256>`_,
`#3264 <https://github.com/fmtlib/fmt/pull/3264>`_,
`#3299 <https://github.com/fmtlib/fmt/issues/3299>`_,
`#3302 <https://github.com/fmtlib/fmt/pull/3302>`_,
`#3312 <https://github.com/fmtlib/fmt/pull/3312>`_,
`#3317 <https://github.com/fmtlib/fmt/issues/3317>`_,
`#3328 <https://github.com/fmtlib/fmt/pull/3328>`_,
`#3333 <https://github.com/fmtlib/fmt/pull/3333>`_,
`#3369 <https://github.com/fmtlib/fmt/pull/3369>`_,
`#3373 <https://github.com/fmtlib/fmt/issues/3373>`_,
`#3395 <https://github.com/fmtlib/fmt/pull/3395>`_,
`#3406 <https://github.com/fmtlib/fmt/pull/3406>`_,
`#3411 <https://github.com/fmtlib/fmt/pull/3411>`_).
Thanks `@dimztimz (Dimitrij Mijoski) <https://github.com/dimztimz>`_,
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@DavidKorczynski <https://github.com/DavidKorczynski>`_,
`@ChrisThrasher (Chris Thrasher) <https://github.com/ChrisThrasher>`_,
`@FrancoisCarouge (François Carouge) <https://github.com/FrancoisCarouge>`_,
`@kennyweiss (Kenny Weiss) <https://github.com/kennyweiss>`_,
`@luzpaz <https://github.com/luzpaz>`_,
`@codeinred (Alecto Irene Perez) <https://github.com/codeinred>`_,
`@Mixaill (Mikhail Paulyshka) <https://github.com/Mixaill>`_,
`@joycebrum (Joyce) <https://github.com/joycebrum>`_,
`@kevinhwang (Kevin Hwang) <https://github.com/kevinhwang>`_,
`@Vertexwahn <https://github.com/Vertexwahn>`_.
* Fixed a regression in handling empty format specifiers after a colon (``{:}``)
(`#3086 <https://github.com/fmtlib/fmt/pull/3086>`_).
Thanks `@oxidase (Michael Krasnyk) <https://github.com/oxidase>`_.
* Worked around a broken implementation of ``std::is_constant_evaluated`` in
some versions of libstdc++ on clang
(`#3247 <https://github.com/fmtlib/fmt/issues/3247>`_,
`#3281 <https://github.com/fmtlib/fmt/pull/3281>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Fixed formatting of volatile variables
(`#3068 <https://github.com/fmtlib/fmt/pull/3068>`_).
* Fixed various warnings and compilation issues
(`#3057 <https://github.com/fmtlib/fmt/pull/3057>`_,
`#3066 <https://github.com/fmtlib/fmt/pull/3066>`_,
`#3072 <https://github.com/fmtlib/fmt/pull/3072>`_,
`#3082 <https://github.com/fmtlib/fmt/pull/3082>`_,
`#3091 <https://github.com/fmtlib/fmt/pull/3091>`_,
`#3092 <https://github.com/fmtlib/fmt/issues/3092>`_,
`#3093 <https://github.com/fmtlib/fmt/pull/3093>`_,
`#3095 <https://github.com/fmtlib/fmt/pull/3095>`_,
`#3096 <https://github.com/fmtlib/fmt/issues/3096>`_,
`#3097 <https://github.com/fmtlib/fmt/pull/3097>`_,
`#3128 <https://github.com/fmtlib/fmt/issues/3128>`_,
`#3129 <https://github.com/fmtlib/fmt/pull/3129>`_,
`#3137 <https://github.com/fmtlib/fmt/pull/3137>`_,
`#3139 <https://github.com/fmtlib/fmt/pull/3139>`_,
`#3140 <https://github.com/fmtlib/fmt/issues/3140>`_,
`#3142 <https://github.com/fmtlib/fmt/pull/3142>`_,
`#3149 <https://github.com/fmtlib/fmt/issues/3149>`_,
`#3150 <https://github.com/fmtlib/fmt/pull/3150>`_,
`#3154 <https://github.com/fmtlib/fmt/issues/3154>`_,
`#3163 <https://github.com/fmtlib/fmt/issues/3163>`_,
`#3178 <https://github.com/fmtlib/fmt/issues/3178>`_,
`#3184 <https://github.com/fmtlib/fmt/pull/3184>`_,
`#3196 <https://github.com/fmtlib/fmt/pull/3196>`_,
`#3204 <https://github.com/fmtlib/fmt/issues/3204>`_,
`#3206 <https://github.com/fmtlib/fmt/pull/3206>`_,
`#3208 <https://github.com/fmtlib/fmt/pull/3208>`_,
`#3213 <https://github.com/fmtlib/fmt/issues/3213>`_,
`#3216 <https://github.com/fmtlib/fmt/pull/3216>`_,
`#3224 <https://github.com/fmtlib/fmt/issues/3224>`_,
`#3226 <https://github.com/fmtlib/fmt/issues/3226>`_,
`#3228 <https://github.com/fmtlib/fmt/issues/3228>`_,
`#3229 <https://github.com/fmtlib/fmt/pull/3229>`_,
`#3259 <https://github.com/fmtlib/fmt/pull/3259>`_,
`#3274 <https://github.com/fmtlib/fmt/issues/3274>`_,
`#3287 <https://github.com/fmtlib/fmt/issues/3287>`_,
`#3288 <https://github.com/fmtlib/fmt/pull/3288>`_,
`#3292 <https://github.com/fmtlib/fmt/issues/3292>`_,
`#3295 <https://github.com/fmtlib/fmt/pull/3295>`_,
`#3296 <https://github.com/fmtlib/fmt/pull/3296>`_,
`#3298 <https://github.com/fmtlib/fmt/issues/3298>`_,
`#3325 <https://github.com/fmtlib/fmt/issues/3325>`_,
`#3326 <https://github.com/fmtlib/fmt/pull/3326>`_,
`#3334 <https://github.com/fmtlib/fmt/issues/3334>`_,
`#3342 <https://github.com/fmtlib/fmt/issues/3342>`_,
`#3343 <https://github.com/fmtlib/fmt/pull/3343>`_,
`#3351 <https://github.com/fmtlib/fmt/issues/3351>`_,
`#3352 <https://github.com/fmtlib/fmt/pull/3352>`_,
`#3362 <https://github.com/fmtlib/fmt/pull/3362>`_,
`#3365 <https://github.com/fmtlib/fmt/issues/3365>`_,
`#3366 <https://github.com/fmtlib/fmt/pull/3366>`_,
`#3374 <https://github.com/fmtlib/fmt/pull/3374>`_,
`#3377 <https://github.com/fmtlib/fmt/issues/3377>`_,
`#3378 <https://github.com/fmtlib/fmt/pull/3378>`_,
`#3381 <https://github.com/fmtlib/fmt/issues/3381>`_,
`#3398 <https://github.com/fmtlib/fmt/pull/3398>`_,
`#3413 <https://github.com/fmtlib/fmt/pull/3413>`_,
`#3415 <https://github.com/fmtlib/fmt/issues/3415>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@NewbieOrange <https://github.com/NewbieOrange>`_,
`@EngineLessCC (VivyaCC) <https://github.com/EngineLessCC>`_,
`@asmaloney (Andy Maloney) <https://github.com/asmaloney>`_,
`@HazardyKnusperkeks (Björn Schäpers)
<https://github.com/HazardyKnusperkeks>`_,
`@sergiud (Sergiu Deitsch) <https://github.com/sergiud>`_,
`@Youw (Ihor Dutchak) <https://github.com/Youw>`_,
`@thesmurph <https://github.com/thesmurph>`_,
`@czudziakm (Maksymilian Czudziak) <https://github.com/czudziakm>`_,
`@Roman-Koshelev <https://github.com/Roman-Koshelev>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_,
`@russelltg (Russell Greene) <https://github.com/russelltg>`_,
`@glebm (Gleb Mazovetskiy) <https://github.com/glebm>`_,
`@tmartin-gh <https://github.com/tmartin-gh>`_,
`@Zhaojun-Liu (June Liu) <https://github.com/Zhaojun-Liu>`_,
`@louiswins (Louis Wilson) <https://github.com/louiswins>`_,
`@mogemimi <https://github.com/mogemimi>`_.
9.1.0 - 2022-08-27 9.1.0 - 2022-08-27
------------------ ------------------
@ -105,6 +536,7 @@
`#2982 <https://github.com/fmtlib/fmt/pull/2982>`_, `#2982 <https://github.com/fmtlib/fmt/pull/2982>`_,
`#2985 <https://github.com/fmtlib/fmt/pull/2985>`_, `#2985 <https://github.com/fmtlib/fmt/pull/2985>`_,
`#2988 <https://github.com/fmtlib/fmt/issues/2988>`_, `#2988 <https://github.com/fmtlib/fmt/issues/2988>`_,
`#2989 <https://github.com/fmtlib/fmt/issues/2989>`_,
`#3000 <https://github.com/fmtlib/fmt/issues/3000>`_, `#3000 <https://github.com/fmtlib/fmt/issues/3000>`_,
`#3006 <https://github.com/fmtlib/fmt/issues/3006>`_, `#3006 <https://github.com/fmtlib/fmt/issues/3006>`_,
`#3014 <https://github.com/fmtlib/fmt/issues/3014>`_, `#3014 <https://github.com/fmtlib/fmt/issues/3014>`_,
@ -2260,7 +2692,7 @@
<https://github.com/kwesolowski>`_. <https://github.com/kwesolowski>`_.
* Replaced ``FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`` with the ``FMT_FUZZ`` * Replaced ``FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`` with the ``FMT_FUZZ``
macro to prevent interferring with fuzzing of projects using {fmt} macro to prevent interfering with fuzzing of projects using {fmt}
(`#1650 <https://github.com/fmtlib/fmt/pull/1650>`_). (`#1650 <https://github.com/fmtlib/fmt/pull/1650>`_).
Thanks `@asraa (Asra Ali) <https://github.com/asraa>`_. Thanks `@asraa (Asra Ali) <https://github.com/asraa>`_.

View File

@ -1,4 +1,4 @@
Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View File

@ -47,7 +47,8 @@ Features
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's * `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_ `format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and * Fast IEEE 754 floating-point formatter with correct rounding, shortness and
round-trip guarantees round-trip guarantees using the `Dragonbox <https://github.com/jk-jeon/dragonbox>`_
algorithm
* Safe `printf implementation * Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX <https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
extension for positional arguments extension for positional arguments
@ -191,24 +192,24 @@ Speed tests
================= ============= =========== ================= ============= ===========
Library Method Run Time, s Library Method Run Time, s
================= ============= =========== ================= ============= ===========
libc printf 1.04 libc printf 0.91
libc++ std::ostream 3.05 libc++ std::ostream 2.49
{fmt} 6.1.1 fmt::print 0.75 {fmt} 9.1 fmt::print 0.74
Boost Format 1.67 boost::format 7.24 Boost Format 1.80 boost::format 6.26
Folly Format folly::format 2.23 Folly Format folly::format 1.87
================= ============= =========== ================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``. {fmt} is the fastest of the benchmarked methods, ~20% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the 12.6.1 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source further details refer to the `source
<https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_. <https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_.
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on {fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_) IEEE754 ``float`` and ``double`` formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and faster than `double-conversion <https://github.com/google/double-conversion>`_ and and faster than `double-conversion <https://github.com/google/double-conversion>`_ and
`ryu <https://github.com/ulfjack/ryu>`_: `ryu <https://github.com/ulfjack/ryu>`_:
@ -322,8 +323,10 @@ Projects using this library
* `ccache <https://ccache.dev/>`_: a compiler cache * `ccache <https://ccache.dev/>`_: a compiler cache
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database * `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: an analytical database
management system management system
* `Contour <https://github.com/contour-terminal/contour/>`_: a modern terminal emulator
* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater * `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle vehicle
@ -360,6 +363,10 @@ Projects using this library
* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node * `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node
* `libunicode <https://github.com/contour-terminal/libunicode/>`_: a modern C++17 Unicode library
* `MariaDB <https://mariadb.org/>`_: relational database management system
* `Microsoft Verona <https://github.com/microsoft/verona>`_: * `Microsoft Verona <https://github.com/microsoft/verona>`_:
research programming language for concurrent ownership research programming language for concurrent ownership
@ -413,6 +420,9 @@ Projects using this library
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source * `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source
MMORPG framework MMORPG framework
* `🐙 userver framework <https://userver.tech/>`_: open-source asynchronous
framework with a rich set of abstractions and database drivers
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows * `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
terminal terminal
@ -523,8 +533,7 @@ Maintainers
----------- -----------
The {fmt} library is maintained by Victor Zverovich (`vitaut The {fmt} library is maintained by Victor Zverovich (`vitaut
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan <https://github.com/vitaut>`_) with contributions from many other people.
<https://github.com/foonathan>`_) with contributions from many other people.
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names. `Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
Let us know if your contribution is not listed or mentioned incorrectly and Let us know if your contribution is not listed or mentioned incorrectly and

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
@ -423,26 +423,6 @@ FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
int result = std::fputs(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
int result = std::fputws(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) { template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m"); auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end()); buffer.append(reset_color.begin(), reset_color.end());
@ -479,17 +459,19 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
template <typename S, typename Char = char_t<S>> inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
void vprint(std::FILE* f, const text_style& ts, const S& format, format_args args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) { // Legacy wide streams are not supported.
basic_memory_buffer<Char> buf; auto buf = memory_buffer();
detail::vformat_to(buf, ts, detail::to_string_view(format), args); detail::vformat_to(buf, ts, fmt, args);
if (detail::is_utf8()) { if (detail::is_utf8()) {
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size())); detail::print(f, string_view(buf.begin(), buf.size()));
} else { return;
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
} }
buf.push_back('\0');
int result = std::fputs(buf.data(), f);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
/** /**
@ -566,7 +548,7 @@ OutputIt vformat_to(
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args); detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf); return detail::get_iterator(buf, out);
} }
/** /**
@ -645,7 +627,7 @@ FMT_CONSTEXPR auto styled(const T& value, text_style ts)
return detail::styled_arg<remove_cvref_t<T>>{value, ts}; return detail::styled_arg<remove_cvref_t<T>>{value, ts};
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COLOR_H_ #endif // FMT_COLOR_H_

View File

@ -331,14 +331,14 @@ template <typename T, typename Char> struct parse_specs_result {
int next_arg_id; int next_arg_id;
}; };
constexpr int manual_indexing_id = -1; enum { manual_indexing_id = -1 };
template <typename T, typename Char> template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) { size_t pos, int next_arg_id) {
str.remove_prefix(pos); str.remove_prefix(pos);
auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {}, auto ctx =
next_arg_id); compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>(); auto f = formatter<T, Char>();
auto end = f.parse(ctx); auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()), return {f, pos + fmt::detail::to_unsigned(end - str.data()),
@ -348,22 +348,18 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
template <typename Char> struct arg_id_handler { template <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id; arg_ref<Char> arg_id;
constexpr int operator()() { constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing"); FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0; return 0;
} }
constexpr int operator()(int id) { constexpr int on_index(int id) {
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
constexpr int operator()(basic_string_view<Char> id) { constexpr int on_name(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
constexpr void on_error(const char* message) {
FMT_THROW(format_error(message));
}
}; };
template <typename Char> struct parse_arg_id_result { template <typename Char> struct parse_arg_id_result {
@ -501,7 +497,7 @@ constexpr auto compile(S format_str) {
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
@ -605,7 +601,7 @@ template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
} // namespace literals } // namespace literals
#endif #endif
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_ #endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

View File

@ -9,13 +9,9 @@
#define FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_
#include <algorithm> #include <algorithm>
#include <cctype>
#include <cerrno> // errno #include <cerrno> // errno
#include <climits> #include <climits>
#include <cmath> #include <cmath>
#include <cstdarg>
#include <cstring> // std::memmove
#include <cwchar>
#include <exception> #include <exception>
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
@ -115,16 +111,43 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
return '.'; return '.';
} }
#endif #endif
FMT_FUNC auto write_loc(appender out, loc_value value,
const format_specs<>& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding.
using facet = format_facet<std::locale>;
if (std::has_facet<facet>(locale))
return std::use_facet<facet>(locale).put(out, value, specs);
return facet(locale).put(out, value, specs);
#endif
return false;
}
} // namespace detail } // namespace detail
#if !FMT_MSC_VERSION template <typename Locale> typename Locale::id format_facet<Locale>::id;
FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
grouping_ = numpunct.grouping();
if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
}
template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, loc_value val, const format_specs<>& specs) const -> bool {
return val.visit(
detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
}
#endif #endif
FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
format_args args) { format_args args) {
auto ec = std::error_code(error_code, std::generic_category()); auto ec = std::error_code(error_code, std::generic_category());
return std::system_error(ec, vformat(format_str, args)); return std::system_error(ec, vformat(fmt, args));
} }
namespace detail { namespace detail {
@ -143,58 +166,8 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
return (n >> r) | (n << (64 - r)); return (n >> r) | (n << (64 - r));
} }
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
#elif defined(_MSC_VER) && defined(_M_X64)
auto result = uint128_fallback();
result.lo_ = _umul128(x, y, &result.hi_);
return result;
#else
const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
uint64_t a = x >> 32;
uint64_t b = x & mask;
uint64_t c = y >> 32;
uint64_t d = y & mask;
uint64_t ac = a * c;
uint64_t bc = b * c;
uint64_t ad = a * d;
uint64_t bd = b * d;
uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
(intermediate << 32) + (bd & mask)};
#endif
}
// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
namespace dragonbox { namespace dragonbox {
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return static_cast<uint64_t>(p >> 64);
#elif defined(_MSC_VER) && defined(_M_X64)
return __umulh(x, y);
#else
return umul128(x, y).high();
#endif
}
// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
inline uint128_fallback umul192_upper128(uint64_t x,
uint128_fallback y) noexcept {
uint128_fallback r = umul128(x, y.high());
r += umul128_upper64(x, y.low());
return r;
}
// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
// 64-bit unsigned integer. // 64-bit unsigned integer.
inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
@ -216,25 +189,13 @@ inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
return x * y; return x * y;
} }
// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
inline int floor_log10_pow2(int e) noexcept {
FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
return (e * 315653) >> 20;
}
// Various fast log computations. // Various fast log computations.
inline int floor_log2_pow10(int e) noexcept {
FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
return (e * 1741647) >> 19;
}
inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
return (e * 631305 - 261663) >> 21; return (e * 631305 - 261663) >> 21;
} }
static constexpr struct { FMT_INLINE_VARIABLE constexpr struct {
uint32_t divisor; uint32_t divisor;
int shift_amount; int shift_amount;
} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; } div_small_pow10_infos[] = {{10, 16}, {100, 16}};
@ -288,7 +249,7 @@ inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
} }
// Various subroutines using pow10 cache // Various subroutines using pow10 cache
template <class T> struct cache_accessor; template <typename T> struct cache_accessor;
template <> struct cache_accessor<float> { template <> struct cache_accessor<float> {
using carrier_uint = float_info<float>::carrier_uint; using carrier_uint = float_info<float>::carrier_uint;
@ -1009,8 +970,23 @@ template <> struct cache_accessor<double> {
{0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
{0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
{0xc5a05277621be293, 0xc7098b7305241886}, {0xc5a05277621be293, 0xc7098b7305241886},
{ 0xf70867153aa2db38, {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8},
0xb8cbee4fc66d1ea8 } {0x9a65406d44a5c903, 0x737f74f1dc043329},
{0xc0fe908895cf3b44, 0x505f522e53053ff3},
{0xf13e34aabb430a15, 0x647726b9e7c68ff0},
{0x96c6e0eab509e64d, 0x5eca783430dc19f6},
{0xbc789925624c5fe0, 0xb67d16413d132073},
{0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890},
{0x933e37a534cbaae7, 0x8e91b962f7b6f15a},
{0xb80dc58e81fe95a1, 0x723627bbb5a4adb1},
{0xe61136f2227e3b09, 0xcec3b1aaa30dd91d},
{0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2},
{0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e},
{0xe0accfa875af45a7, 0x93eb1b80a33b8606},
{0x8c6c01c9498d8b88, 0xbc72f130660533c4},
{0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
{ 0xdb68c2ca82ed2a05,
0xa67398db9f6820e2 }
#else #else
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
@ -1034,8 +1010,8 @@ template <> struct cache_accessor<double> {
{0x8da471a9de737e24, 0x5ceaecfed289e5d3}, {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
{0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
{0xb8da1662e7b00a17, 0x3d6a751f3b936244}, {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
{ 0x95527a5202df0ccb, {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
0x0f37801e0c43ebc9 } {0xf13e34aabb430a15, 0x647726b9e7c68ff0}
#endif #endif
}; };
@ -1138,8 +1114,12 @@ template <> struct cache_accessor<double> {
} }
}; };
FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
return cache_accessor<double>::get_cached_power(k);
}
// Various integer checks // Various integer checks
template <class T> template <typename T>
bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
const int case_shorter_interval_left_endpoint_lower_threshold = 2; const int case_shorter_interval_left_endpoint_lower_threshold = 2;
const int case_shorter_interval_left_endpoint_upper_threshold = 3; const int case_shorter_interval_left_endpoint_upper_threshold = 3;
@ -1150,8 +1130,12 @@ bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
// Remove trailing zeros from n and return the number of zeros removed (float) // Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
FMT_ASSERT(n != 0, ""); FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
// See https://github.com/fmtlib/fmt/issues/3163 for more details.
const uint32_t mod_inv_5 = 0xcccccccd; const uint32_t mod_inv_5 = 0xcccccccd;
const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; // Casts are needed to workaround a bug in MSVC 19.22 and older.
const uint32_t mod_inv_25 =
static_cast<uint32_t>(uint64_t(mod_inv_5) * mod_inv_5);
int s = 0; int s = 0;
while (true) { while (true) {
@ -1165,7 +1149,6 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
n = q; n = q;
s |= 1; s |= 1;
} }
return s; return s;
} }
@ -1223,7 +1206,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
} }
// The main algorithm for shorter interval case // The main algorithm for shorter interval case
template <class T> template <typename T>
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept { FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
decimal_fp<T> ret_value; decimal_fp<T> ret_value;
// Compute k and beta // Compute k and beta
@ -1394,17 +1377,6 @@ small_divisor_case_label:
return ret_value; return ret_value;
} }
} // namespace dragonbox } // namespace dragonbox
#ifdef _MSC_VER
FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...)
-> int {
auto args = va_list();
va_start(args, fmt);
int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args);
va_end(args);
return result;
}
#endif
} // namespace detail } // namespace detail
template <> struct formatter<detail::bigint> { template <> struct formatter<detail::bigint> {
@ -1413,9 +1385,8 @@ template <> struct formatter<detail::bigint> {
return ctx.begin(); return ctx.begin();
} }
template <typename FormatContext> auto format(const detail::bigint& n, format_context& ctx) const
auto format(const detail::bigint& n, FormatContext& ctx) const -> -> format_context::iterator {
typename FormatContext::iterator {
auto out = ctx.out(); auto out = ctx.out();
bool first = true; bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) { for (auto i = n.bigits_.size(); i > 0; --i) {
@ -1474,57 +1445,44 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
} }
namespace detail { namespace detail {
#ifdef _WIN32 #ifndef _WIN32
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
#else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>; using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
void*, const void*, dword, dword*, void*); void*, const void*, dword, dword*, void*);
FMT_FUNC bool write_console(std::FILE* f, string_view text) { FMT_FUNC bool write_console(std::FILE* f, string_view text) {
auto fd = _fileno(f); auto fd = _fileno(f);
if (_isatty(fd)) { if (!_isatty(fd)) return false;
detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); auto u16 = utf8_to_utf16(text);
auto written = detail::dword(); auto written = dword();
if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
u16.c_str(), static_cast<uint32_t>(u16.size()), static_cast<uint32_t>(u16.size()), &written, nullptr);
&written, nullptr)) {
return true;
}
}
// We return false if the file descriptor was not TTY, or it was but
// SetConsoleW failed which can happen if the output has been redirected to
// NUL. In both cases when we return false, we should attempt to do regular
// write via fwrite or std::ostream::write.
return false;
}
#endif
FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32
if (write_console(f, text)) return;
#endif
detail::fwrite_fully(text.data(), 1, text.size(), f);
}
} // namespace detail
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer;
detail::vformat_to(buffer, format_str, args);
detail::print(f, {buffer.data(), buffer.size()});
} }
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding. // Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
format_args args) { auto buffer = memory_buffer();
memory_buffer buffer; detail::vformat_to(buffer, fmt,
detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args)); basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f); fwrite_fully(buffer.data(), 1, buffer.size(), f);
} }
#endif #endif
FMT_FUNC void vprint(string_view format_str, format_args args) { FMT_FUNC void print(std::FILE* f, string_view text) {
vprint(stdout, format_str, args); if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
}
} // namespace detail
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
detail::print(f, {buffer.data(), buffer.size()});
}
FMT_FUNC void vprint(string_view fmt, format_args args) {
vprint(stdout, fmt, args);
} }
namespace detail { namespace detail {

File diff suppressed because it is too large Load Diff

View File

@ -71,7 +71,7 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
/** /**
\rst \rst
@ -120,48 +120,10 @@ template <typename Char> class basic_cstring_view {
using cstring_view = basic_cstring_view<char>; using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>; using wcstring_view = basic_cstring_view<wchar_t>;
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#ifdef _WIN32 #ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept; FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(basic_string_view<wchar_t> s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
@ -355,6 +317,12 @@ class FMT_API file {
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
buffered_file fdopen(const char* mode); buffered_file fdopen(const char* mode);
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
// wcstring_view filename. Windows only.
static file open_windows_file(wcstring_view path, int oflag);
# endif
}; };
// Returns the memory page size. // Returns the memory page size.
@ -397,6 +365,28 @@ struct ostream_params {
# endif # endif
}; };
class file_buffer final : public buffer<char> {
file file_;
FMT_API void grow(size_t) override;
public:
FMT_API file_buffer(cstring_view path, const ostream_params& params);
FMT_API file_buffer(file_buffer&& other);
FMT_API ~file_buffer();
void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
void close() {
flush();
file_.close();
}
};
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
// Added {} below to work around default constructor error known to // Added {} below to work around default constructor error known to
@ -404,49 +394,32 @@ FMT_END_DETAIL_NAMESPACE
constexpr detail::buffer_size buffer_size{}; constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */ /** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> { class FMT_API ostream {
private: private:
file file_; FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params) ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) { : buffer_(path, params) {}
set(new char[params.buffer_size], params.buffer_size);
}
public: public:
ostream(ostream&& other) ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
void flush() { ~ostream();
if (size() == 0) return;
file_.write(data(), size()); void flush() { buffer_.flush(); }
clear();
}
template <typename... T> template <typename... T>
friend ostream output_file(cstring_view path, T... params); friend ostream output_file(cstring_view path, T... params);
void close() { void close() { buffer_.close(); }
flush();
file_.close();
}
/** /**
Formats ``args`` according to specifications in ``fmt`` and writes the Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file. output to the file.
*/ */
template <typename... T> void print(format_string<T...> fmt, T&&... args) { template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt, vformat_to(detail::buffer_appender<char>(buffer_), fmt,
fmt::make_format_args(args...)); fmt::make_format_args(args...));
} }
}; };
@ -472,7 +445,7 @@ inline ostream output_file(cstring_view path, T... params) {
} }
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OS_H_ #endif // FMT_OS_H_

View File

@ -8,8 +8,8 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <fstream> #include <fstream> // std::filebuf
#include <ostream>
#if defined(_WIN32) && defined(__GLIBCXX__) #if defined(_WIN32) && defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h> # include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h> # include <ext/stdio_sync_filebuf.h>
@ -21,48 +21,14 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail { namespace detail {
// Checks if T has a user-defined operator<<.
template <typename T, typename Char, typename Enable = void>
class is_streamable {
private:
template <typename U>
static auto test(int)
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
<< std::declval<U>()) != 0>;
template <typename> static auto test(...) -> std::false_type;
using result = decltype(test<T>(0));
public:
is_streamable() = default;
static const bool value = result::value;
};
// Formatting of built-in types and arrays is intentionally disabled because
// it's handled by standard (non-ostream) formatters.
template <typename T, typename Char>
struct is_streamable<
T, Char,
enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
std::is_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {};
// Generate a unique explicit instantion in every translation unit using a tag // Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace. // type in an anonymous namespace.
namespace { namespace {
struct file_access_tag {}; struct file_access_tag {};
} // namespace } // namespace
template <class Tag, class BufType, FILE* BufType::*FileMemberPtr> template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class file_access { class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
}; };
@ -84,8 +50,8 @@ inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
#elif defined(_WIN32) && defined(__GLIBCXX__) #elif defined(_WIN32) && defined(__GLIBCXX__)
auto* rdbuf = os.rdbuf(); auto* rdbuf = os.rdbuf();
FILE* c_file; FILE* c_file;
if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
c_file = fbuf->file(); c_file = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
c_file = fbuf->file(); c_file = fbuf->file();
else else
@ -145,7 +111,7 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt { -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); detail::format_value(buffer, value, ctx.locale());
return formatter<basic_string_view<Char>, Char>::format( return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx); {buffer.data(), buffer.size()}, ctx);
} }
@ -180,13 +146,6 @@ auto streamed(const T& value) -> detail::streamed_view<T> {
namespace detail { namespace detail {
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: basic_ostream_formatter<Char> {
using basic_ostream_formatter<Char>::format;
};
inline void vprint_directly(std::ostream& os, string_view format_str, inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) { format_args args) {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
@ -232,6 +191,19 @@ void print(std::wostream& os,
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
} }
FMT_MODULE_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_MODULE_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
}
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View File

@ -14,7 +14,7 @@
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter { printf_formatter() = delete; }; template <typename T> struct printf_formatter { printf_formatter() = delete; };
@ -81,13 +81,13 @@ class printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) { int operator()(T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big")); throw_format_error("number is too big");
return (std::max)(static_cast<int>(value), 0); return (std::max)(static_cast<int>(value), 0);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) { int operator()(T) {
FMT_THROW(format_error("precision is not integer")); throw_format_error("precision is not integer");
return 0; return 0;
} }
}; };
@ -194,12 +194,10 @@ template <typename Char> struct get_cstring {
// left alignment if it is negative. // left alignment if it is negative.
template <typename Char> class printf_width_handler { template <typename Char> class printf_width_handler {
private: private:
using format_specs = basic_format_specs<Char>; format_specs<Char>& specs_;
format_specs& specs_;
public: public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {} explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) { unsigned operator()(T value) {
@ -209,24 +207,31 @@ template <typename Char> class printf_width_handler {
width = 0 - width; width = 0 - width;
} }
unsigned int_max = max_value<int>(); unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big")); if (width > int_max) throw_format_error("number is too big");
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) { unsigned operator()(T) {
FMT_THROW(format_error("width is not integer")); throw_format_error("width is not integer");
return 0; return 0;
} }
}; };
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The ``printf`` argument formatter. // The ``printf`` argument formatter.
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
class printf_arg_formatter : public arg_formatter<Char> { class printf_arg_formatter : public arg_formatter<Char> {
private: private:
using base = arg_formatter<Char>; using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>; using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = basic_format_specs<Char>;
context_type& context_; context_type& context_;
@ -237,8 +242,8 @@ class printf_arg_formatter : public arg_formatter<Char> {
} }
public: public:
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) printf_arg_formatter(OutputIt iter, format_specs<Char>& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {} : base(make_arg_formatter(iter, s)), context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); } OutputIt operator()(monostate value) { return base::operator()(value); }
@ -247,7 +252,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
// MSVC2013 fails to compile separate overloads for bool and Char so use // MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead. // std::is_same instead.
if (std::is_same<T, Char>::value) { if (std::is_same<T, Char>::value) {
format_specs fmt_specs = this->specs; format_specs<Char> fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none && if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) { fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
@ -300,8 +305,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
}; };
template <typename Char> template <typename Char>
void parse_flags(basic_format_specs<Char>& specs, const Char*& it, void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
const Char* end) {
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
case '-': case '-':
@ -328,8 +332,8 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
} }
template <typename Char, typename GetArg> template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end, int parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
basic_format_specs<Char>& specs, GetArg get_arg) { GetArg get_arg) {
int arg_index = -1; int arg_index = -1;
Char c = *it; Char c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
@ -344,7 +348,7 @@ int parse_header(const Char*& it, const Char* end,
if (value != 0) { if (value != 0) {
// Nonzero value means that we parsed width and don't need to // Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now. // parse it or flags again, so return now.
if (value == -1) FMT_THROW(format_error("number is too big")); if (value == -1) throw_format_error("number is too big");
specs.width = value; specs.width = value;
return arg_index; return arg_index;
} }
@ -355,7 +359,7 @@ int parse_header(const Char*& it, const Char* end,
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1); specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) FMT_THROW(format_error("number is too big")); if (specs.width == -1) throw_format_error("number is too big");
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(visit_format_arg(
@ -365,12 +369,52 @@ int parse_header(const Char*& it, const Char* end,
return arg_index; return arg_index;
} }
inline auto parse_printf_presentation_type(char c, type t)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
case 'x':
return in(t, integral_set) ? pt::hex_lower : pt::none;
case 'X':
return in(t, integral_set) ? pt::hex_upper : pt::none;
case 'a':
return in(t, float_set) ? pt::hexfloat_lower : pt::none;
case 'A':
return in(t, float_set) ? pt::hexfloat_upper : pt::none;
case 'e':
return in(t, float_set) ? pt::exp_lower : pt::none;
case 'E':
return in(t, float_set) ? pt::exp_upper : pt::none;
case 'f':
return in(t, float_set) ? pt::fixed_lower : pt::none;
case 'F':
return in(t, float_set) ? pt::fixed_upper : pt::none;
case 'g':
return in(t, float_set) ? pt::general_lower : pt::none;
case 'G':
return in(t, float_set) ? pt::general_upper : pt::none;
case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default:
return pt::none;
}
}
template <typename Char, typename Context> template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format, void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
using OutputIt = buffer_appender<Char>; using iterator = buffer_appender<Char>;
auto out = OutputIt(buf); auto out = iterator(buf);
auto context = basic_printf_context<OutputIt, Char>(out, args); auto context = basic_printf_context<iterator, Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format); auto parse_ctx = basic_printf_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next // Returns the argument with specified index or, if arg_index is -1, the next
@ -387,26 +431,25 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
const Char* end = parse_ctx.end(); const Char* end = parse_ctx.end();
auto it = start; auto it = start;
while (it != end) { while (it != end) {
if (!detail::find<false, Char>(it, end, '%', it)) { if (!find<false, Char>(it, end, '%', it)) {
it = end; // detail::find leaves it == nullptr if it doesn't find '%' it = end; // find leaves it == nullptr if it doesn't find '%'.
break; break;
} }
Char c = *it++; Char c = *it++;
if (it != end && *it == c) { if (it != end && *it == c) {
out = detail::write( out = write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
start = ++it; start = ++it;
continue; continue;
} }
out = detail::write(out, basic_string_view<Char>( out =
start, detail::to_unsigned(it - 1 - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
basic_format_specs<Char> specs; auto specs = format_specs<Char>();
specs.align = align::right; specs.align = align::right;
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg); int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) parse_ctx.on_error("argument not found"); if (arg_index == 0) throw_format_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
@ -417,7 +460,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); visit_format_arg(printf_precision_handler(), get_arg(-1)));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
@ -429,17 +472,15 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
if (specs.precision >= 0 && arg.is_integral()) if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] = specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present. ' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg); auto str = visit_format_arg(get_cstring<Char>(), arg);
auto str_end = str + specs.precision; auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char()); auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>( arg = make_arg<basic_printf_context<iterator, Char>>(
basic_string_view<Char>( basic_string_view<Char>(
str, detail::to_unsigned(nul != str_end ? nul - str str, to_unsigned(nul != str_end ? nul - str : specs.precision)));
: specs.precision)));
} }
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
specs.alt = false;
if (specs.fill[0] == '0') { if (specs.fill[0] == '0') {
if (arg.is_arithmetic() && specs.align != align::left) if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric; specs.align = align::numeric;
@ -451,7 +492,6 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0; c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0; Char t = it != end ? *it : 0;
using detail::convert_arg;
switch (c) { switch (c) {
case 'h': case 'h':
if (t == 'h') { if (t == 'h') {
@ -490,7 +530,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} }
// Parse type. // Parse type.
if (it == end) FMT_THROW(format_error("invalid format string")); if (it == end) throw_format_error("invalid format string");
char type = static_cast<char>(*it++); char type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral()) {
// Normalize type. // Normalize type.
@ -501,22 +541,21 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
break; break;
case 'c': case 'c':
visit_format_arg( visit_format_arg(
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), char_converter<basic_printf_context<iterator, Char>>(arg), arg);
arg);
break; break;
} }
} }
specs.type = parse_presentation_type(type); specs.type = parse_printf_presentation_type(type, arg.type());
if (specs.type == presentation_type::none) if (specs.type == presentation_type::none)
parse_ctx.on_error("invalid type specifier"); throw_format_error("invalid format specifier");
start = it; start = it;
// Format argument. // Format argument.
out = visit_format_arg( out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg); printf_arg_formatter<iterator, Char>(out, specs, context), arg);
} }
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
@ -559,9 +598,9 @@ inline auto vsprintf(
const S& fmt, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; auto buf = basic_memory_buffer<Char>();
vprintf(buffer, detail::to_string_view(fmt), args); detail::vprintf(buf, detail::to_string_view(fmt), args);
return to_string(buffer); return to_string(buf);
} }
/** /**
@ -586,10 +625,10 @@ inline auto vfprintf(
std::FILE* f, const S& fmt, std::FILE* f, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int { -> int {
basic_memory_buffer<Char> buffer; auto buf = basic_memory_buffer<Char>();
vprintf(buffer, detail::to_string_view(fmt), args); detail::vprintf(buf, detail::to_string_view(fmt), args);
size_t size = buffer.size(); size_t size = buf.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1 ? -1
: static_cast<int>(size); : static_cast<int>(size);
} }
@ -634,7 +673,7 @@ inline auto printf(const S& fmt, const T&... args) -> int {
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_ #endif // FMT_PRINTF_H_

View File

@ -22,27 +22,25 @@ FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename RangeT, typename OutputIterator> template <typename Range, typename OutputIt>
OutputIterator copy(const RangeT& range, OutputIterator out) { auto copy(const Range& range, OutputIt out) -> OutputIt {
for (auto it = range.begin(), end = range.end(); it != end; ++it) for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it; *out++ = *it;
return out; return out;
} }
template <typename OutputIterator> template <typename OutputIt>
OutputIterator copy(const char* str, OutputIterator out) { auto copy(const char* str, OutputIt out) -> OutputIt {
while (*str) *out++ = *str++; while (*str) *out++ = *str++;
return out; return out;
} }
template <typename OutputIterator> template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch; *out++ = ch;
return out; return out;
} }
template <typename OutputIterator> template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
OutputIterator copy(wchar_t ch, OutputIterator out) {
*out++ = ch; *out++ = ch;
return out; return out;
} }
@ -69,7 +67,7 @@ template <typename T> class is_map {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
#ifdef FMT_FORMAT_MAP_AS_LIST #ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
static constexpr const bool value = false; static constexpr const bool value = false;
#else #else
static constexpr const bool value = static constexpr const bool value =
@ -82,7 +80,7 @@ template <typename T> class is_set {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
#ifdef FMT_FORMAT_SET_AS_LIST #ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
static constexpr const bool value = false; static constexpr const bool value = false;
#else #else
static constexpr const bool value = static constexpr const bool value =
@ -157,8 +155,9 @@ template <typename T>
struct has_mutable_begin_end< struct has_mutable_begin_end<
T, void_t<decltype(detail::range_begin(std::declval<T>())), T, void_t<decltype(detail::range_begin(std::declval<T>())),
decltype(detail::range_end(std::declval<T>())), decltype(detail::range_end(std::declval<T>())),
enable_if_t<std::is_copy_constructible<T>::value>>> // the extra int here is because older versions of MSVC don't
: std::true_type {}; // SFINAE properly unless there are distinct types
int>> : std::true_type {};
template <typename T> template <typename T>
struct is_range_<T, void> struct is_range_<T, void>
@ -211,41 +210,61 @@ class is_tuple_formattable_ {
static constexpr const bool value = false; static constexpr const bool value = false;
}; };
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... I> template <std::size_t... Is>
static std::true_type check2(index_sequence<I...>, static std::true_type check2(index_sequence<Is...>,
integer_sequence<bool, (I == I)...>); integer_sequence<bool, (Is == Is)...>);
static std::false_type check2(...); static std::false_type check2(...);
template <std::size_t... I> template <std::size_t... Is>
static decltype(check2( static decltype(check2(
index_sequence<I...>{}, index_sequence<Is...>{},
integer_sequence< integer_sequence<
bool, (is_formattable<typename std::tuple_element<I, T>::type, bool, (is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{})) check(index_sequence<I...>); C>::value)...>{})) check(index_sequence<Is...>);
public: public:
static constexpr const bool value = static constexpr const bool value =
decltype(check(tuple_index_sequence<T>{}))::value; decltype(check(tuple_index_sequence<T>{}))::value;
}; };
template <class Tuple, class F, size_t... Is> template <typename Tuple, typename F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept { FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
using std::get; using std::get;
// using free function get<I>(T) now. // Using a free function get<Is>(Tuple) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
(void)_; // blocks warnings ignore_unused(unused);
} }
template <class T> template <typename Tuple, typename F>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
T const&) { for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
return {}; std::forward<Tuple>(t), std::forward<F>(f));
} }
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
const auto indexes = get_indexes(tup); void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); using std::get;
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
ignore_unused(unused);
} }
template <typename Tuple1, typename Tuple2, typename F>
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
std::forward<F>(f));
}
namespace tuple {
// Workaround a bug in MSVC 2019 (v140).
template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
// Older MSVC doesn't get the reference type correctly for arrays. // Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl { template <typename R> struct range_reference_type_impl {
@ -269,45 +288,37 @@ using range_reference_type =
template <typename Range> template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>; using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename Range> template <typename Formatter>
using uncvref_first_type = FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>; -> decltype(f.set_debug_format(set)) {
f.set_debug_format(set);
template <typename Range>
using uncvref_second_type = remove_cvref_t<
decltype(std::declval<range_reference_type<Range>>().second)>;
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
*out++ = ',';
*out++ = ' ';
return out;
} }
template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename Char, typename OutputIt> // These are not generic lambdas for compatibility with C++11.
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { template <typename ParseContext> struct parse_empty_specs {
return write_escaped_string(out, str); template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
} f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
ParseContext& ctx;
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
template <typename Char, typename OutputIt, typename T, template <typename T>
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)> void operator()(const formatter<T, char_type>& f, const T& v) {
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt { if (i > 0)
auto sv = std_string_view<Char>(str); ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
return write_range_entry<Char>(out, basic_string_view<Char>(sv)); ctx.advance_to(f.format(v, ctx));
} ++i;
}
template <typename Char, typename OutputIt, typename Arg, int i;
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> FormatContext& ctx;
OutputIt write_range_entry(OutputIt out, const Arg v) { basic_string_view<char_type> separator;
return write_escaped_char(out, v); };
}
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
!std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
return write<Char>(out, v);
}
} // namespace detail } // namespace detail
@ -321,29 +332,20 @@ template <typename T, typename C> struct is_tuple_formattable {
detail::is_tuple_formattable_<T, C>::value; detail::is_tuple_formattable_<T, C>::value;
}; };
template <typename TupleT, typename Char> template <typename Tuple, typename Char>
struct formatter<TupleT, Char, struct formatter<Tuple, Char,
enable_if_t<fmt::is_tuple_like<TupleT>::value && enable_if_t<fmt::is_tuple_like<Tuple>::value &&
fmt::is_tuple_formattable<TupleT, Char>::value>> { fmt::is_tuple_formattable<Tuple, Char>::value>> {
private: private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ = basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '('>{}; detail::string_literal<Char, '('>{};
basic_string_view<Char> closing_bracket_ = basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ')'>{}; detail::string_literal<Char, ')'>{};
// C++11 generic lambda for format().
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) out = detail::copy_str<Char>(separator, out);
out = detail::write_range_entry<Char>(out, v);
++i;
}
int i;
typename FormatContext::iterator& out;
basic_string_view<Char> separator;
};
public: public:
FMT_CONSTEXPR formatter() {} FMT_CONSTEXPR formatter() {}
@ -359,17 +361,21 @@ struct formatter<TupleT, Char,
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); auto it = ctx.begin();
if (it != ctx.end() && *it != '}')
FMT_THROW(format_error("invalid format specifier"));
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
return it;
} }
template <typename FormatContext = format_context> template <typename FormatContext>
auto format(const TupleT& values, FormatContext& ctx) const auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto out = ctx.out(); ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
out = detail::copy_str<Char>(opening_bracket_, out); detail::for_each2(
detail::for_each(values, format_each<FormatContext>{0, out, separator_}); formatters_, value,
out = detail::copy_str<Char>(closing_bracket_, out); detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return out; return detail::copy_str<Char>(closing_bracket_, ctx.out());
} }
}; };
@ -398,12 +404,10 @@ template <typename Context> struct range_mapper {
}; };
template <typename Char, typename Element> template <typename Char, typename Element>
using range_formatter_type = conditional_t< using range_formatter_type =
is_formattable<Element, Char>::value,
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
std::declval<Element>()))>, std::declval<Element>()))>,
Char>, Char>;
fallback_formatter<Element, Char>>;
template <typename R> template <typename R>
using maybe_const_range = using maybe_const_range =
@ -413,11 +417,8 @@ using maybe_const_range =
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
template <typename R, typename Char> template <typename R, typename Char>
struct is_formattable_delayed struct is_formattable_delayed
: disjunction< : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {};
#endif #endif
} // namespace detail } // namespace detail
template <typename T, typename Char, typename Enable = void> template <typename T, typename Char, typename Enable = void>
@ -426,32 +427,16 @@ struct range_formatter;
template <typename T, typename Char> template <typename T, typename Char>
struct range_formatter< struct range_formatter<
T, Char, T, Char,
enable_if_t<conjunction< enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
std::is_same<T, remove_cvref_t<T>>, is_formattable<T, Char>>::value>> {
disjunction<is_formattable<T, Char>,
detail::has_fallback_formatter<T, Char>>>::value>> {
private: private:
detail::range_formatter_type<Char, T> underlying_; detail::range_formatter_type<Char, T> underlying_;
bool custom_specs_ = false;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ = basic_string_view<Char> opening_bracket_ =
detail::string_literal<Char, '['>{}; detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ = basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{}; detail::string_literal<Char, ']'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int)
-> decltype(u.set_debug_format()) {
u.set_debug_format();
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
FMT_CONSTEXPR void maybe_set_debug_format() {
maybe_set_debug_format(underlying_, 0);
}
public: public:
FMT_CONSTEXPR range_formatter() {} FMT_CONSTEXPR range_formatter() {}
@ -473,31 +458,24 @@ struct range_formatter<
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(); auto it = ctx.begin();
auto end = ctx.end(); auto end = ctx.end();
if (it == end || *it == '}') {
maybe_set_debug_format();
return it;
}
if (*it == 'n') { if (it != end && *it == 'n') {
set_brackets({}, {}); set_brackets({}, {});
++it; ++it;
} }
if (*it == '}') { if (it != end && *it != '}') {
maybe_set_debug_format(); if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
return it; ++it;
} else {
detail::maybe_set_debug_format(underlying_, true);
} }
if (*it != ':')
FMT_THROW(format_error("no other top-level range formatters supported"));
custom_specs_ = true;
++it;
ctx.advance_to(it); ctx.advance_to(it);
return underlying_.parse(ctx); return underlying_.parse(ctx);
} }
template <typename R, class FormatContext> template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
detail::range_mapper<buffer_context<Char>> mapper; detail::range_mapper<buffer_context<Char>> mapper;
auto out = ctx.out(); auto out = ctx.out();
@ -507,7 +485,6 @@ struct range_formatter<
auto end = detail::range_end(range); auto end = detail::range_end(range);
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out); if (i > 0) out = detail::copy_str<Char>(separator_, out);
;
ctx.advance_to(out); ctx.advance_to(out);
out = underlying_.format(mapper.map(*it), ctx); out = underlying_.format(mapper.map(*it), ctx);
++i; ++i;
@ -520,13 +497,14 @@ struct range_formatter<
enum class range_format { disabled, map, set, sequence, string, debug_string }; enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail { namespace detail {
template <typename T> struct range_format_kind_ { template <typename T>
static constexpr auto value = std::is_same<range_reference_type<T>, T>::value struct range_format_kind_
? range_format::disabled : std::integral_constant<range_format,
: is_map<T>::value ? range_format::map std::is_same<uncvref_type<T>, T>::value
: is_set<T>::value ? range_format::set ? range_format::disabled
: range_format::sequence; : is_map<T>::value ? range_format::map
}; : is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K, typename R, typename Char, typename Enable = void> template <range_format K, typename R, typename Char, typename Enable = void>
struct range_default_formatter; struct range_default_formatter;
@ -601,9 +579,6 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
: tuple(t), sep{s} {} : tuple(t), sep{s} {}
}; };
template <typename Char, typename... T>
using tuple_arg_join = tuple_join_view<Char, T...>;
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with // support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision. // the dynamic width and precision.
@ -673,7 +648,42 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
} }
}; };
FMT_MODULE_EXPORT_BEGIN namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
template <typename T, typename Char>
struct formatter<T, Char,
enable_if_t<detail::is_container_adaptor_like<T>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
static auto get(const T& t) -> all {
return {t.*(&getter::c)}; // Access c through the derived class.
}
};
return formatter<all>::format(getter::get(t), ctx);
}
};
FMT_BEGIN_EXPORT
/** /**
\rst \rst
@ -716,7 +726,7 @@ auto join(std::initializer_list<T> list, string_view sep)
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View File

@ -8,8 +8,12 @@
#ifndef FMT_STD_H_ #ifndef FMT_STD_H_
#define FMT_STD_H_ #define FMT_STD_H_
#include <cstdlib>
#include <exception>
#include <memory>
#include <thread> #include <thread>
#include <type_traits> #include <type_traits>
#include <typeinfo>
#include <utility> #include <utility>
#include "ostream.h" #include "ostream.h"
@ -25,6 +29,19 @@
# if FMT_HAS_INCLUDE(<variant>) # if FMT_HAS_INCLUDE(<variant>)
# include <variant> # include <variant>
# endif # endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// Android NDK with gabi++ library on some architectures does not implement
// abi::__cxa_demangle().
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_ABI_CXA_DEMANGLE
# endif
#endif #endif
#ifdef __cpp_lib_filesystem #ifdef __cpp_lib_filesystem
@ -39,12 +56,13 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
} }
# ifdef _WIN32 # ifdef _WIN32
template <> template <>
inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted, inline void write_escaped_path<char>(memory_buffer& quoted,
const std::filesystem::path& p) { const std::filesystem::path& p) {
auto s = p.u8string(); auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<char>( write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
std::back_inserter(quoted), // Convert UTF-16 to UTF-8.
string_view(reinterpret_cast<const char*>(s.c_str()), s.size())); if (!unicode_to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()}))
FMT_THROW(std::runtime_error("invalid utf16"));
} }
# endif # endif
template <> template <>
@ -57,13 +75,19 @@ inline void write_escaped_path<std::filesystem::path::value_type>(
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT
template <typename Char> template <typename Char>
struct formatter<std::filesystem::path, Char> struct formatter<std::filesystem::path, Char>
: formatter<basic_string_view<Char>> { : formatter<basic_string_view<Char>> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto out = formatter<basic_string_view<Char>>::parse(ctx);
this->set_debug_format(false);
return out;
}
template <typename FormatContext> template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const -> auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
typename FormatContext::iterator { typename FormatContext::iterator {
basic_memory_buffer<Char> quoted; auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p); detail::write_escaped_path(quoted, p);
return formatter<basic_string_view<Char>>::format( return formatter<basic_string_view<Char>>::format(
basic_string_view<Char>(quoted.data(), quoted.size()), ctx); basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
@ -73,12 +97,58 @@ FMT_END_NAMESPACE
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename Char> template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(std::optional<T> const& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#ifdef __cpp_lib_variant #ifdef __cpp_lib_variant
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename Char> struct formatter<std::monostate, Char> { template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
@ -100,19 +170,16 @@ template <typename T>
using variant_index_sequence = using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>; std::make_index_sequence<std::variant_size<T>::value>;
// variant_size and variant_alternative check. template <typename> struct is_variant_like_ : std::false_type {};
template <typename T, typename U = void> template <typename... Types>
struct is_variant_like_ : std::false_type {}; struct is_variant_like_<std::variant<Types...>> : std::true_type {};
template <typename T>
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
: std::true_type {};
// formattable element check // formattable element check.
template <typename T, typename C> class is_variant_formattable_ { template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... I> template <std::size_t... Is>
static std::conjunction< static std::conjunction<
is_formattable<std::variant_alternative_t<I, T>, C>...> is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<I...>); check(std::index_sequence<Is...>);
public: public:
static constexpr const bool value = static constexpr const bool value =
@ -130,7 +197,6 @@ auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
} }
} // namespace detail } // namespace detail
template <typename T> struct is_variant_like { template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value; static constexpr const bool value = detail::is_variant_like_<T>::value;
}; };
@ -140,6 +206,7 @@ template <typename T, typename C> struct is_variant_formattable {
detail::is_variant_formattable_<T, C>::value; detail::is_variant_formattable_<T, C>::value;
}; };
FMT_MODULE_EXPORT
template <typename Variant, typename Char> template <typename Variant, typename Char>
struct formatter< struct formatter<
Variant, Char, Variant, Char,
@ -156,16 +223,127 @@ struct formatter<
auto out = ctx.out(); auto out = ctx.out();
out = detail::write<Char>(out, "variant("); out = detail::write<Char>(out, "variant(");
std::visit( try {
[&](const auto& v) { std::visit(
out = detail::write_variant_alternative<Char>(out, v); [&](const auto& v) {
}, out = detail::write_variant_alternative<Char>(out, v);
value); },
value);
} catch (const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')'; *out++ = ')';
return out; return out;
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // __cpp_lib_variant
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
FMT_MODULE_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = true;
}
return it;
}
template <typename OutputIt>
auto format(const std::exception& ex,
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
format_specs<Char> spec;
auto out = ctx.out();
if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec);
const std::type_info& ti = typeid(ex);
#ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
out = detail::write_bytes(out, demangled_name_view, spec);
#elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name());
if (demangled_name_view.starts_with("class "))
demangled_name_view.remove_prefix(6);
else if (demangled_name_view.starts_with("struct "))
demangled_name_view.remove_prefix(7);
out = detail::write_bytes(out, demangled_name_view, spec);
#else
out = detail::write_bytes(out, string_view(ti.name()), spec);
#endif #endif
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, Char(' '));
out = detail::write_bytes(out, string_view(ex.what()), spec);
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_ #endif // FMT_STD_H_

View File

@ -12,13 +12,32 @@
#include "format.h" #include "format.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename T> template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
loc_value value, const format_specs<wchar_t>& specs,
locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>; using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>; using wformat_parse_context = basic_format_parse_context<wchar_t>;
@ -33,7 +52,9 @@ inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else #else
template <typename... Args> template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; } inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#endif #endif
template <> struct is_char<wchar_t> : std::true_type {}; template <> struct is_char<wchar_t> : std::true_type {};
@ -126,7 +147,7 @@ auto vformat_to(OutputIt out, const S& format_str,
-> OutputIt { -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(format_str), args); detail::vformat_to(buf, detail::to_string_view(format_str), args);
return detail::get_iterator(buf); return detail::get_iterator(buf, out);
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
@ -149,7 +170,7 @@ inline auto vformat_to(
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(format_str), args, vformat_to(buf, detail::to_string_view(format_str), args,
detail::locale_ref(loc)); detail::locale_ref(loc));
return detail::get_iterator(buf); return detail::get_iterator(buf, out);
} }
template < template <
@ -160,7 +181,7 @@ template <
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, to_string_view(format_str), return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
@ -217,13 +238,22 @@ template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
} }
template <typename... T>
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/** /**
Converts *value* to ``std::wstring`` using the default format for type *T*. Converts *value* to ``std::wstring`` using the default format for type *T*.
*/ */
template <typename T> inline auto to_wstring(const T& value) -> std::wstring { template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value); return format(FMT_STRING(L"{}"), value);
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_ #endif // FMT_XCHAR_H_

View File

@ -1,49 +1,44 @@
module; module;
#ifndef __cpp_modules
# error Module not supported.
#endif
// put all implementation-provided headers into the global module fragment
// to prevent attachment to this module
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#if !defined(WIN32_LEAN_AND_MEAN) && defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
#endif
// Put all implementation-provided headers into the global module fragment
// to prevent attachment to this module.
#include <algorithm> #include <algorithm>
#include <cctype>
#include <cerrno> #include <cerrno>
#include <chrono> #include <chrono>
#include <climits> #include <climits>
#include <clocale>
#include <cmath> #include <cmath>
#include <cstdarg>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <cwchar>
#include <exception> #include <exception>
#include <filesystem>
#include <fstream>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <locale> #include <locale>
#include <memory> #include <memory>
#include <optional>
#include <ostream> #include <ostream>
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <system_error> #include <system_error>
#include <thread>
#include <type_traits> #include <type_traits>
#include <typeinfo>
#include <utility> #include <utility>
#include <variant>
#include <vector> #include <vector>
#include <version>
#if _MSC_VER #if __has_include(<cxxabi.h>)
# include <cxxabi.h>
#endif
#if defined(_MSC_VER) || defined(__MINGW32__)
# include <intrin.h> # include <intrin.h>
#endif #endif
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
@ -65,22 +60,38 @@ module;
# endif # endif
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
# if defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# elif defined(_LIBCPP_VERSION)
# include <__std_stream>
# endif
# define WIN32_LEAN_AND_MEAN
# include <windows.h> # include <windows.h>
#endif #endif
export module fmt; export module fmt;
#define FMT_MODULE_EXPORT export #define FMT_MODULE_EXPORT export
#define FMT_MODULE_EXPORT_BEGIN export { #define FMT_BEGIN_EXPORT export {
#define FMT_MODULE_EXPORT_END } #define FMT_END_EXPORT }
#define FMT_BEGIN_DETAIL_NAMESPACE \ #define FMT_BEGIN_DETAIL_NAMESPACE \
} \ } \
namespace detail { namespace detail {
#define FMT_END_DETAIL_NAMESPACE \ #define FMT_END_DETAIL_NAMESPACE \
} \ } \
export { export {
// all library-provided declarations and definitions // If you define FMT_ATTACH_TO_GLOBAL_MODULE
// must be in the module purview to be exported // - all declarations are detached from module 'fmt'
// - the module behaves like a traditional static library, too
// - all library symbols are mangled traditionally
// - you can mix TUs with either importing or #including the {fmt} API
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
extern "C++" {
#endif
// All library-provided declarations and definitions must be in the module
// purview to be exported.
#include "fmt/args.h" #include "fmt/args.h"
#include "fmt/chrono.h" #include "fmt/chrono.h"
#include "fmt/color.h" #include "fmt/color.h"
@ -88,11 +99,17 @@ export module fmt;
#include "fmt/format.h" #include "fmt/format.h"
#include "fmt/os.h" #include "fmt/os.h"
#include "fmt/printf.h" #include "fmt/printf.h"
#include "fmt/std.h"
#include "fmt/xchar.h" #include "fmt/xchar.h"
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
}
#endif
// gcc doesn't yet implement private module fragments // gcc doesn't yet implement private module fragments
#if !FMT_GCC_VERSION #if !FMT_GCC_VERSION
module : private; module :private;
#endif #endif
#include "format.cc" #include "format.cc"

View File

@ -28,12 +28,8 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char;
template FMT_API void buffer<char>::append(const char*, const char*); template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
// There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377).
template FMT_API void vformat_to(buffer<char>&, string_view, template FMT_API void vformat_to(buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, typename vformat_args<>::type, locale_ref);
locale_ref);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.

View File

@ -72,34 +72,6 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
#ifdef _WIN32 #ifdef _WIN32
detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
// WideCharToMultiByte does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return 0;
}
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
length, nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_[length] = 0;
return 0;
}
namespace detail { namespace detail {
class system_message { class system_message {
@ -140,8 +112,8 @@ class utf8_system_category final : public std::error_category {
std::string message(int error_code) const override { std::string message(int error_code) const override {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
utf16_to_utf8 utf8_message; unicode_to_utf8<wchar_t> utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) { if (utf8_message.convert(msg)) {
return utf8_message.str(); return utf8_message.str();
} }
} }
@ -167,9 +139,10 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
FMT_TRY { FMT_TRY {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
utf16_to_utf8 utf8_message; unicode_to_utf8<wchar_t> utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) { if (utf8_message.convert(msg)) {
fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message); fmt::format_to(buffer_appender<char>(out), FMT_STRING("{}: {}"),
message, string_view(utf8_message));
return; return;
} }
} }
@ -192,37 +165,47 @@ buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr); nullptr);
if (!file_) if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); FMT_THROW(system_error(errno, FMT_STRING("cannot open file {}"),
filename.c_str()));
} }
void buffered_file::close() { void buffered_file::close() {
if (!file_) return; if (!file_) return;
int result = FMT_SYSTEM(fclose(file_)); int result = FMT_SYSTEM(fclose(file_));
file_ = nullptr; file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
} }
int buffered_file::descriptor() const { int buffered_file::descriptor() const {
#ifdef fileno // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
int fd = fileno(file_);
#else
int fd = FMT_POSIX_CALL(fileno(file_)); int fd = FMT_POSIX_CALL(fileno(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); #endif
if (fd == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor")));
return fd; return fd;
} }
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
# ifdef _WIN32 # ifdef _WIN32
using mode_t = int; using mode_t = int;
# endif # endif
constexpr mode_t mode = constexpr mode_t default_open_mode =
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
file::file(cstring_view path, int oflag) {
# if defined(_WIN32) && !defined(__MINGW32__) # if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1; fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); auto converted = detail::utf8_to_utf16(string_view(path.c_str()));
*this = file::open_windows_file(converted.c_str(), oflag);
# else # else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode)));
# endif
if (fd_ == -1) if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); FMT_THROW(
system_error(errno, FMT_STRING("cannot open file {}"), path.c_str()));
# endif
} }
file::~file() noexcept { file::~file() noexcept {
@ -238,7 +221,8 @@ void file::close() {
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_)); int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1; fd_ = -1;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
} }
long long file::size() const { long long file::size() const {
@ -260,7 +244,7 @@ long long file::size() const {
using Stat = struct stat; using Stat = struct stat;
Stat file_stat = Stat(); Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes")); FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes")));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size), static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough"); "return type of file::size is not large enough");
return file_stat.st_size; return file_stat.st_size;
@ -270,14 +254,16 @@ long long file::size() const {
std::size_t file::read(void* buffer, std::size_t count) { std::size_t file::read(void* buffer, std::size_t count) {
rwresult result = 0; rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot read from file")));
return detail::to_unsigned(result); return detail::to_unsigned(result);
} }
std::size_t file::write(const void* buffer, std::size_t count) { std::size_t file::write(const void* buffer, std::size_t count) {
rwresult result = 0; rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
return detail::to_unsigned(result); return detail::to_unsigned(result);
} }
@ -286,7 +272,8 @@ file file::dup(int fd) {
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd)); int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1) if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); FMT_THROW(system_error(
errno, FMT_STRING("cannot duplicate file descriptor {}"), fd));
return file(new_fd); return file(new_fd);
} }
@ -294,8 +281,9 @@ void file::dup2(int fd) {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) { if (result == -1) {
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", FMT_THROW(system_error(
fd_, fd)); errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_,
fd));
} }
} }
@ -320,7 +308,8 @@ void file::pipe(file& read_end, file& write_end) {
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds)); int result = FMT_POSIX_CALL(pipe(fds));
# endif # endif
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe")));
// The following assignments don't throw because read_fd and write_fd // The following assignments don't throw because read_fd and write_fd
// are closed. // are closed.
read_end = file(fds[0]); read_end = file(fds[0]);
@ -334,28 +323,68 @@ buffered_file file::fdopen(const char* mode) {
# else # else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
# endif # endif
if (!f) if (!f) {
FMT_THROW( FMT_THROW(system_error(
system_error(errno, "cannot associate stream with file descriptor")); errno, FMT_STRING("cannot associate stream with file descriptor")));
}
buffered_file bf(f); buffered_file bf(f);
fd_ = -1; fd_ = -1;
return bf; return bf;
} }
# if defined(_WIN32) && !defined(__MINGW32__)
file file::open_windows_file(wcstring_view path, int oflag) {
int fd = -1;
auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode);
if (fd == -1) {
FMT_THROW(
system_error(err, FMT_STRING("cannot open file {}"),
detail::unicode_to_utf8<wchar_t>(path.c_str()).c_str()));
}
return file(fd);
}
# endif
# if !defined(__MSDOS__)
long getpagesize() { long getpagesize() {
# ifdef _WIN32 # ifdef _WIN32
SYSTEM_INFO si; SYSTEM_INFO si;
GetSystemInfo(&si); GetSystemInfo(&si);
return si.dwPageSize; return si.dwPageSize;
# else # else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); if (size < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size")));
return size; return size;
# endif # endif
} }
# endif
FMT_API void ostream::grow(size_t) { namespace detail {
void file_buffer::grow(size_t) {
if (this->size() == this->capacity()) flush(); if (this->size() == this->capacity()) flush();
} }
file_buffer::file_buffer(cstring_view path,
const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
file_buffer::file_buffer(file_buffer&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
file_buffer::~file_buffer() {
flush();
delete[] data();
}
} // namespace detail
ostream::~ostream() = default;
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -1,15 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := fmt_static
LOCAL_MODULE_FILENAME := libfmt
LOCAL_SRC_FILES := ../src/format.cc
LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_CFLAGS += -std=c++11 -fexceptions
include $(BUILD_STATIC_LIBRARY)

View File

@ -1 +0,0 @@
<manifest package="net.fmtlib" />

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
This directory contains build support files such as
* CMake modules
* Build scripts

View File

@ -1,20 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
config.disksize.size = '15GB'
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"
end
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y g++ make wget git
wget -q https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.tar.gz
tar xzf cmake-3.14.4-Linux-x86_64.tar.gz
ln -s `pwd`/cmake-3.14.4-Linux-x86_64/bin/cmake /usr/local/bin
SHELL
end

View File

@ -1 +0,0 @@
build --symlink_prefix=/ # Out of source build

View File

@ -1 +0,0 @@
5.1.1

View File

@ -1,28 +0,0 @@
cc_library(
name = "fmt",
srcs = [
#"src/fmt.cc", # No C++ module support
"src/format.cc",
"src/os.cc",
],
hdrs = [
"include/fmt/args.h",
"include/fmt/chrono.h",
"include/fmt/color.h",
"include/fmt/compile.h",
"include/fmt/core.h",
"include/fmt/format-inl.h",
"include/fmt/format.h",
"include/fmt/os.h",
"include/fmt/ostream.h",
"include/fmt/printf.h",
"include/fmt/ranges.h",
"include/fmt/std.h",
"include/fmt/xchar.h",
],
includes = [
"include",
],
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)

View File

@ -1,73 +0,0 @@
# Bazel support
To get [Bazel](https://bazel.build/) working with {fmt} you can copy the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` from this folder (`support/bazel`) to the root folder of this project. This way {fmt} gets bazelized and can be used with Bazel (e.g. doing a `bazel build //...` on {fmt}).
## Using {fmt} as a dependency
The following minimal example shows how to use {fmt} as a dependency within a Bazel project.
The following file structure is assumed:
```
example
├── BUILD.bazel
├── main.cpp
└── WORKSPACE.bazel
```
*main.cpp*:
```c++
#include "fmt/core.h"
int main() {
fmt::print("The answer is {}\n", 42);
}
```
The expected output of this example is `The answer is 42`.
*WORKSPACE.bazel*:
```python
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "fmt",
branch = "master",
remote = "https://github.com/fmtlib/fmt",
patch_cmds = [
"mv support/bazel/.bazelrc .bazelrc",
"mv support/bazel/.bazelversion .bazelversion",
"mv support/bazel/BUILD.bazel BUILD.bazel",
"mv support/bazel/WORKSPACE.bazel WORKSPACE.bazel",
],
# Windows-related patch commands are only needed in the case MSYS2 is not installed.
# More details about the installation process of MSYS2 on Windows systems can be found here:
# https://docs.bazel.build/versions/main/install-windows.html#installing-compilers-and-language-runtimes
# Even if MSYS2 is installed the Windows related patch commands can still be used.
patch_cmds_win = [
"Move-Item -Path support/bazel/.bazelrc -Destination .bazelrc",
"Move-Item -Path support/bazel/.bazelversion -Destination .bazelversion",
"Move-Item -Path support/bazel/BUILD.bazel -Destination BUILD.bazel",
"Move-Item -Path support/bazel/WORKSPACE.bazel -Destination WORKSPACE.bazel",
],
)
```
In the *WORKSPACE* file, the {fmt} GitHub repository is fetched. Using the attribute `patch_cmds` the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` are moved to the root of the {fmt} repository. This way the {fmt} repository is recognized as a bazelized workspace.
*BUILD.bazel*:
```python
cc_binary(
name = "Demo",
srcs = ["main.cpp"],
deps = ["@fmt"],
)
```
The *BUILD* file defines a binary named `Demo` that has a dependency to {fmt}.
To execute the binary you can run `bazel run //:Demo`.

View File

@ -1 +0,0 @@
workspace(name = "fmt")

View File

@ -1,58 +0,0 @@
#!/usr/bin/env python
# Build the documentation in CI.
from __future__ import print_function
import errno, os, shutil, subprocess, sys, urllib
from subprocess import call, check_call, Popen, PIPE, STDOUT
def rmtree_if_exists(dir):
try:
shutil.rmtree(dir)
except OSError as e:
if e.errno == errno.ENOENT:
pass
# Build the docs.
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
import build
build.create_build_env()
html_dir = build.build_docs()
repo = 'fmtlib.github.io'
branch = os.environ['GITHUB_REF']
is_ci = 'CI' in os.environ
if is_ci and branch != 'refs/heads/master':
print('Branch: ' + branch)
exit(0) # Ignore non-master branches
if is_ci and 'KEY' not in os.environ:
# Don't update the repo if building in CI from an account that doesn't have
# push access.
print('Skipping update of ' + repo)
exit(0)
# Clone the fmtlib.github.io repo.
rmtree_if_exists(repo)
git_url = 'https://github.com/' if is_ci else 'git@github.com:'
check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)])
# Copy docs to the repo.
target_dir = os.path.join(repo, 'dev')
rmtree_if_exists(target_dir)
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
if is_ci:
check_call(['git', 'config', '--global', 'user.name', 'fmtbot'])
check_call(['git', 'config', '--global', 'user.email', 'viz@fmt.dev'])
# Push docs to GitHub pages.
check_call(['git', 'add', '--all'], cwd=repo)
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
cmd = 'git push'
if is_ci:
cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master'
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
# Print the output without the key.
print(p.communicate()[0].decode('utf-8').replace(os.environ['KEY'], '$KEY'))
if p.returncode != 0:
raise subprocess.CalledProcessError(p.returncode, cmd)

View File

@ -1,132 +0,0 @@
import java.nio.file.Paths
// General gradle arguments for root project
buildscript {
repositories {
google()
jcenter()
}
dependencies {
//
// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
//
// Notice that 4.0.0 here is the version of [Android Gradle Plugin]
// Accroding to URL above you will need Gradle 6.1 or higher
//
classpath "com.android.tools.build:gradle:4.1.1"
}
}
repositories {
google()
jcenter()
}
// Project's root where CMakeLists.txt exists: rootDir/support/.cxx -> rootDir
def rootDir = Paths.get(project.buildDir.getParent()).getParent()
println("rootDir: ${rootDir}")
// Output: Shared library (.so) for Android
apply plugin: "com.android.library"
android {
compileSdkVersion 25 // Android 7.0
// Target ABI
// - This option controls target platform of module
// - The platform might be limited by compiler's support
// some can work with Clang(default), but some can work only with GCC...
// if bad, both toolchains might not support it
splits {
abi {
enable true
// Specify platforms for Application
reset()
include "arm64-v8a", "armeabi-v7a", "x86_64"
}
}
ndkVersion "21.3.6528147" // ANDROID_NDK_HOME is deprecated. Be explicit
defaultConfig {
minSdkVersion 21 // Android 5.0+
targetSdkVersion 25 // Follow Compile SDK
versionCode 34 // Follow release count
versionName "7.1.2" // Follow Official version
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared" // Specify Android STL
arguments "-DBUILD_SHARED_LIBS=true" // Build shared object
arguments "-DFMT_TEST=false" // Skip test
arguments "-DFMT_DOC=false" // Skip document
cppFlags "-std=c++17"
targets "fmt"
}
}
println(externalNativeBuild.cmake.cppFlags)
println(externalNativeBuild.cmake.arguments)
}
// External Native build
// - Use existing CMakeList.txt
// - Give path to CMake. This gradle file should be
// neighbor of the top level cmake
externalNativeBuild {
cmake {
version "3.10.0+"
path "${rootDir}/CMakeLists.txt"
// buildStagingDirectory "./build" // Custom path for cmake output
}
}
sourceSets{
// Android Manifest for Gradle
main {
manifest.srcFile "AndroidManifest.xml"
}
}
// https://developer.android.com/studio/build/native-dependencies#build_system_configuration
buildFeatures {
prefab true
prefabPublishing true
}
prefab {
fmt {
headers "${rootDir}/include"
}
}
}
assemble.doLast
{
// Instead of `ninja install`, Gradle will deploy the files.
// We are doing this since FMT is dependent to the ANDROID_STL after build
copy {
from "build/intermediates/cmake"
into "${rootDir}/libs"
}
// Copy debug binaries
copy {
from "${rootDir}/libs/debug/obj"
into "${rootDir}/libs/debug"
}
// Copy Release binaries
copy {
from "${rootDir}/libs/release/obj"
into "${rootDir}/libs/release"
}
// Remove empty directory
delete "${rootDir}/libs/debug/obj"
delete "${rootDir}/libs/release/obj"
// Copy AAR files. Notice that the aar is named after the folder of this script.
copy {
from "build/outputs/aar/support-release.aar"
into "${rootDir}/libs"
rename "support-release.aar", "fmt-release.aar"
}
copy {
from "build/outputs/aar/support-debug.aar"
into "${rootDir}/libs"
rename "support-debug.aar", "fmt-debug.aar"
}
}

View File

@ -1,54 +0,0 @@
# C++14 feature support detection
include(CheckCXXCompilerFlag)
function (fmt_check_cxx_compiler_flag flag result)
if (NOT MSVC)
check_cxx_compiler_flag("${flag}" ${result})
endif ()
endfunction ()
if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
if (CMAKE_CXX_STANDARD EQUAL 20)
fmt_check_cxx_compiler_flag(-std=c++20 has_std_20_flag)
fmt_check_cxx_compiler_flag(-std=c++2a has_std_2a_flag)
if (has_std_20_flag)
set(CXX_STANDARD_FLAG -std=c++20)
elseif (has_std_2a_flag)
set(CXX_STANDARD_FLAG -std=c++2a)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 17)
fmt_check_cxx_compiler_flag(-std=c++17 has_std_17_flag)
fmt_check_cxx_compiler_flag(-std=c++1z has_std_1z_flag)
if (has_std_17_flag)
set(CXX_STANDARD_FLAG -std=c++17)
elseif (has_std_1z_flag)
set(CXX_STANDARD_FLAG -std=c++1z)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 14)
fmt_check_cxx_compiler_flag(-std=c++14 has_std_14_flag)
fmt_check_cxx_compiler_flag(-std=c++1y has_std_1y_flag)
if (has_std_14_flag)
set(CXX_STANDARD_FLAG -std=c++14)
elseif (has_std_1y_flag)
set(CXX_STANDARD_FLAG -std=c++1y)
endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 11)
fmt_check_cxx_compiler_flag(-std=c++11 has_std_11_flag)
fmt_check_cxx_compiler_flag(-std=c++0x has_std_0x_flag)
if (has_std_11_flag)
set(CXX_STANDARD_FLAG -std=c++11)
elseif (has_std_0x_flag)
set(CXX_STANDARD_FLAG -std=c++0x)
endif ()
endif ()

View File

@ -1,53 +0,0 @@
#!/usr/bin/env python
# Compute 10 ** exp with exp in the range [min_exponent, max_exponent] and print
# normalized (with most-significant bit equal to 1) significands in hexadecimal.
from __future__ import print_function
min_exponent = -348
max_exponent = 340
step = 8
significand_size = 64
exp_offset = 2000
class fp:
pass
powers = []
for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)):
result = fp()
n = 10 ** exp if exp >= 0 else 2 ** exp_offset / 10 ** -exp
k = significand_size + 1
# Convert to binary and round.
binary = '{:b}'.format(n)
result.f = (int('{:0<{}}'.format(binary[:k], k), 2) + 1) / 2
result.e = len(binary) - (exp_offset if exp < 0 else 0) - significand_size
powers.append(result)
# Sanity check.
exp_offset10 = 400
actual = result.f * 10 ** exp_offset10
if result.e > 0:
actual *= 2 ** result.e
else:
for j in range(-result.e):
actual /= 2
expected = 10 ** (exp_offset10 + exp)
precision = len('{}'.format(expected)) - len('{}'.format(actual - expected))
if precision < 19:
print('low precision:', precision)
exit(1)
print('Significands:', end='')
for i, fp in enumerate(powers):
if i % 3 == 0:
print(end='\n ')
print(' {:0<#16x}'.format(fp.f, ), end=',')
print('\n\nExponents:', end='')
for i, fp in enumerate(powers):
if i % 11 == 0:
print(end='\n ')
print(' {:5}'.format(fp.e), end=',')
print('\n\nMax exponent difference:',
max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:]))

View File

@ -1,581 +0,0 @@
"""Pythonic command-line interface parser that will make you smile.
* http://docopt.org
* Repository and issue-tracker: https://github.com/docopt/docopt
* Licensed under terms of MIT license (see LICENSE-MIT)
* Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com
"""
import sys
import re
__all__ = ['docopt']
__version__ = '0.6.1'
class DocoptLanguageError(Exception):
"""Error in construction of usage-message by developer."""
class DocoptExit(SystemExit):
"""Exit in case user invoked program with incorrect arguments."""
usage = ''
def __init__(self, message=''):
SystemExit.__init__(self, (message + '\n' + self.usage).strip())
class Pattern(object):
def __eq__(self, other):
return repr(self) == repr(other)
def __hash__(self):
return hash(repr(self))
def fix(self):
self.fix_identities()
self.fix_repeating_arguments()
return self
def fix_identities(self, uniq=None):
"""Make pattern-tree tips point to same object if they are equal."""
if not hasattr(self, 'children'):
return self
uniq = list(set(self.flat())) if uniq is None else uniq
for i, child in enumerate(self.children):
if not hasattr(child, 'children'):
assert child in uniq
self.children[i] = uniq[uniq.index(child)]
else:
child.fix_identities(uniq)
def fix_repeating_arguments(self):
"""Fix elements that should accumulate/increment values."""
either = [list(child.children) for child in transform(self).children]
for case in either:
for e in [child for child in case if case.count(child) > 1]:
if type(e) is Argument or type(e) is Option and e.argcount:
if e.value is None:
e.value = []
elif type(e.value) is not list:
e.value = e.value.split()
if type(e) is Command or type(e) is Option and e.argcount == 0:
e.value = 0
return self
def transform(pattern):
"""Expand pattern into an (almost) equivalent one, but with single Either.
Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d)
Quirks: [-a] => (-a), (-a...) => (-a -a)
"""
result = []
groups = [[pattern]]
while groups:
children = groups.pop(0)
parents = [Required, Optional, OptionsShortcut, Either, OneOrMore]
if any(t in map(type, children) for t in parents):
child = [c for c in children if type(c) in parents][0]
children.remove(child)
if type(child) is Either:
for c in child.children:
groups.append([c] + children)
elif type(child) is OneOrMore:
groups.append(child.children * 2 + children)
else:
groups.append(child.children + children)
else:
result.append(children)
return Either(*[Required(*e) for e in result])
class LeafPattern(Pattern):
"""Leaf/terminal node of a pattern tree."""
def __init__(self, name, value=None):
self.name, self.value = name, value
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
def flat(self, *types):
return [self] if not types or type(self) in types else []
def match(self, left, collected=None):
collected = [] if collected is None else collected
pos, match = self.single_match(left)
if match is None:
return False, left, collected
left_ = left[:pos] + left[pos + 1:]
same_name = [a for a in collected if a.name == self.name]
if type(self.value) in (int, list):
if type(self.value) is int:
increment = 1
else:
increment = ([match.value] if type(match.value) is str
else match.value)
if not same_name:
match.value = increment
return True, left_, collected + [match]
same_name[0].value += increment
return True, left_, collected
return True, left_, collected + [match]
class BranchPattern(Pattern):
"""Branch/inner node of a pattern tree."""
def __init__(self, *children):
self.children = list(children)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__,
', '.join(repr(a) for a in self.children))
def flat(self, *types):
if type(self) in types:
return [self]
return sum([child.flat(*types) for child in self.children], [])
class Argument(LeafPattern):
def single_match(self, left):
for n, pattern in enumerate(left):
if type(pattern) is Argument:
return n, Argument(self.name, pattern.value)
return None, None
@classmethod
def parse(class_, source):
name = re.findall('(<\S*?>)', source)[0]
value = re.findall('\[default: (.*)\]', source, flags=re.I)
return class_(name, value[0] if value else None)
class Command(Argument):
def __init__(self, name, value=False):
self.name, self.value = name, value
def single_match(self, left):
for n, pattern in enumerate(left):
if type(pattern) is Argument:
if pattern.value == self.name:
return n, Command(self.name, True)
else:
break
return None, None
class Option(LeafPattern):
def __init__(self, short=None, long=None, argcount=0, value=False):
assert argcount in (0, 1)
self.short, self.long, self.argcount = short, long, argcount
self.value = None if value is False and argcount else value
@classmethod
def parse(class_, option_description):
short, long, argcount, value = None, None, 0, False
options, _, description = option_description.strip().partition(' ')
options = options.replace(',', ' ').replace('=', ' ')
for s in options.split():
if s.startswith('--'):
long = s
elif s.startswith('-'):
short = s
else:
argcount = 1
if argcount:
matched = re.findall('\[default: (.*)\]', description, flags=re.I)
value = matched[0] if matched else None
return class_(short, long, argcount, value)
def single_match(self, left):
for n, pattern in enumerate(left):
if self.name == pattern.name:
return n, pattern
return None, None
@property
def name(self):
return self.long or self.short
def __repr__(self):
return 'Option(%r, %r, %r, %r)' % (self.short, self.long,
self.argcount, self.value)
class Required(BranchPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
l = left
c = collected
for pattern in self.children:
matched, l, c = pattern.match(l, c)
if not matched:
return False, left, collected
return True, l, c
class Optional(BranchPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
for pattern in self.children:
m, left, collected = pattern.match(left, collected)
return True, left, collected
class OptionsShortcut(Optional):
"""Marker/placeholder for [options] shortcut."""
class OneOrMore(BranchPattern):
def match(self, left, collected=None):
assert len(self.children) == 1
collected = [] if collected is None else collected
l = left
c = collected
l_ = None
matched = True
times = 0
while matched:
# could it be that something didn't match but changed l or c?
matched, l, c = self.children[0].match(l, c)
times += 1 if matched else 0
if l_ == l:
break
l_ = l
if times >= 1:
return True, l, c
return False, left, collected
class Either(BranchPattern):
def match(self, left, collected=None):
collected = [] if collected is None else collected
outcomes = []
for pattern in self.children:
matched, _, _ = outcome = pattern.match(left, collected)
if matched:
outcomes.append(outcome)
if outcomes:
return min(outcomes, key=lambda outcome: len(outcome[1]))
return False, left, collected
class Tokens(list):
def __init__(self, source, error=DocoptExit):
self += source.split() if hasattr(source, 'split') else source
self.error = error
@staticmethod
def from_pattern(source):
source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source)
source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s]
return Tokens(source, error=DocoptLanguageError)
def move(self):
return self.pop(0) if len(self) else None
def current(self):
return self[0] if len(self) else None
def parse_long(tokens, options):
"""long ::= '--' chars [ ( ' ' | '=' ) chars ] ;"""
long, eq, value = tokens.move().partition('=')
assert long.startswith('--')
value = None if eq == value == '' else value
similar = [o for o in options if o.long == long]
if tokens.error is DocoptExit and similar == []: # if no exact match
similar = [o for o in options if o.long and o.long.startswith(long)]
if len(similar) > 1: # might be simply specified ambiguously 2+ times?
raise tokens.error('%s is not a unique prefix: %s?' %
(long, ', '.join(o.long for o in similar)))
elif len(similar) < 1:
argcount = 1 if eq == '=' else 0
o = Option(None, long, argcount)
options.append(o)
if tokens.error is DocoptExit:
o = Option(None, long, argcount, value if argcount else True)
else:
o = Option(similar[0].short, similar[0].long,
similar[0].argcount, similar[0].value)
if o.argcount == 0:
if value is not None:
raise tokens.error('%s must not have an argument' % o.long)
else:
if value is None:
if tokens.current() in [None, '--']:
raise tokens.error('%s requires argument' % o.long)
value = tokens.move()
if tokens.error is DocoptExit:
o.value = value if value is not None else True
return [o]
def parse_shorts(tokens, options):
"""shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;"""
token = tokens.move()
assert token.startswith('-') and not token.startswith('--')
left = token.lstrip('-')
parsed = []
while left != '':
short, left = '-' + left[0], left[1:]
similar = [o for o in options if o.short == short]
if len(similar) > 1:
raise tokens.error('%s is specified ambiguously %d times' %
(short, len(similar)))
elif len(similar) < 1:
o = Option(short, None, 0)
options.append(o)
if tokens.error is DocoptExit:
o = Option(short, None, 0, True)
else: # why copying is necessary here?
o = Option(short, similar[0].long,
similar[0].argcount, similar[0].value)
value = None
if o.argcount != 0:
if left == '':
if tokens.current() in [None, '--']:
raise tokens.error('%s requires argument' % short)
value = tokens.move()
else:
value = left
left = ''
if tokens.error is DocoptExit:
o.value = value if value is not None else True
parsed.append(o)
return parsed
def parse_pattern(source, options):
tokens = Tokens.from_pattern(source)
result = parse_expr(tokens, options)
if tokens.current() is not None:
raise tokens.error('unexpected ending: %r' % ' '.join(tokens))
return Required(*result)
def parse_expr(tokens, options):
"""expr ::= seq ( '|' seq )* ;"""
seq = parse_seq(tokens, options)
if tokens.current() != '|':
return seq
result = [Required(*seq)] if len(seq) > 1 else seq
while tokens.current() == '|':
tokens.move()
seq = parse_seq(tokens, options)
result += [Required(*seq)] if len(seq) > 1 else seq
return [Either(*result)] if len(result) > 1 else result
def parse_seq(tokens, options):
"""seq ::= ( atom [ '...' ] )* ;"""
result = []
while tokens.current() not in [None, ']', ')', '|']:
atom = parse_atom(tokens, options)
if tokens.current() == '...':
atom = [OneOrMore(*atom)]
tokens.move()
result += atom
return result
def parse_atom(tokens, options):
"""atom ::= '(' expr ')' | '[' expr ']' | 'options'
| long | shorts | argument | command ;
"""
token = tokens.current()
result = []
if token in '([':
tokens.move()
matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token]
result = pattern(*parse_expr(tokens, options))
if tokens.move() != matching:
raise tokens.error("unmatched '%s'" % token)
return [result]
elif token == 'options':
tokens.move()
return [OptionsShortcut()]
elif token.startswith('--') and token != '--':
return parse_long(tokens, options)
elif token.startswith('-') and token not in ('-', '--'):
return parse_shorts(tokens, options)
elif token.startswith('<') and token.endswith('>') or token.isupper():
return [Argument(tokens.move())]
else:
return [Command(tokens.move())]
def parse_argv(tokens, options, options_first=False):
"""Parse command-line argument vector.
If options_first:
argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ;
else:
argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ;
"""
parsed = []
while tokens.current() is not None:
if tokens.current() == '--':
return parsed + [Argument(None, v) for v in tokens]
elif tokens.current().startswith('--'):
parsed += parse_long(tokens, options)
elif tokens.current().startswith('-') and tokens.current() != '-':
parsed += parse_shorts(tokens, options)
elif options_first:
return parsed + [Argument(None, v) for v in tokens]
else:
parsed.append(Argument(None, tokens.move()))
return parsed
def parse_defaults(doc):
defaults = []
for s in parse_section('options:', doc):
# FIXME corner case "bla: options: --foo"
_, _, s = s.partition(':') # get rid of "options:"
split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:]
split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
options = [Option.parse(s) for s in split if s.startswith('-')]
defaults += options
return defaults
def parse_section(name, source):
pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)',
re.IGNORECASE | re.MULTILINE)
return [s.strip() for s in pattern.findall(source)]
def formal_usage(section):
_, _, section = section.partition(':') # drop "usage:"
pu = section.split()
return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )'
def extras(help, version, options, doc):
if help and any((o.name in ('-h', '--help')) and o.value for o in options):
print(doc.strip("\n"))
sys.exit()
if version and any(o.name == '--version' and o.value for o in options):
print(version)
sys.exit()
class Dict(dict):
def __repr__(self):
return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items()))
def docopt(doc, argv=None, help=True, version=None, options_first=False):
"""Parse `argv` based on command-line interface described in `doc`.
`docopt` creates your command-line interface based on its
description that you pass as `doc`. Such description can contain
--options, <positional-argument>, commands, which could be
[optional], (required), (mutually | exclusive) or repeated...
Parameters
----------
doc : str
Description of your command-line interface.
argv : list of str, optional
Argument vector to be parsed. sys.argv[1:] is used if not
provided.
help : bool (default: True)
Set to False to disable automatic help on -h or --help
options.
version : any object
If passed, the object will be printed if --version is in
`argv`.
options_first : bool (default: False)
Set to True to require options precede positional arguments,
i.e. to forbid options and positional arguments intermix.
Returns
-------
args : dict
A dictionary, where keys are names of command-line elements
such as e.g. "--verbose" and "<path>", and values are the
parsed values of those elements.
Example
-------
>>> from docopt import docopt
>>> doc = '''
... Usage:
... my_program tcp <host> <port> [--timeout=<seconds>]
... my_program serial <port> [--baud=<n>] [--timeout=<seconds>]
... my_program (-h | --help | --version)
...
... Options:
... -h, --help Show this screen and exit.
... --baud=<n> Baudrate [default: 9600]
... '''
>>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30']
>>> docopt(doc, argv)
{'--baud': '9600',
'--help': False,
'--timeout': '30',
'--version': False,
'<host>': '127.0.0.1',
'<port>': '80',
'serial': False,
'tcp': True}
See also
--------
* For video introduction see http://docopt.org
* Full documentation is available in README.rst as well as online
at https://github.com/docopt/docopt#readme
"""
argv = sys.argv[1:] if argv is None else argv
usage_sections = parse_section('usage:', doc)
if len(usage_sections) == 0:
raise DocoptLanguageError('"usage:" (case-insensitive) not found.')
if len(usage_sections) > 1:
raise DocoptLanguageError('More than one "usage:" (case-insensitive).')
DocoptExit.usage = usage_sections[0]
options = parse_defaults(doc)
pattern = parse_pattern(formal_usage(DocoptExit.usage), options)
# [default] syntax for argument is disabled
#for a in pattern.flat(Argument):
# same_name = [d for d in arguments if d.name == a.name]
# if same_name:
# a.value = same_name[0].value
argv = parse_argv(Tokens(argv), list(options), options_first)
pattern_options = set(pattern.flat(Option))
for options_shortcut in pattern.flat(OptionsShortcut):
doc_options = parse_defaults(doc)
options_shortcut.children = list(set(doc_options) - pattern_options)
#if any_options:
# options_shortcut.children += [Option(o.short, o.long, o.argcount)
# for o in argv if type(o) is Option]
extras(help, version, argv, doc)
matched, left, collected = pattern.fix().match(argv)
if matched and left == []: # better error message if left?
return Dict((a.name, a.value) for a in (pattern.flat() + collected))
raise DocoptExit()

View File

@ -1,303 +0,0 @@
#!/usr/bin/env python3
"""Manage site and releases.
Usage:
manage.py release [<branch>]
manage.py site
For the release command $FMT_TOKEN should contain a GitHub personal access token
obtained from https://github.com/settings/tokens.
"""
from __future__ import print_function
import datetime, docopt, errno, fileinput, json, os
import re, requests, shutil, sys, tempfile
from contextlib import contextmanager
from distutils.version import LooseVersion
from subprocess import check_call
class Git:
def __init__(self, dir):
self.dir = dir
def call(self, method, args, **kwargs):
return check_call(['git', method] + list(args), **kwargs)
def add(self, *args):
return self.call('add', args, cwd=self.dir)
def checkout(self, *args):
return self.call('checkout', args, cwd=self.dir)
def clean(self, *args):
return self.call('clean', args, cwd=self.dir)
def clone(self, *args):
return self.call('clone', list(args) + [self.dir])
def commit(self, *args):
return self.call('commit', args, cwd=self.dir)
def pull(self, *args):
return self.call('pull', args, cwd=self.dir)
def push(self, *args):
return self.call('push', args, cwd=self.dir)
def reset(self, *args):
return self.call('reset', args, cwd=self.dir)
def update(self, *args):
clone = not os.path.exists(self.dir)
if clone:
self.clone(*args)
return clone
def clean_checkout(repo, branch):
repo.clean('-f', '-d')
repo.reset('--hard')
repo.checkout(branch)
class Runner:
def __init__(self, cwd):
self.cwd = cwd
def __call__(self, *args, **kwargs):
kwargs['cwd'] = kwargs.get('cwd', self.cwd)
check_call(args, **kwargs)
def create_build_env():
"""Create a build environment."""
class Env:
pass
env = Env()
# Import the documentation build module.
env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(env.fmt_dir, 'doc'))
import build
env.build_dir = 'build'
env.versions = build.versions
# Virtualenv and repos are cached to speed up builds.
build.create_build_env(os.path.join(env.build_dir, 'virtualenv'))
env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt'))
return env
@contextmanager
def rewrite(filename):
class Buffer:
pass
buffer = Buffer()
if not os.path.exists(filename):
buffer.data = ''
yield buffer
return
with open(filename) as f:
buffer.data = f.read()
yield buffer
with open(filename, 'w') as f:
f.write(buffer.data)
fmt_repo_url = 'git@github.com:fmtlib/fmt'
def update_site(env):
env.fmt_repo.update(fmt_repo_url)
doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io'))
doc_repo.update('git@github.com:fmtlib/fmtlib.github.io')
for version in env.versions:
clean_checkout(env.fmt_repo, version)
target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc')
# Remove the old theme.
for entry in os.listdir(target_doc_dir):
path = os.path.join(target_doc_dir, entry)
if os.path.isdir(path):
shutil.rmtree(path)
# Copy the new theme.
for entry in ['_static', '_templates', 'basic-bootstrap', 'bootstrap',
'conf.py', 'fmt.less']:
src = os.path.join(env.fmt_dir, 'doc', entry)
dst = os.path.join(target_doc_dir, entry)
copy = shutil.copytree if os.path.isdir(src) else shutil.copyfile
copy(src, dst)
# Rename index to contents.
contents = os.path.join(target_doc_dir, 'contents.rst')
if not os.path.exists(contents):
os.rename(os.path.join(target_doc_dir, 'index.rst'), contents)
# Fix issues in reference.rst/api.rst.
for filename in ['reference.rst', 'api.rst', 'index.rst']:
pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M)
with rewrite(os.path.join(target_doc_dir, filename)) as b:
b.data = b.data.replace('std::ostream &', 'std::ostream&')
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
b.data = b.data.replace('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned')
#b.data = b.data.replace('operator""_', 'operator"" _')
b.data = b.data.replace(
'format_to_n(OutputIt, size_t, string_view, Args&&',
'format_to_n(OutputIt, size_t, const S&, const Args&')
b.data = b.data.replace(
'format_to_n(OutputIt, std::size_t, string_view, Args&&',
'format_to_n(OutputIt, std::size_t, const S&, const Args&')
if version == ('3.0.2'):
b.data = b.data.replace(
'fprintf(std::ostream&', 'fprintf(std::ostream &')
if version == ('5.3.0'):
b.data = b.data.replace(
'format_to(OutputIt, const S&, const Args&...)',
'format_to(OutputIt, const S &, const Args &...)')
if version.startswith('5.') or version.startswith('6.'):
b.data = b.data.replace(', size_t', ', std::size_t')
if version.startswith('7.'):
b.data = b.data.replace(', std::size_t', ', size_t')
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
if version.startswith('7.1.'):
b.data = b.data.replace(', std::size_t', ', size_t')
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
b.data = b.data.replace(
'fmt::format_to(OutputIt, const S&, Args&&...)',
'fmt::format_to(OutputIt, const S&, Args&&...) -> ' +
'typename std::enable_if<enable, OutputIt>::type')
b.data = b.data.replace('aa long', 'a long')
b.data = b.data.replace('serveral', 'several')
if version.startswith('6.2.'):
b.data = b.data.replace(
'vformat(const S&, basic_format_args<' +
'buffer_context<Char>>)',
'vformat(const S&, basic_format_args<' +
'buffer_context<type_identity_t<Char>>>)')
# Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b:
b.data = b.data.replace(
'doc/latest/index.html#format-string-syntax', 'syntax.html')
# Fix issues in syntax.rst.
index = os.path.join(target_doc_dir, 'syntax.rst')
with rewrite(index) as b:
b.data = b.data.replace(
'..productionlist:: sf\n', '.. productionlist:: sf\n ')
b.data = b.data.replace('Examples:\n', 'Examples::\n')
# Build the docs.
html_dir = os.path.join(env.build_dir, 'html')
if os.path.exists(html_dir):
shutil.rmtree(html_dir)
include_dir = env.fmt_repo.dir
if LooseVersion(version) >= LooseVersion('5.0.0'):
include_dir = os.path.join(include_dir, 'include', 'fmt')
elif LooseVersion(version) >= LooseVersion('3.0.0'):
include_dir = os.path.join(include_dir, 'fmt')
import build
build.build_docs(version, doc_dir=target_doc_dir,
include_dir=include_dir, work_dir=env.build_dir)
shutil.rmtree(os.path.join(html_dir, '.doctrees'))
# Create symlinks for older versions.
for link, target in {'index': 'contents', 'api': 'reference'}.items():
link = os.path.join(html_dir, link) + '.html'
target += '.html'
if os.path.exists(os.path.join(html_dir, target)) and \
not os.path.exists(link):
os.symlink(target, link)
# Copy docs to the website.
version_doc_dir = os.path.join(doc_repo.dir, version)
try:
shutil.rmtree(version_doc_dir)
except OSError as e:
if e.errno != errno.ENOENT:
raise
shutil.move(html_dir, version_doc_dir)
def release(args):
env = create_build_env()
fmt_repo = env.fmt_repo
branch = args.get('<branch>')
if branch is None:
branch = 'master'
if not fmt_repo.update('-b', branch, fmt_repo_url):
clean_checkout(fmt_repo, branch)
# Convert changelog from RST to GitHub-flavored Markdown and get the
# version.
changelog = 'ChangeLog.rst'
changelog_path = os.path.join(fmt_repo.dir, changelog)
import rst2md
changes, version = rst2md.convert(changelog_path)
cmakelists = 'CMakeLists.txt'
for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists),
inplace=True):
prefix = 'set(FMT_VERSION '
if line.startswith(prefix):
line = prefix + version + ')\n'
sys.stdout.write(line)
# Update the version in the changelog.
title_len = 0
for line in fileinput.input(changelog_path, inplace=True):
if line.startswith(version + ' - TBD'):
line = version + ' - ' + datetime.date.today().isoformat()
title_len = len(line)
line += '\n'
elif title_len:
line = '-' * title_len + '\n'
title_len = 0
sys.stdout.write(line)
# Add the version to the build script.
script = os.path.join('doc', 'build.py')
script_path = os.path.join(fmt_repo.dir, script)
for line in fileinput.input(script_path, inplace=True):
m = re.match(r'( *versions = )\[(.+)\]', line)
if m:
line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version)
sys.stdout.write(line)
fmt_repo.checkout('-B', 'release')
fmt_repo.add(changelog, cmakelists, script)
fmt_repo.commit('-m', 'Update version')
# Build the docs and package.
run = Runner(fmt_repo.dir)
run('cmake', '.')
run('make', 'doc', 'package_source')
update_site(env)
# Create a release on GitHub.
fmt_repo.push('origin', 'release')
auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
headers=auth_headers,
data=json.dumps({'tag_name': version,
'target_commitish': 'release',
'body': changes, 'draft': True}))
if r.status_code != 201:
raise Exception('Failed to create a release ' + str(r))
id = r.json()['id']
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
package = 'fmt-{}.zip'.format(version)
r = requests.post(
'{}/{}/assets?name={}'.format(uploads_url, id, package),
headers={'Content-Type': 'application/zip'} | auth_headers,
data=open('build/fmt/' + package, 'rb'))
if r.status_code != 201:
raise Exception('Failed to upload an asset ' + str(r))
if __name__ == '__main__':
args = docopt.docopt(__doc__)
if args.get('release'):
release(args)
elif args.get('site'):
update_site(create_build_env())

View File

@ -1,201 +0,0 @@
#!/usr/bin/env python3
# This script is based on
# https://github.com/rust-lang/rust/blob/master/library/core/src/unicode/printable.py
# distributed under https://github.com/rust-lang/rust/blob/master/LICENSE-MIT.
# This script uses the following Unicode tables:
# - UnicodeData.txt
from collections import namedtuple
import csv
import os
import subprocess
NUM_CODEPOINTS=0x110000
def to_ranges(iter):
current = None
for i in iter:
if current is None or i != current[1] or i in (0x10000, 0x20000):
if current is not None:
yield tuple(current)
current = [i, i + 1]
else:
current[1] += 1
if current is not None:
yield tuple(current)
def get_escaped(codepoints):
for c in codepoints:
if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '):
yield c.value
def get_file(f):
try:
return open(os.path.basename(f))
except FileNotFoundError:
subprocess.run(["curl", "-O", f], check=True)
return open(os.path.basename(f))
Codepoint = namedtuple('Codepoint', 'value class_')
def get_codepoints(f):
r = csv.reader(f, delimiter=";")
prev_codepoint = 0
class_first = None
for row in r:
codepoint = int(row[0], 16)
name = row[1]
class_ = row[2]
if class_first is not None:
if not name.endswith("Last>"):
raise ValueError("Missing Last after First")
for c in range(prev_codepoint + 1, codepoint):
yield Codepoint(c, class_first)
class_first = None
if name.endswith("First>"):
class_first = class_
yield Codepoint(codepoint, class_)
prev_codepoint = codepoint
if class_first is not None:
raise ValueError("Missing Last after First")
for c in range(prev_codepoint + 1, NUM_CODEPOINTS):
yield Codepoint(c, None)
def compress_singletons(singletons):
uppers = [] # (upper, # items in lowers)
lowers = []
for i in singletons:
upper = i >> 8
lower = i & 0xff
if len(uppers) == 0 or uppers[-1][0] != upper:
uppers.append((upper, 1))
else:
upper, count = uppers[-1]
uppers[-1] = upper, count + 1
lowers.append(lower)
return uppers, lowers
def compress_normal(normal):
# lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f
# lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff
compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)]
prev_start = 0
for start, count in normal:
truelen = start - prev_start
falselen = count
prev_start = start + count
assert truelen < 0x8000 and falselen < 0x8000
entry = []
if truelen > 0x7f:
entry.append(0x80 | (truelen >> 8))
entry.append(truelen & 0xff)
else:
entry.append(truelen & 0x7f)
if falselen > 0x7f:
entry.append(0x80 | (falselen >> 8))
entry.append(falselen & 0xff)
else:
entry.append(falselen & 0x7f)
compressed.append(entry)
return compressed
def print_singletons(uppers, lowers, uppersname, lowersname):
print(" static constexpr singleton {}[] = {{".format(uppersname))
for u, c in uppers:
print(" {{{:#04x}, {}}},".format(u, c))
print(" };")
print(" static constexpr unsigned char {}[] = {{".format(lowersname))
for i in range(0, len(lowers), 8):
print(" {}".format(" ".join("{:#04x},".format(l) for l in lowers[i:i+8])))
print(" };")
def print_normal(normal, normalname):
print(" static constexpr unsigned char {}[] = {{".format(normalname))
for v in normal:
print(" {}".format(" ".join("{:#04x},".format(i) for i in v)))
print(" };")
def main():
file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt")
codepoints = get_codepoints(file)
CUTOFF=0x10000
singletons0 = []
singletons1 = []
normal0 = []
normal1 = []
extra = []
for a, b in to_ranges(get_escaped(codepoints)):
if a > 2 * CUTOFF:
extra.append((a, b - a))
elif a == b - 1:
if a & CUTOFF:
singletons1.append(a & ~CUTOFF)
else:
singletons0.append(a)
elif a == b - 2:
if a & CUTOFF:
singletons1.append(a & ~CUTOFF)
singletons1.append((a + 1) & ~CUTOFF)
else:
singletons0.append(a)
singletons0.append(a + 1)
else:
if a >= 2 * CUTOFF:
extra.append((a, b - a))
elif a & CUTOFF:
normal1.append((a & ~CUTOFF, b - a))
else:
normal0.append((a, b - a))
singletons0u, singletons0l = compress_singletons(singletons0)
singletons1u, singletons1l = compress_singletons(singletons1)
normal0 = compress_normal(normal0)
normal1 = compress_normal(normal1)
print("""\
FMT_FUNC auto is_printable(uint32_t cp) -> bool {\
""")
print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower')
print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower')
print_normal(normal0, 'normal0')
print_normal(normal1, 'normal1')
print("""\
auto lower = static_cast<uint16_t>(cp);
if (cp < 0x10000) {
return is_printable(lower, singletons0,
sizeof(singletons0) / sizeof(*singletons0),
singletons0_lower, normal0, sizeof(normal0));
}
if (cp < 0x20000) {
return is_printable(lower, singletons1,
sizeof(singletons1) / sizeof(*singletons1),
singletons1_lower, normal1, sizeof(normal1));
}\
""")
for a, b in extra:
print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b))
print("""\
return cp < 0x{:x};
}}\
""".format(NUM_CODEPOINTS))
if __name__ == '__main__':
main()

View File

@ -1,159 +0,0 @@
#!/usr/bin/env python
# reStructuredText (RST) to GitHub-flavored Markdown converter
import re, sys
from docutils import core, nodes, writers
def is_github_ref(node):
return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri'])
class Translator(nodes.NodeVisitor):
def __init__(self, document):
nodes.NodeVisitor.__init__(self, document)
self.output = ''
self.indent = 0
self.preserve_newlines = False
def write(self, text):
self.output += text.replace('\n', '\n' + ' ' * self.indent)
def visit_document(self, node):
pass
def depart_document(self, node):
pass
def visit_section(self, node):
pass
def depart_section(self, node):
# Skip all sections except the first one.
raise nodes.StopTraversal
def visit_title(self, node):
self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1)
raise nodes.SkipChildren
def visit_title_reference(self, node):
raise Exception(node)
def depart_title(self, node):
pass
def visit_Text(self, node):
if not self.preserve_newlines:
node = node.replace('\n', ' ')
self.write(node)
def depart_Text(self, node):
pass
def visit_bullet_list(self, node):
pass
def depart_bullet_list(self, node):
pass
def visit_list_item(self, node):
self.write('* ')
self.indent += 2
def depart_list_item(self, node):
self.indent -= 2
self.write('\n\n')
def visit_paragraph(self, node):
self.write('\n\n')
def depart_paragraph(self, node):
pass
def visit_reference(self, node):
if not is_github_ref(node):
self.write('[')
def depart_reference(self, node):
if not is_github_ref(node):
self.write('](' + node['refuri'] + ')')
def visit_target(self, node):
pass
def depart_target(self, node):
pass
def visit_literal(self, node):
self.write('`')
def depart_literal(self, node):
self.write('`')
def visit_literal_block(self, node):
self.write('\n\n```')
if 'c++' in node['classes']:
self.write('c++')
self.write('\n')
self.preserve_newlines = True
def depart_literal_block(self, node):
self.write('\n```\n')
self.preserve_newlines = False
def visit_inline(self, node):
pass
def depart_inline(self, node):
pass
def visit_image(self, node):
self.write('![](' + node['uri'] + ')')
def depart_image(self, node):
pass
def write_row(self, row, widths):
for i, entry in enumerate(row):
text = entry[0][0] if len(entry) > 0 else ''
if i != 0:
self.write('|')
self.write('{:{}}'.format(text, widths[i]))
self.write('\n')
def visit_table(self, node):
table = node.children[0]
colspecs = table[:-2]
thead = table[-2]
tbody = table[-1]
widths = [int(cs['colwidth']) for cs in colspecs]
sep = '|'.join(['-' * w for w in widths]) + '\n'
self.write('\n\n')
self.write_row(thead[0], widths)
self.write(sep)
for row in tbody:
self.write_row(row, widths)
raise nodes.SkipChildren
def depart_table(self, node):
pass
class MDWriter(writers.Writer):
"""GitHub-flavored markdown writer"""
supported = ('md',)
"""Formats this writer supports."""
def translate(self):
translator = Translator(self.document)
self.document.walkabout(translator)
self.output = (translator.output, translator.version)
def convert(rst_path):
"""Converts RST file to Markdown."""
return core.publish_file(source_path=rst_path, writer=MDWriter())
if __name__ == '__main__':
convert(sys.argv[1])

View File

@ -1,7 +0,0 @@
# Sphinx configuration for readthedocs.
import os, sys
master_doc = 'index'
html_theme = 'theme'
html_theme_path = ["."]

View File

@ -1,2 +0,0 @@
If you are not redirected automatically, follow the
`link to the fmt documentation <https://fmt.dev/latest/>`_.

View File

@ -1,17 +0,0 @@
{% extends "basic/layout.html" %}
{% block extrahead %}
<meta charset="UTF-8">
<meta http-equiv="refresh" content="1;url=https://fmt.dev/latest/">
<script type="text/javascript">
window.location.href = "https://fmt.dev/latest/"
</script>
<title>Page Redirection</title>
{% endblock %}
{% block document %}
If you are not redirected automatically, follow the <a href='https://fmt.dev/latest/'>link to the fmt documentation</a>.
{% endblock %}
{% block footer %}
{% endblock %}

View File

@ -1,2 +0,0 @@
[theme]
inherit = basic

View File

@ -60,27 +60,28 @@ const char lua_ident[] =
static TValue *index2value (lua_State *L, int idx) { static TValue *index2value (lua_State *L, int idx) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
if (idx > 0) { if (idx > 0) {
StkId o = ci->func + idx; StkId o = ci->func.p + idx;
api_check(L, idx <= L->ci->top - (ci->func + 1), "unacceptable index"); api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index");
if (o >= L->top) return &G(L)->nilvalue; if (o >= L->top.p) return &G(L)->nilvalue;
else return s2v(o); else return s2v(o);
} }
else if (!ispseudo(idx)) { /* negative index */ else if (!ispseudo(idx)) { /* negative index */
api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
return s2v(L->top + idx); "invalid index");
return s2v(L->top.p + idx);
} }
else if (idx == LUA_REGISTRYINDEX) else if (idx == LUA_REGISTRYINDEX)
return &G(L)->l_registry; return &G(L)->l_registry;
else { /* upvalues */ else { /* upvalues */
idx = LUA_REGISTRYINDEX - idx; idx = LUA_REGISTRYINDEX - idx;
api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
if (ttisCclosure(s2v(ci->func))) { /* C closure? */ if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */
CClosure *func = clCvalue(s2v(ci->func)); CClosure *func = clCvalue(s2v(ci->func.p));
return (idx <= func->nupvalues) ? &func->upvalue[idx-1] return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
: &G(L)->nilvalue; : &G(L)->nilvalue;
} }
else { /* light C function or Lua function (through a hook)?) */ else { /* light C function or Lua function (through a hook)?) */
api_check(L, ttislcf(s2v(ci->func)), "caller not a C function"); api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function");
return &G(L)->nilvalue; /* no upvalues */ return &G(L)->nilvalue; /* no upvalues */
} }
} }
@ -94,14 +95,15 @@ static TValue *index2value (lua_State *L, int idx) {
l_sinline StkId index2stack (lua_State *L, int idx) { l_sinline StkId index2stack (lua_State *L, int idx) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
if (idx > 0) { if (idx > 0) {
StkId o = ci->func + idx; StkId o = ci->func.p + idx;
api_check(L, o < L->top, "invalid index"); api_check(L, o < L->top.p, "invalid index");
return o; return o;
} }
else { /* non-positive index */ else { /* non-positive index */
api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
"invalid index");
api_check(L, !ispseudo(idx), "invalid index"); api_check(L, !ispseudo(idx), "invalid index");
return L->top + idx; return L->top.p + idx;
} }
} }
@ -112,17 +114,12 @@ LUA_API int lua_checkstack (lua_State *L, int n) {
lua_lock(L); lua_lock(L);
ci = L->ci; ci = L->ci;
api_check(L, n >= 0, "negative 'n'"); api_check(L, n >= 0, "negative 'n'");
if (L->stack_last - L->top > n) /* stack large enough? */ if (L->stack_last.p - L->top.p > n) /* stack large enough? */
res = 1; /* yes; check is OK */ res = 1; /* yes; check is OK */
else { /* no; need to grow stack */ else /* need to grow stack */
int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; res = luaD_growstack(L, n, 0);
if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ if (res && ci->top.p < L->top.p + n)
res = 0; /* no */ ci->top.p = L->top.p + n; /* adjust frame top */
else /* try to grow stack */
res = luaD_growstack(L, n, 0);
}
if (res && ci->top < L->top + n)
ci->top = L->top + n; /* adjust frame top */
lua_unlock(L); lua_unlock(L);
return res; return res;
} }
@ -134,11 +131,11 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
lua_lock(to); lua_lock(to);
api_checknelems(from, n); api_checknelems(from, n);
api_check(from, G(from) == G(to), "moving among independent states"); api_check(from, G(from) == G(to), "moving among independent states");
api_check(from, to->ci->top - to->top >= n, "stack overflow"); api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow");
from->top -= n; from->top.p -= n;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
setobjs2s(to, to->top, from->top + i); setobjs2s(to, to->top.p, from->top.p + i);
to->top++; /* stack already checked by previous 'api_check' */ to->top.p++; /* stack already checked by previous 'api_check' */
} }
lua_unlock(to); lua_unlock(to);
} }
@ -172,12 +169,12 @@ LUA_API lua_Number lua_version (lua_State *L) {
LUA_API int lua_absindex (lua_State *L, int idx) { LUA_API int lua_absindex (lua_State *L, int idx) {
return (idx > 0 || ispseudo(idx)) return (idx > 0 || ispseudo(idx))
? idx ? idx
: cast_int(L->top - L->ci->func) + idx; : cast_int(L->top.p - L->ci->func.p) + idx;
} }
LUA_API int lua_gettop (lua_State *L) { LUA_API int lua_gettop (lua_State *L) {
return cast_int(L->top - (L->ci->func + 1)); return cast_int(L->top.p - (L->ci->func.p + 1));
} }
@ -187,24 +184,24 @@ LUA_API void lua_settop (lua_State *L, int idx) {
ptrdiff_t diff; /* difference for new top */ ptrdiff_t diff; /* difference for new top */
lua_lock(L); lua_lock(L);
ci = L->ci; ci = L->ci;
func = ci->func; func = ci->func.p;
if (idx >= 0) { if (idx >= 0) {
api_check(L, idx <= ci->top - (func + 1), "new top too large"); api_check(L, idx <= ci->top.p - (func + 1), "new top too large");
diff = ((func + 1) + idx) - L->top; diff = ((func + 1) + idx) - L->top.p;
for (; diff > 0; diff--) for (; diff > 0; diff--)
setnilvalue(s2v(L->top++)); /* clear new slots */ setnilvalue(s2v(L->top.p++)); /* clear new slots */
} }
else { else {
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top");
diff = idx + 1; /* will "subtract" index (as it is negative) */ diff = idx + 1; /* will "subtract" index (as it is negative) */
} }
api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot"); api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot");
newtop = L->top + diff; newtop = L->top.p + diff;
if (diff < 0 && L->tbclist >= newtop) { if (diff < 0 && L->tbclist.p >= newtop) {
lua_assert(hastocloseCfunc(ci->nresults)); lua_assert(hastocloseCfunc(ci->nresults));
luaF_close(L, newtop, CLOSEKTOP, 0); newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
} }
L->top = newtop; /* correct top only after closing any upvalue */ L->top.p = newtop; /* correct top only after closing any upvalue */
lua_unlock(L); lua_unlock(L);
} }
@ -213,10 +210,9 @@ LUA_API void lua_closeslot (lua_State *L, int idx) {
StkId level; StkId level;
lua_lock(L); lua_lock(L);
level = index2stack(L, idx); level = index2stack(L, idx);
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level, api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level,
"no variable to close at given level"); "no variable to close at given level");
luaF_close(L, level, CLOSEKTOP, 0); level = luaF_close(L, level, CLOSEKTOP, 0);
level = index2stack(L, idx); /* stack may be moved */
setnilvalue(s2v(level)); setnilvalue(s2v(level));
lua_unlock(L); lua_unlock(L);
} }
@ -245,7 +241,7 @@ l_sinline void reverse (lua_State *L, StkId from, StkId to) {
LUA_API void lua_rotate (lua_State *L, int idx, int n) { LUA_API void lua_rotate (lua_State *L, int idx, int n) {
StkId p, t, m; StkId p, t, m;
lua_lock(L); lua_lock(L);
t = L->top - 1; /* end of stack segment being rotated */ t = L->top.p - 1; /* end of stack segment being rotated */
p = index2stack(L, idx); /* start of segment */ p = index2stack(L, idx); /* start of segment */
api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'");
m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */
@ -264,7 +260,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
api_check(L, isvalid(L, to), "invalid index"); api_check(L, isvalid(L, to), "invalid index");
setobj(L, to, fr); setobj(L, to, fr);
if (isupvalue(toidx)) /* function upvalue? */ if (isupvalue(toidx)) /* function upvalue? */
luaC_barrier(L, clCvalue(s2v(L->ci->func)), fr); luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr);
/* LUA_REGISTRYINDEX does not need gc barrier /* LUA_REGISTRYINDEX does not need gc barrier
(collector revisits it before finishing collection) */ (collector revisits it before finishing collection) */
lua_unlock(L); lua_unlock(L);
@ -273,7 +269,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
LUA_API void lua_pushvalue (lua_State *L, int idx) { LUA_API void lua_pushvalue (lua_State *L, int idx) {
lua_lock(L); lua_lock(L);
setobj2s(L, L->top, index2value(L, idx)); setobj2s(L, L->top.p, index2value(L, idx));
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@ -342,12 +338,12 @@ LUA_API void lua_arith (lua_State *L, int op) {
api_checknelems(L, 2); /* all other operations expect two operands */ api_checknelems(L, 2); /* all other operations expect two operands */
else { /* for unary operations, add fake 2nd operand */ else { /* for unary operations, add fake 2nd operand */
api_checknelems(L, 1); api_checknelems(L, 1);
setobjs2s(L, L->top, L->top - 1); setobjs2s(L, L->top.p, L->top.p - 1);
api_incr_top(L); api_incr_top(L);
} }
/* first operand at top - 2, second at top - 1; result go to top - 2 */ /* first operand at top - 2, second at top - 1; result go to top - 2 */
luaO_arith(L, op, s2v(L->top - 2), s2v(L->top - 1), L->top - 2); luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2);
L->top--; /* remove second operand */ L->top.p--; /* remove second operand */
lua_unlock(L); lua_unlock(L);
} }
@ -373,7 +369,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) {
LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) {
size_t sz = luaO_str2num(s, s2v(L->top)); size_t sz = luaO_str2num(s, s2v(L->top.p));
if (sz != 0) if (sz != 0)
api_incr_top(L); api_incr_top(L);
return sz; return sz;
@ -500,7 +496,7 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) {
LUA_API void lua_pushnil (lua_State *L) { LUA_API void lua_pushnil (lua_State *L) {
lua_lock(L); lua_lock(L);
setnilvalue(s2v(L->top)); setnilvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@ -508,7 +504,7 @@ LUA_API void lua_pushnil (lua_State *L) {
LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
lua_lock(L); lua_lock(L);
setfltvalue(s2v(L->top), n); setfltvalue(s2v(L->top.p), n);
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@ -516,7 +512,7 @@ LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {
lua_lock(L); lua_lock(L);
setivalue(s2v(L->top), n); setivalue(s2v(L->top.p), n);
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@ -531,7 +527,7 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
TString *ts; TString *ts;
lua_lock(L); lua_lock(L);
ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
setsvalue2s(L, L->top, ts); setsvalue2s(L, L->top.p, ts);
api_incr_top(L); api_incr_top(L);
luaC_checkGC(L); luaC_checkGC(L);
lua_unlock(L); lua_unlock(L);
@ -542,11 +538,11 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
LUA_API const char *lua_pushstring (lua_State *L, const char *s) { LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
lua_lock(L); lua_lock(L);
if (s == NULL) if (s == NULL)
setnilvalue(s2v(L->top)); setnilvalue(s2v(L->top.p));
else { else {
TString *ts; TString *ts;
ts = luaS_new(L, s); ts = luaS_new(L, s);
setsvalue2s(L, L->top, ts); setsvalue2s(L, L->top.p, ts);
s = getstr(ts); /* internal copy's address */ s = getstr(ts); /* internal copy's address */
} }
api_incr_top(L); api_incr_top(L);
@ -583,7 +579,7 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
lua_lock(L); lua_lock(L);
if (n == 0) { if (n == 0) {
setfvalue(s2v(L->top), fn); setfvalue(s2v(L->top.p), fn);
api_incr_top(L); api_incr_top(L);
} }
else { else {
@ -592,13 +588,13 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
api_check(L, n <= MAXUPVAL, "upvalue index too large"); api_check(L, n <= MAXUPVAL, "upvalue index too large");
cl = luaF_newCclosure(L, n); cl = luaF_newCclosure(L, n);
cl->f = fn; cl->f = fn;
L->top -= n; L->top.p -= n;
while (n--) { while (n--) {
setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n));
/* does not need barrier because closure is white */ /* does not need barrier because closure is white */
lua_assert(iswhite(cl)); lua_assert(iswhite(cl));
} }
setclCvalue(L, s2v(L->top), cl); setclCvalue(L, s2v(L->top.p), cl);
api_incr_top(L); api_incr_top(L);
luaC_checkGC(L); luaC_checkGC(L);
} }
@ -609,9 +605,9 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
LUA_API void lua_pushboolean (lua_State *L, int b) { LUA_API void lua_pushboolean (lua_State *L, int b) {
lua_lock(L); lua_lock(L);
if (b) if (b)
setbtvalue(s2v(L->top)); setbtvalue(s2v(L->top.p));
else else
setbfvalue(s2v(L->top)); setbfvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@ -619,7 +615,7 @@ LUA_API void lua_pushboolean (lua_State *L, int b) {
LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
lua_lock(L); lua_lock(L);
setpvalue(s2v(L->top), p); setpvalue(s2v(L->top.p), p);
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@ -627,7 +623,7 @@ LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
LUA_API int lua_pushthread (lua_State *L) { LUA_API int lua_pushthread (lua_State *L) {
lua_lock(L); lua_lock(L);
setthvalue(L, s2v(L->top), L); setthvalue(L, s2v(L->top.p), L);
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
return (G(L)->mainthread == L); return (G(L)->mainthread == L);
@ -644,16 +640,16 @@ l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) {
const TValue *slot; const TValue *slot;
TString *str = luaS_new(L, k); TString *str = luaS_new(L, k);
if (luaV_fastget(L, t, str, slot, luaH_getstr)) { if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
setobj2s(L, L->top, slot); setobj2s(L, L->top.p, slot);
api_incr_top(L); api_incr_top(L);
} }
else { else {
setsvalue2s(L, L->top, str); setsvalue2s(L, L->top.p, str);
api_incr_top(L); api_incr_top(L);
luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
} }
lua_unlock(L); lua_unlock(L);
return ttype(s2v(L->top - 1)); return ttype(s2v(L->top.p - 1));
} }
@ -680,13 +676,13 @@ LUA_API int lua_gettable (lua_State *L, int idx) {
TValue *t; TValue *t;
lua_lock(L); lua_lock(L);
t = index2value(L, idx); t = index2value(L, idx);
if (luaV_fastget(L, t, s2v(L->top - 1), slot, luaH_get)) { if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) {
setobj2s(L, L->top - 1, slot); setobj2s(L, L->top.p - 1, slot);
} }
else else
luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
lua_unlock(L); lua_unlock(L);
return ttype(s2v(L->top - 1)); return ttype(s2v(L->top.p - 1));
} }
@ -702,27 +698,27 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
lua_lock(L); lua_lock(L);
t = index2value(L, idx); t = index2value(L, idx);
if (luaV_fastgeti(L, t, n, slot)) { if (luaV_fastgeti(L, t, n, slot)) {
setobj2s(L, L->top, slot); setobj2s(L, L->top.p, slot);
} }
else { else {
TValue aux; TValue aux;
setivalue(&aux, n); setivalue(&aux, n);
luaV_finishget(L, t, &aux, L->top, slot); luaV_finishget(L, t, &aux, L->top.p, slot);
} }
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
return ttype(s2v(L->top - 1)); return ttype(s2v(L->top.p - 1));
} }
l_sinline int finishrawget (lua_State *L, const TValue *val) { l_sinline int finishrawget (lua_State *L, const TValue *val) {
if (isempty(val)) /* avoid copying empty items to the stack */ if (isempty(val)) /* avoid copying empty items to the stack */
setnilvalue(s2v(L->top)); setnilvalue(s2v(L->top.p));
else else
setobj2s(L, L->top, val); setobj2s(L, L->top.p, val);
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
return ttype(s2v(L->top - 1)); return ttype(s2v(L->top.p - 1));
} }
@ -739,8 +735,8 @@ LUA_API int lua_rawget (lua_State *L, int idx) {
lua_lock(L); lua_lock(L);
api_checknelems(L, 1); api_checknelems(L, 1);
t = gettable(L, idx); t = gettable(L, idx);
val = luaH_get(t, s2v(L->top - 1)); val = luaH_get(t, s2v(L->top.p - 1));
L->top--; /* remove key */ L->top.p--; /* remove key */
return finishrawget(L, val); return finishrawget(L, val);
} }
@ -767,7 +763,7 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
Table *t; Table *t;
lua_lock(L); lua_lock(L);
t = luaH_new(L); t = luaH_new(L);
sethvalue2s(L, L->top, t); sethvalue2s(L, L->top.p, t);
api_incr_top(L); api_incr_top(L);
if (narray > 0 || nrec > 0) if (narray > 0 || nrec > 0)
luaH_resize(L, t, narray, nrec); luaH_resize(L, t, narray, nrec);
@ -794,7 +790,7 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) {
break; break;
} }
if (mt != NULL) { if (mt != NULL) {
sethvalue2s(L, L->top, mt); sethvalue2s(L, L->top.p, mt);
api_incr_top(L); api_incr_top(L);
res = 1; res = 1;
} }
@ -810,12 +806,12 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
o = index2value(L, idx); o = index2value(L, idx);
api_check(L, ttisfulluserdata(o), "full userdata expected"); api_check(L, ttisfulluserdata(o), "full userdata expected");
if (n <= 0 || n > uvalue(o)->nuvalue) { if (n <= 0 || n > uvalue(o)->nuvalue) {
setnilvalue(s2v(L->top)); setnilvalue(s2v(L->top.p));
t = LUA_TNONE; t = LUA_TNONE;
} }
else { else {
setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv); setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv);
t = ttype(s2v(L->top)); t = ttype(s2v(L->top.p));
} }
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
@ -835,14 +831,14 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
TString *str = luaS_new(L, k); TString *str = luaS_new(L, k);
api_checknelems(L, 1); api_checknelems(L, 1);
if (luaV_fastget(L, t, str, slot, luaH_getstr)) { if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
luaV_finishfastset(L, t, slot, s2v(L->top - 1)); luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
L->top--; /* pop value */ L->top.p--; /* pop value */
} }
else { else {
setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */
api_incr_top(L); api_incr_top(L);
luaV_finishset(L, t, s2v(L->top - 1), s2v(L->top - 2), slot); luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot);
L->top -= 2; /* pop value and key */ L->top.p -= 2; /* pop value and key */
} }
lua_unlock(L); /* lock done by caller */ lua_unlock(L); /* lock done by caller */
} }
@ -862,12 +858,12 @@ LUA_API void lua_settable (lua_State *L, int idx) {
lua_lock(L); lua_lock(L);
api_checknelems(L, 2); api_checknelems(L, 2);
t = index2value(L, idx); t = index2value(L, idx);
if (luaV_fastget(L, t, s2v(L->top - 2), slot, luaH_get)) { if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) {
luaV_finishfastset(L, t, slot, s2v(L->top - 1)); luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
} }
else else
luaV_finishset(L, t, s2v(L->top - 2), s2v(L->top - 1), slot); luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot);
L->top -= 2; /* pop index and value */ L->top.p -= 2; /* pop index and value */
lua_unlock(L); lua_unlock(L);
} }
@ -885,14 +881,14 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
api_checknelems(L, 1); api_checknelems(L, 1);
t = index2value(L, idx); t = index2value(L, idx);
if (luaV_fastgeti(L, t, n, slot)) { if (luaV_fastgeti(L, t, n, slot)) {
luaV_finishfastset(L, t, slot, s2v(L->top - 1)); luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
} }
else { else {
TValue aux; TValue aux;
setivalue(&aux, n); setivalue(&aux, n);
luaV_finishset(L, t, &aux, s2v(L->top - 1), slot); luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot);
} }
L->top--; /* pop value */ L->top.p--; /* pop value */
lua_unlock(L); lua_unlock(L);
} }
@ -902,16 +898,16 @@ static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
lua_lock(L); lua_lock(L);
api_checknelems(L, n); api_checknelems(L, n);
t = gettable(L, idx); t = gettable(L, idx);
luaH_set(L, t, key, s2v(L->top - 1)); luaH_set(L, t, key, s2v(L->top.p - 1));
invalidateTMcache(t); invalidateTMcache(t);
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
L->top -= n; L->top.p -= n;
lua_unlock(L); lua_unlock(L);
} }
LUA_API void lua_rawset (lua_State *L, int idx) { LUA_API void lua_rawset (lua_State *L, int idx) {
aux_rawset(L, idx, s2v(L->top - 2), 2); aux_rawset(L, idx, s2v(L->top.p - 2), 2);
} }
@ -927,9 +923,9 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
lua_lock(L); lua_lock(L);
api_checknelems(L, 1); api_checknelems(L, 1);
t = gettable(L, idx); t = gettable(L, idx);
luaH_setint(L, t, n, s2v(L->top - 1)); luaH_setint(L, t, n, s2v(L->top.p - 1));
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
L->top--; L->top.p--;
lua_unlock(L); lua_unlock(L);
} }
@ -940,11 +936,11 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
lua_lock(L); lua_lock(L);
api_checknelems(L, 1); api_checknelems(L, 1);
obj = index2value(L, objindex); obj = index2value(L, objindex);
if (ttisnil(s2v(L->top - 1))) if (ttisnil(s2v(L->top.p - 1)))
mt = NULL; mt = NULL;
else { else {
api_check(L, ttistable(s2v(L->top - 1)), "table expected"); api_check(L, ttistable(s2v(L->top.p - 1)), "table expected");
mt = hvalue(s2v(L->top - 1)); mt = hvalue(s2v(L->top.p - 1));
} }
switch (ttype(obj)) { switch (ttype(obj)) {
case LUA_TTABLE: { case LUA_TTABLE: {
@ -968,7 +964,7 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
break; break;
} }
} }
L->top--; L->top.p--;
lua_unlock(L); lua_unlock(L);
return 1; return 1;
} }
@ -984,11 +980,11 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue)))
res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */
else { else {
setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1)); setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1));
luaC_barrierback(L, gcvalue(o), s2v(L->top - 1)); luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1));
res = 1; res = 1;
} }
L->top--; L->top.p--;
lua_unlock(L); lua_unlock(L);
return res; return res;
} }
@ -1000,7 +996,8 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
#define checkresults(L,na,nr) \ #define checkresults(L,na,nr) \
api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ api_check(L, (nr) == LUA_MULTRET \
|| (L->ci->top.p - L->top.p >= (nr) - (na)), \
"results from function overflow current stack size") "results from function overflow current stack size")
@ -1013,7 +1010,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults,
api_checknelems(L, nargs+1); api_checknelems(L, nargs+1);
api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
checkresults(L, nargs, nresults); checkresults(L, nargs, nresults);
func = L->top - (nargs+1); func = L->top.p - (nargs+1);
if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ if (k != NULL && yieldable(L)) { /* need to prepare continuation? */
L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.k = k; /* save continuation */
L->ci->u.c.ctx = ctx; /* save context */ L->ci->u.c.ctx = ctx; /* save context */
@ -1061,7 +1058,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); api_check(L, ttisfunction(s2v(o)), "error handler must be a function");
func = savestack(L, o); func = savestack(L, o);
} }
c.func = L->top - (nargs+1); /* function to be called */ c.func = L->top.p - (nargs+1); /* function to be called */
if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */
c.nresults = nresults; /* do a 'conventional' protected call */ c.nresults = nresults; /* do a 'conventional' protected call */
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
@ -1096,12 +1093,12 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
luaZ_init(L, &z, reader, data); luaZ_init(L, &z, reader, data);
status = luaD_protectedparser(L, &z, chunkname, mode); status = luaD_protectedparser(L, &z, chunkname, mode);
if (status == LUA_OK) { /* no errors? */ if (status == LUA_OK) { /* no errors? */
LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */ LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */
if (f->nupvalues >= 1) { /* does it have an upvalue? */ if (f->nupvalues >= 1) { /* does it have an upvalue? */
/* get global table from registry */ /* get global table from registry */
const TValue *gt = getGtable(L); const TValue *gt = getGtable(L);
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
setobj(L, f->upvals[0]->v, gt); setobj(L, f->upvals[0]->v.p, gt);
luaC_barrier(L, f->upvals[0], gt); luaC_barrier(L, f->upvals[0], gt);
} }
} }
@ -1115,7 +1112,7 @@ LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) {
TValue *o; TValue *o;
lua_lock(L); lua_lock(L);
api_checknelems(L, 1); api_checknelems(L, 1);
o = s2v(L->top - 1); o = s2v(L->top.p - 1);
if (isLfunction(o)) if (isLfunction(o))
status = luaU_dump(L, getproto(o), writer, data, strip); status = luaU_dump(L, getproto(o), writer, data, strip);
else else
@ -1241,7 +1238,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
LUA_API int lua_error (lua_State *L) { LUA_API int lua_error (lua_State *L) {
TValue *errobj; TValue *errobj;
lua_lock(L); lua_lock(L);
errobj = s2v(L->top - 1); errobj = s2v(L->top.p - 1);
api_checknelems(L, 1); api_checknelems(L, 1);
/* error object is the memory error message? */ /* error object is the memory error message? */
if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
@ -1259,12 +1256,12 @@ LUA_API int lua_next (lua_State *L, int idx) {
lua_lock(L); lua_lock(L);
api_checknelems(L, 1); api_checknelems(L, 1);
t = gettable(L, idx); t = gettable(L, idx);
more = luaH_next(L, t, L->top - 1); more = luaH_next(L, t, L->top.p - 1);
if (more) { if (more) {
api_incr_top(L); api_incr_top(L);
} }
else /* no more elements */ else /* no more elements */
L->top -= 1; /* remove key */ L->top.p -= 1; /* remove key */
lua_unlock(L); lua_unlock(L);
return more; return more;
} }
@ -1276,7 +1273,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
lua_lock(L); lua_lock(L);
o = index2stack(L, idx); o = index2stack(L, idx);
nresults = L->ci->nresults; nresults = L->ci->nresults;
api_check(L, L->tbclist < o, "given index below or equal a marked one"); api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
if (!hastocloseCfunc(nresults)) /* function not marked yet? */ if (!hastocloseCfunc(nresults)) /* function not marked yet? */
L->ci->nresults = codeNresults(nresults); /* mark it */ L->ci->nresults = codeNresults(nresults); /* mark it */
@ -1291,7 +1288,7 @@ LUA_API void lua_concat (lua_State *L, int n) {
if (n > 0) if (n > 0)
luaV_concat(L, n); luaV_concat(L, n);
else { /* nothing to concatenate */ else { /* nothing to concatenate */
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */ setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */
api_incr_top(L); api_incr_top(L);
} }
luaC_checkGC(L); luaC_checkGC(L);
@ -1303,7 +1300,7 @@ LUA_API void lua_len (lua_State *L, int idx) {
TValue *t; TValue *t;
lua_lock(L); lua_lock(L);
t = index2value(L, idx); t = index2value(L, idx);
luaV_objlen(L, L->top, t); luaV_objlen(L, L->top.p, t);
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@ -1348,7 +1345,7 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
lua_lock(L); lua_lock(L);
api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value");
u = luaS_newudata(L, size, nuvalue); u = luaS_newudata(L, size, nuvalue);
setuvalue(L, s2v(L->top), u); setuvalue(L, s2v(L->top.p), u);
api_incr_top(L); api_incr_top(L);
luaC_checkGC(L); luaC_checkGC(L);
lua_unlock(L); lua_unlock(L);
@ -1374,7 +1371,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val,
Proto *p = f->p; Proto *p = f->p;
if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues)))
return NULL; /* 'n' not in [1, p->sizeupvalues] */ return NULL; /* 'n' not in [1, p->sizeupvalues] */
*val = f->upvals[n-1]->v; *val = f->upvals[n-1]->v.p;
if (owner) *owner = obj2gco(f->upvals[n - 1]); if (owner) *owner = obj2gco(f->upvals[n - 1]);
name = p->upvalues[n-1].name; name = p->upvalues[n-1].name;
return (name == NULL) ? "(no name)" : getstr(name); return (name == NULL) ? "(no name)" : getstr(name);
@ -1390,7 +1387,7 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
lua_lock(L); lua_lock(L);
name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); name = aux_upvalue(index2value(L, funcindex), n, &val, NULL);
if (name) { if (name) {
setobj2s(L, L->top, val); setobj2s(L, L->top.p, val);
api_incr_top(L); api_incr_top(L);
} }
lua_unlock(L); lua_unlock(L);
@ -1408,8 +1405,8 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
api_checknelems(L, 1); api_checknelems(L, 1);
name = aux_upvalue(fi, n, &val, &owner); name = aux_upvalue(fi, n, &val, &owner);
if (name) { if (name) {
L->top--; L->top.p--;
setobj(L, val, s2v(L->top)); setobj(L, val, s2v(L->top.p));
luaC_barrier(L, owner, val); luaC_barrier(L, owner, val);
} }
lua_unlock(L); lua_unlock(L);

View File

@ -12,23 +12,26 @@
#include "lstate.h" #include "lstate.h"
/* Increments 'L->top', checking for stack overflows */ /* Increments 'L->top.p', checking for stack overflows */
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ #define api_incr_top(L) {L->top.p++; \
"stack overflow");} api_check(L, L->top.p <= L->ci->top.p, \
"stack overflow");}
/* /*
** If a call returns too many multiple returns, the callee may not have ** If a call returns too many multiple returns, the callee may not have
** stack space to accommodate all results. In this case, this macro ** stack space to accommodate all results. In this case, this macro
** increases its stack space ('L->ci->top'). ** increases its stack space ('L->ci->top.p').
*/ */
#define adjustresults(L,nres) \ #define adjustresults(L,nres) \
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
L->ci->top.p = L->top.p; }
/* Ensure the stack has at least 'n' elements */ /* Ensure the stack has at least 'n' elements */
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ #define api_checknelems(L,n) \
"not enough elements in the stack") api_check(L, (n) < (L->top.p - L->ci->func.p), \
"not enough elements in the stack")
/* /*

View File

@ -526,13 +526,14 @@ static void newbox (lua_State *L) {
/* /*
** Compute new size for buffer 'B', enough to accommodate extra 'sz' ** Compute new size for buffer 'B', enough to accommodate extra 'sz'
** bytes. ** bytes. (The test for "not big enough" also gets the case when the
** computation of 'newsize' overflows.)
*/ */
static size_t newbuffsize (luaL_Buffer *B, size_t sz) { static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
size_t newsize = B->size * 2; /* double buffer size */ size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */
if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
return luaL_error(B->L, "buffer too large"); return luaL_error(B->L, "buffer too large");
if (newsize < B->n + sz) /* double is not big enough? */ if (newsize < B->n + sz) /* not big enough? */
newsize = B->n + sz; newsize = B->n + sz;
return newsize; return newsize;
} }
@ -611,7 +612,7 @@ LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) {
** box (if existent) is not on the top of the stack. So, instead of ** box (if existent) is not on the top of the stack. So, instead of
** calling 'luaL_addlstring', it replicates the code using -2 as the ** calling 'luaL_addlstring', it replicates the code using -2 as the
** last argument to 'prepbuffsize', signaling that the box is (or will ** last argument to 'prepbuffsize', signaling that the box is (or will
** be) bellow the string being added to the buffer. (Box creation can ** be) below the string being added to the buffer. (Box creation can
** trigger an emergency GC, so we should not remove the string from the ** trigger an emergency GC, so we should not remove the string from the
** stack before we have the space guaranteed.) ** stack before we have the space guaranteed.)
*/ */
@ -739,17 +740,18 @@ static int errfile (lua_State *L, const char *what, int fnameindex) {
} }
static int skipBOM (LoadF *lf) { /*
const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ ** Skip an optional BOM at the start of a stream. If there is an
int c; ** incomplete BOM (the first character is correct but the rest is
lf->n = 0; ** not), returns the first character anyway to force an error
do { ** (as no chunk can start with 0xEF).
c = getc(lf->f); */
if (c == EOF || c != *(const unsigned char *)p++) return c; static int skipBOM (FILE *f) {
lf->buff[lf->n++] = c; /* to be read by the parser */ int c = getc(f); /* read first character */
} while (*p != '\0'); if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */
lf->n = 0; /* prefix matched; discard it */ return getc(f); /* ignore BOM and return next char */
return getc(lf->f); /* return next character */ else /* no (valid) BOM */
return c; /* return first character */
} }
@ -760,13 +762,13 @@ static int skipBOM (LoadF *lf) {
** first "valid" character of the file (after the optional BOM and ** first "valid" character of the file (after the optional BOM and
** a first-line comment). ** a first-line comment).
*/ */
static int skipcomment (LoadF *lf, int *cp) { static int skipcomment (FILE *f, int *cp) {
int c = *cp = skipBOM(lf); int c = *cp = skipBOM(f);
if (c == '#') { /* first line is a comment (Unix exec. file)? */ if (c == '#') { /* first line is a comment (Unix exec. file)? */
do { /* skip first line */ do { /* skip first line */
c = getc(lf->f); c = getc(f);
} while (c != EOF && c != '\n'); } while (c != EOF && c != '\n');
*cp = getc(lf->f); /* skip end-of-line, if present */ *cp = getc(f); /* next character after comment, if present */
return 1; /* there was a comment */ return 1; /* there was a comment */
} }
else return 0; /* no comment */ else return 0; /* no comment */
@ -788,12 +790,16 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
lf.f = fopen(filename, "r"); lf.f = fopen(filename, "r");
if (lf.f == NULL) return errfile(L, "open", fnameindex); if (lf.f == NULL) return errfile(L, "open", fnameindex);
} }
if (skipcomment(&lf, &c)) /* read initial portion */ lf.n = 0;
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ if (skipcomment(lf.f, &c)) /* read initial portion */
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (c == LUA_SIGNATURE[0]) { /* binary file? */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex); lf.n = 0; /* remove possible newline */
skipcomment(&lf, &c); /* re-read initial portion */ if (filename) { /* "real" file? */
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
skipcomment(lf.f, &c); /* re-read initial portion */
}
} }
if (c != EOF) if (c != EOF)
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */

View File

@ -1351,6 +1351,35 @@ static int constfolding (FuncState *fs, int op, expdesc *e1,
} }
/*
** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP)
*/
l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) {
lua_assert(baser <= opr &&
((baser == OPR_ADD && opr <= OPR_SHR) ||
(baser == OPR_LT && opr <= OPR_LE)));
return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base));
}
/*
** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP)
*/
l_sinline OpCode unopr2op (UnOpr opr) {
return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) +
cast_int(OP_UNM));
}
/*
** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM)
*/
l_sinline TMS binopr2TM (BinOpr opr) {
lua_assert(OPR_ADD <= opr && opr <= OPR_SHR);
return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD));
}
/* /*
** Emit code for unary expressions that "produce values" ** Emit code for unary expressions that "produce values"
** (everything but 'not'). ** (everything but 'not').
@ -1389,12 +1418,15 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
** Emit code for binary expressions that "produce values" over ** Emit code for binary expressions that "produce values" over
** two registers. ** two registers.
*/ */
static void codebinexpval (FuncState *fs, OpCode op, static void codebinexpval (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int line) { expdesc *e1, expdesc *e2, int line) {
int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */ OpCode op = binopr2op(opr, OPR_ADD, OP_ADD);
int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */
/* 'e1' must be already in a register or it is a constant */
lua_assert((VNIL <= e1->k && e1->k <= VKSTR) ||
e1->k == VNONRELOC || e1->k == VRELOC);
lua_assert(OP_ADD <= op && op <= OP_SHR); lua_assert(OP_ADD <= op && op <= OP_SHR);
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr));
cast(TMS, (op - OP_ADD) + TM_ADD));
} }
@ -1410,6 +1442,18 @@ static void codebini (FuncState *fs, OpCode op,
} }
/*
** Code binary operators with K operand.
*/
static void codebinK (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int flip, int line) {
TMS event = binopr2TM(opr);
int v2 = e2->u.info; /* K index */
OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK);
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
}
/* Try to code a binary operator negating its second operand. /* Try to code a binary operator negating its second operand.
** For the metamethod, 2nd operand must keep its original value. ** For the metamethod, 2nd operand must keep its original value.
*/ */
@ -1437,24 +1481,27 @@ static void swapexps (expdesc *e1, expdesc *e2) {
} }
/*
** Code binary operators with no constant operand.
*/
static void codebinNoK (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int flip, int line) {
if (flip)
swapexps(e1, e2); /* back to original order */
codebinexpval(fs, opr, e1, e2, line); /* use standard operators */
}
/* /*
** Code arithmetic operators ('+', '-', ...). If second operand is a ** Code arithmetic operators ('+', '-', ...). If second operand is a
** constant in the proper range, use variant opcodes with K operands. ** constant in the proper range, use variant opcodes with K operands.
*/ */
static void codearith (FuncState *fs, BinOpr opr, static void codearith (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int flip, int line) { expdesc *e1, expdesc *e2, int flip, int line) {
TMS event = cast(TMS, opr + TM_ADD); if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ codebinK(fs, opr, e1, e2, flip, line);
int v2 = e2->u.info; /* K index */ else /* 'e2' is neither an immediate nor a K operand */
OpCode op = cast(OpCode, opr + OP_ADDK); codebinNoK(fs, opr, e1, e2, flip, line);
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
}
else { /* 'e2' is neither an immediate nor a K operand */
OpCode op = cast(OpCode, opr + OP_ADD);
if (flip)
swapexps(e1, e2); /* back to original order */
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
}
} }
@ -1471,35 +1518,27 @@ static void codecommutative (FuncState *fs, BinOpr op,
flip = 1; flip = 1;
} }
if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */ if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD); codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD);
else else
codearith(fs, op, e1, e2, flip, line); codearith(fs, op, e1, e2, flip, line);
} }
/* /*
** Code bitwise operations; they are all associative, so the function ** Code bitwise operations; they are all commutative, so the function
** tries to put an integer constant as the 2nd operand (a K operand). ** tries to put an integer constant as the 2nd operand (a K operand).
*/ */
static void codebitwise (FuncState *fs, BinOpr opr, static void codebitwise (FuncState *fs, BinOpr opr,
expdesc *e1, expdesc *e2, int line) { expdesc *e1, expdesc *e2, int line) {
int flip = 0; int flip = 0;
int v2; if (e1->k == VKINT) {
OpCode op;
if (e1->k == VKINT && luaK_exp2RK(fs, e1)) {
swapexps(e1, e2); /* 'e2' will be the constant operand */ swapexps(e1, e2); /* 'e2' will be the constant operand */
flip = 1; flip = 1;
} }
else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */ if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */
op = cast(OpCode, opr + OP_ADD); codebinK(fs, opr, e1, e2, flip, line);
codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */ else /* no constants */
return; codebinNoK(fs, opr, e1, e2, flip, line);
}
v2 = e2->u.info; /* index in K array */
op = cast(OpCode, opr + OP_ADDK);
lua_assert(ttisinteger(&fs->f->k[v2]));
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK,
cast(TMS, opr + TM_ADD));
} }
@ -1507,25 +1546,27 @@ static void codebitwise (FuncState *fs, BinOpr opr,
** Emit code for order comparisons. When using an immediate operand, ** Emit code for order comparisons. When using an immediate operand,
** 'isfloat' tells whether the original value was a float. ** 'isfloat' tells whether the original value was a float.
*/ */
static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
int r1, r2; int r1, r2;
int im; int im;
int isfloat = 0; int isfloat = 0;
OpCode op;
if (isSCnumber(e2, &im, &isfloat)) { if (isSCnumber(e2, &im, &isfloat)) {
/* use immediate operand */ /* use immediate operand */
r1 = luaK_exp2anyreg(fs, e1); r1 = luaK_exp2anyreg(fs, e1);
r2 = im; r2 = im;
op = cast(OpCode, (op - OP_LT) + OP_LTI); op = binopr2op(opr, OPR_LT, OP_LTI);
} }
else if (isSCnumber(e1, &im, &isfloat)) { else if (isSCnumber(e1, &im, &isfloat)) {
/* transform (A < B) to (B > A) and (A <= B) to (B >= A) */ /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
r1 = luaK_exp2anyreg(fs, e2); r1 = luaK_exp2anyreg(fs, e2);
r2 = im; r2 = im;
op = (op == OP_LT) ? OP_GTI : OP_GEI; op = binopr2op(opr, OPR_LT, OP_GTI);
} }
else { /* regular case, compare two registers */ else { /* regular case, compare two registers */
r1 = luaK_exp2anyreg(fs, e1); r1 = luaK_exp2anyreg(fs, e1);
r2 = luaK_exp2anyreg(fs, e2); r2 = luaK_exp2anyreg(fs, e2);
op = binopr2op(opr, OPR_LT, OP_LT);
} }
freeexps(fs, e1, e2); freeexps(fs, e1, e2);
e1->u.info = condjump(fs, op, r1, r2, isfloat, 1); e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
@ -1551,7 +1592,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
op = OP_EQI; op = OP_EQI;
r2 = im; /* immediate operand */ r2 = im; /* immediate operand */
} }
else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */ else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */
op = OP_EQK; op = OP_EQK;
r2 = e2->u.info; /* constant index */ r2 = e2->u.info; /* constant index */
} }
@ -1568,16 +1609,16 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
/* /*
** Apply prefix operation 'op' to expression 'e'. ** Apply prefix operation 'op' to expression 'e'.
*/ */
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) {
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
luaK_dischargevars(fs, e); luaK_dischargevars(fs, e);
switch (op) { switch (opr) {
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
if (constfolding(fs, op + LUA_OPUNM, e, &ef)) if (constfolding(fs, opr + LUA_OPUNM, e, &ef))
break; break;
/* else */ /* FALLTHROUGH */ /* else */ /* FALLTHROUGH */
case OPR_LEN: case OPR_LEN:
codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line); codeunexpval(fs, unopr2op(opr), e, line);
break; break;
case OPR_NOT: codenot(fs, e); break; case OPR_NOT: codenot(fs, e); break;
default: lua_assert(0); default: lua_assert(0);
@ -1611,7 +1652,8 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
case OPR_SHL: case OPR_SHR: { case OPR_SHL: case OPR_SHR: {
if (!tonumeral(v, NULL)) if (!tonumeral(v, NULL))
luaK_exp2anyreg(fs, v); luaK_exp2anyreg(fs, v);
/* else keep numeral, which may be folded with 2nd operand */ /* else keep numeral, which may be folded or used as an immediate
operand */
break; break;
} }
case OPR_EQ: case OPR_NE: { case OPR_EQ: case OPR_NE: {
@ -1706,30 +1748,27 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
/* coded as (r1 >> -I) */; /* coded as (r1 >> -I) */;
} }
else /* regular case (two registers) */ else /* regular case (two registers) */
codebinexpval(fs, OP_SHL, e1, e2, line); codebinexpval(fs, opr, e1, e2, line);
break; break;
} }
case OPR_SHR: { case OPR_SHR: {
if (isSCint(e2)) if (isSCint(e2))
codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */ codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */
else /* regular case (two registers) */ else /* regular case (two registers) */
codebinexpval(fs, OP_SHR, e1, e2, line); codebinexpval(fs, opr, e1, e2, line);
break; break;
} }
case OPR_EQ: case OPR_NE: { case OPR_EQ: case OPR_NE: {
codeeq(fs, opr, e1, e2); codeeq(fs, opr, e1, e2);
break; break;
} }
case OPR_LT: case OPR_LE: {
OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ);
codeorder(fs, op, e1, e2);
break;
}
case OPR_GT: case OPR_GE: { case OPR_GT: case OPR_GE: {
/* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */ /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */
OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ);
swapexps(e1, e2); swapexps(e1, e2);
codeorder(fs, op, e1, e2); opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT);
} /* FALLTHROUGH */
case OPR_LT: case OPR_LE: {
codeorder(fs, opr, e1, e2);
break; break;
} }
default: lua_assert(0); default: lua_assert(0);

View File

@ -76,7 +76,7 @@ static int luaB_auxwrap (lua_State *L) {
if (l_unlikely(r < 0)) { /* error? */ if (l_unlikely(r < 0)) { /* error? */
int stat = lua_status(co); int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
stat = lua_resetthread(co); /* close its tbc variables */ stat = lua_closethread(co, L); /* close its tbc variables */
lua_assert(stat != LUA_OK); lua_assert(stat != LUA_OK);
lua_xmove(co, L, 1); /* move error message to the caller */ lua_xmove(co, L, 1); /* move error message to the caller */
} }
@ -172,7 +172,7 @@ static int luaB_close (lua_State *L) {
int status = auxstatus(L, co); int status = auxstatus(L, co);
switch (status) { switch (status) {
case COS_DEAD: case COS_YIELD: { case COS_DEAD: case COS_YIELD: {
status = lua_resetthread(co); status = lua_closethread(co, L);
if (status == LUA_OK) { if (status == LUA_OK) {
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;

View File

@ -182,10 +182,10 @@ static const char *upvalname (const Proto *p, int uv) {
static const char *findvararg (CallInfo *ci, int n, StkId *pos) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
if (clLvalue(s2v(ci->func))->p->is_vararg) { if (clLvalue(s2v(ci->func.p))->p->is_vararg) {
int nextra = ci->u.l.nextraargs; int nextra = ci->u.l.nextraargs;
if (n >= -nextra) { /* 'n' is negative */ if (n >= -nextra) { /* 'n' is negative */
*pos = ci->func - nextra - (n + 1); *pos = ci->func.p - nextra - (n + 1);
return "(vararg)"; /* generic name for any vararg */ return "(vararg)"; /* generic name for any vararg */
} }
} }
@ -194,7 +194,7 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
StkId base = ci->func + 1; StkId base = ci->func.p + 1;
const char *name = NULL; const char *name = NULL;
if (isLua(ci)) { if (isLua(ci)) {
if (n < 0) /* access to vararg values? */ if (n < 0) /* access to vararg values? */
@ -203,7 +203,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
} }
if (name == NULL) { /* no 'standard' name? */ if (name == NULL) { /* no 'standard' name? */
StkId limit = (ci == L->ci) ? L->top : ci->next->func; StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p;
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
/* generic name for any valid slot */ /* generic name for any valid slot */
name = isLua(ci) ? "(temporary)" : "(C temporary)"; name = isLua(ci) ? "(temporary)" : "(C temporary)";
@ -221,16 +221,16 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
const char *name; const char *name;
lua_lock(L); lua_lock(L);
if (ar == NULL) { /* information about non-active function? */ if (ar == NULL) { /* information about non-active function? */
if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */ if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */
name = NULL; name = NULL;
else /* consider live variables at function start (parameters) */ else /* consider live variables at function start (parameters) */
name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0); name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0);
} }
else { /* active function; get information through 'ar' */ else { /* active function; get information through 'ar' */
StkId pos = NULL; /* to avoid warnings */ StkId pos = NULL; /* to avoid warnings */
name = luaG_findlocal(L, ar->i_ci, n, &pos); name = luaG_findlocal(L, ar->i_ci, n, &pos);
if (name) { if (name) {
setobjs2s(L, L->top, pos); setobjs2s(L, L->top.p, pos);
api_incr_top(L); api_incr_top(L);
} }
} }
@ -245,8 +245,8 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
lua_lock(L); lua_lock(L);
name = luaG_findlocal(L, ar->i_ci, n, &pos); name = luaG_findlocal(L, ar->i_ci, n, &pos);
if (name) { if (name) {
setobjs2s(L, pos, L->top - 1); setobjs2s(L, pos, L->top.p - 1);
L->top--; /* pop value */ L->top.p--; /* pop value */
} }
lua_unlock(L); lua_unlock(L);
return name; return name;
@ -289,7 +289,7 @@ static int nextline (const Proto *p, int currentline, int pc) {
static void collectvalidlines (lua_State *L, Closure *f) { static void collectvalidlines (lua_State *L, Closure *f) {
if (noLuaClosure(f)) { if (noLuaClosure(f)) {
setnilvalue(s2v(L->top)); setnilvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
} }
else { else {
@ -298,7 +298,7 @@ static void collectvalidlines (lua_State *L, Closure *f) {
const Proto *p = f->l.p; const Proto *p = f->l.p;
int currentline = p->linedefined; int currentline = p->linedefined;
Table *t = luaH_new(L); /* new table to store active lines */ Table *t = luaH_new(L); /* new table to store active lines */
sethvalue2s(L, L->top, t); /* push it on stack */ sethvalue2s(L, L->top.p, t); /* push it on stack */
api_incr_top(L); api_incr_top(L);
setbtvalue(&v); /* boolean 'true' to be the value of all indices */ setbtvalue(&v); /* boolean 'true' to be the value of all indices */
if (!p->is_vararg) /* regular function? */ if (!p->is_vararg) /* regular function? */
@ -388,20 +388,20 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
lua_lock(L); lua_lock(L);
if (*what == '>') { if (*what == '>') {
ci = NULL; ci = NULL;
func = s2v(L->top - 1); func = s2v(L->top.p - 1);
api_check(L, ttisfunction(func), "function expected"); api_check(L, ttisfunction(func), "function expected");
what++; /* skip the '>' */ what++; /* skip the '>' */
L->top--; /* pop function */ L->top.p--; /* pop function */
} }
else { else {
ci = ar->i_ci; ci = ar->i_ci;
func = s2v(ci->func); func = s2v(ci->func.p);
lua_assert(ttisfunction(func)); lua_assert(ttisfunction(func));
} }
cl = ttisclosure(func) ? clvalue(func) : NULL; cl = ttisclosure(func) ? clvalue(func) : NULL;
status = auxgetinfo(L, what, ar, cl, ci); status = auxgetinfo(L, what, ar, cl, ci);
if (strchr(what, 'f')) { if (strchr(what, 'f')) {
setobj2s(L, L->top, func); setobj2s(L, L->top.p, func);
api_incr_top(L); api_incr_top(L);
} }
if (strchr(what, 'L')) if (strchr(what, 'L'))
@ -656,18 +656,19 @@ static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
/* /*
** Check whether pointer 'o' points to some value in the stack ** Check whether pointer 'o' points to some value in the stack frame of
** frame of the current function. Because 'o' may not point to a ** the current function and, if so, returns its index. Because 'o' may
** value in this stack, we cannot compare it with the region ** not point to a value in this stack, we cannot compare it with the
** boundaries (undefined behaviour in ISO C). ** region boundaries (undefined behavior in ISO C).
*/ */
static int isinstack (CallInfo *ci, const TValue *o) { static int instack (CallInfo *ci, const TValue *o) {
StkId pos; int pos;
for (pos = ci->func + 1; pos < ci->top; pos++) { StkId base = ci->func.p + 1;
if (o == s2v(pos)) for (pos = 0; base + pos < ci->top.p; pos++) {
return 1; if (o == s2v(base + pos))
return pos;
} }
return 0; /* not found */ return -1; /* not found */
} }
@ -681,7 +682,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o,
LClosure *c = ci_func(ci); LClosure *c = ci_func(ci);
int i; int i;
for (i = 0; i < c->nupvalues; i++) { for (i = 0; i < c->nupvalues; i++) {
if (c->upvals[i]->v == o) { if (c->upvals[i]->v.p == o) {
*name = upvalname(c->p, i); *name = upvalname(c->p, i);
return "upvalue"; return "upvalue";
} }
@ -708,9 +709,11 @@ static const char *varinfo (lua_State *L, const TValue *o) {
const char *kind = NULL; const char *kind = NULL;
if (isLua(ci)) { if (isLua(ci)) {
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
if (!kind && isinstack(ci, o)) /* no? try a register */ if (!kind) { /* not an upvalue? */
kind = getobjname(ci_func(ci)->p, currentpc(ci), int reg = instack(ci, o); /* try a register */
cast_int(cast(StkId, o) - (ci->func + 1)), &name); if (reg >= 0) /* is 'o' a register? */
kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
}
} }
return formatvarinfo(L, kind, name); return formatvarinfo(L, kind, name);
} }
@ -807,10 +810,10 @@ l_noret luaG_errormsg (lua_State *L) {
if (L->errfunc != 0) { /* is there an error handling function? */ if (L->errfunc != 0) { /* is there an error handling function? */
StkId errfunc = restorestack(L, L->errfunc); StkId errfunc = restorestack(L, L->errfunc);
lua_assert(ttisfunction(s2v(errfunc))); lua_assert(ttisfunction(s2v(errfunc)));
setobjs2s(L, L->top, L->top - 1); /* move argument */ setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */
setobjs2s(L, L->top - 1, errfunc); /* push function */ setobjs2s(L, L->top.p - 1, errfunc); /* push function */
L->top++; /* assume EXTRA_STACK */ L->top.p++; /* assume EXTRA_STACK */
luaD_callnoyield(L, L->top - 2, 1); /* call it */ luaD_callnoyield(L, L->top.p - 2, 1); /* call it */
} }
luaD_throw(L, LUA_ERRRUN); luaD_throw(L, LUA_ERRRUN);
} }
@ -824,8 +827,11 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
va_start(argp, fmt); va_start(argp, fmt);
msg = luaO_pushvfstring(L, fmt, argp); /* format message */ msg = luaO_pushvfstring(L, fmt, argp); /* format message */
va_end(argp); va_end(argp);
if (isLua(ci)) /* if Lua function, add source:line information */ if (isLua(ci)) { /* if Lua function, add source:line information */
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */
L->top.p--;
}
luaG_errormsg(L); luaG_errormsg(L);
} }
@ -842,7 +848,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
if (p->lineinfo == NULL) /* no debug information? */ if (p->lineinfo == NULL) /* no debug information? */
return 0; return 0;
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */ if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
int delta = 0; /* line diference */ int delta = 0; /* line difference */
int pc = oldpc; int pc = oldpc;
for (;;) { for (;;) {
int lineinfo = p->lineinfo[++pc]; int lineinfo = p->lineinfo[++pc];
@ -869,7 +875,7 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc' ** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
** at most causes an extra call to a line hook.) ** at most causes an extra call to a line hook.)
** This function is not "Protected" when called, so it should correct ** This function is not "Protected" when called, so it should correct
** 'L->top' before calling anything that can run the GC. ** 'L->top.p' before calling anything that can run the GC.
*/ */
int luaG_traceexec (lua_State *L, const Instruction *pc) { int luaG_traceexec (lua_State *L, const Instruction *pc) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
@ -892,7 +898,7 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
return 1; /* do not call hook again (VM yielded, so it did not move) */ return 1; /* do not call hook again (VM yielded, so it did not move) */
} }
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
L->top = ci->top; /* correct top */ L->top.p = ci->top.p; /* correct top */
if (counthook) if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
if (mask & LUA_MASKLINE) { if (mask & LUA_MASKLINE) {

View File

@ -15,7 +15,7 @@
/* Active Lua function (given call info) */ /* Active Lua function (given call info) */
#define ci_func(ci) (clLvalue(s2v((ci)->func))) #define ci_func(ci) (clLvalue(s2v((ci)->func.p)))
#define resethookcount(L) (L->hookcount = L->basehookcount) #define resethookcount(L) (L->hookcount = L->basehookcount)

View File

@ -104,11 +104,11 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
} }
default: { default: {
lua_assert(errorstatus(errcode)); /* real error */ lua_assert(errorstatus(errcode)); /* real error */
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */
break; break;
} }
} }
L->top = oldtop + 1; L->top.p = oldtop + 1;
} }
@ -121,7 +121,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
global_State *g = G(L); global_State *g = G(L);
errcode = luaE_resetthread(L, errcode); /* close all upvalues */ errcode = luaE_resetthread(L, errcode); /* close all upvalues */
if (g->mainthread->errorJmp) { /* main thread has a handler? */ if (g->mainthread->errorJmp) { /* main thread has a handler? */
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */ setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
} }
else { /* no handler at all; abort */ else { /* no handler at all; abort */
@ -157,16 +157,38 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
** Stack reallocation ** Stack reallocation
** =================================================================== ** ===================================================================
*/ */
static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
/*
** Change all pointers to the stack into offsets.
*/
static void relstack (lua_State *L) {
CallInfo *ci; CallInfo *ci;
UpVal *up; UpVal *up;
L->top = (L->top - oldstack) + newstack; L->top.offset = savestack(L, L->top.p);
L->tbclist = (L->tbclist - oldstack) + newstack; L->tbclist.offset = savestack(L, L->tbclist.p);
for (up = L->openupval; up != NULL; up = up->u.open.next) for (up = L->openupval; up != NULL; up = up->u.open.next)
up->v = s2v((uplevel(up) - oldstack) + newstack); up->v.offset = savestack(L, uplevel(up));
for (ci = L->ci; ci != NULL; ci = ci->previous) { for (ci = L->ci; ci != NULL; ci = ci->previous) {
ci->top = (ci->top - oldstack) + newstack; ci->top.offset = savestack(L, ci->top.p);
ci->func = (ci->func - oldstack) + newstack; ci->func.offset = savestack(L, ci->func.p);
}
}
/*
** Change back all offsets into pointers.
*/
static void correctstack (lua_State *L) {
CallInfo *ci;
UpVal *up;
L->top.p = restorestack(L, L->top.offset);
L->tbclist.p = restorestack(L, L->tbclist.offset);
for (up = L->openupval; up != NULL; up = up->u.open.next)
up->v.p = s2v(restorestack(L, up->v.offset));
for (ci = L->ci; ci != NULL; ci = ci->previous) {
ci->top.p = restorestack(L, ci->top.offset);
ci->func.p = restorestack(L, ci->func.offset);
if (isLua(ci)) if (isLua(ci))
ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */
} }
@ -176,44 +198,45 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
/* some space for error handling */ /* some space for error handling */
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
/* /*
** Reallocate the stack to a new size, correcting all pointers into ** Reallocate the stack to a new size, correcting all pointers into it.
** it. (There are pointers to a stack from its upvalues, from its list ** In ISO C, any pointer use after the pointer has been deallocated is
** of call infos, plus a few individual pointers.) The reallocation is ** undefined behavior. So, before the reallocation, all pointers are
** done in two steps (allocation + free) because the correction must be ** changed to offsets, and after the reallocation they are changed back
** done while both addresses (the old stack and the new one) are valid. ** to pointers. As during the reallocation the pointers are invalid, the
** (In ISO C, any pointer use after the pointer has been deallocated is ** reallocation cannot run emergency collections.
** undefined behavior.) **
** In case of allocation error, raise an error or return false according ** In case of allocation error, raise an error or return false according
** to 'raiseerror'. ** to 'raiseerror'.
*/ */
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
int oldsize = stacksize(L); int oldsize = stacksize(L);
int i; int i;
StkId newstack = luaM_reallocvector(L, NULL, 0, StkId newstack;
newsize + EXTRA_STACK, StackValue); int oldgcstop = G(L)->gcstopem;
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
relstack(L); /* change pointers to offsets */
G(L)->gcstopem = 1; /* stop emergency collection */
newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK,
newsize + EXTRA_STACK, StackValue);
G(L)->gcstopem = oldgcstop; /* restore emergency collection */
if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ if (l_unlikely(newstack == NULL)) { /* reallocation failed? */
correctstack(L); /* change offsets back to pointers */
if (raiseerror) if (raiseerror)
luaM_error(L); luaM_error(L);
else return 0; /* do not raise an error */ else return 0; /* do not raise an error */
} }
/* number of elements to be copied to the new stack */ L->stack.p = newstack;
i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK; correctstack(L); /* change offsets back to pointers */
memcpy(newstack, L->stack, i * sizeof(StackValue)); L->stack_last.p = L->stack.p + newsize;
for (; i < newsize + EXTRA_STACK; i++) for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++)
setnilvalue(s2v(newstack + i)); /* erase new segment */ setnilvalue(s2v(newstack + i)); /* erase new segment */
correctstack(L, L->stack, newstack);
luaM_freearray(L, L->stack, oldsize + EXTRA_STACK);
L->stack = newstack;
L->stack_last = L->stack + newsize;
return 1; return 1;
} }
/* /*
** Try to grow the stack by at least 'n' elements. when 'raiseerror' ** Try to grow the stack by at least 'n' elements. When 'raiseerror'
** is true, raises any error; otherwise, return 0 in case of errors. ** is true, raises any error; otherwise, return 0 in case of errors.
*/ */
int luaD_growstack (lua_State *L, int n, int raiseerror) { int luaD_growstack (lua_State *L, int n, int raiseerror) {
@ -227,35 +250,38 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) {
luaD_throw(L, LUA_ERRERR); /* error inside message handler */ luaD_throw(L, LUA_ERRERR); /* error inside message handler */
return 0; /* if not 'raiseerror', just signal it */ return 0; /* if not 'raiseerror', just signal it */
} }
else { else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */
int newsize = 2 * size; /* tentative new size */ int newsize = 2 * size; /* tentative new size */
int needed = cast_int(L->top - L->stack) + n; int needed = cast_int(L->top.p - L->stack.p) + n;
if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
newsize = LUAI_MAXSTACK; newsize = LUAI_MAXSTACK;
if (newsize < needed) /* but must respect what was asked for */ if (newsize < needed) /* but must respect what was asked for */
newsize = needed; newsize = needed;
if (l_likely(newsize <= LUAI_MAXSTACK)) if (l_likely(newsize <= LUAI_MAXSTACK))
return luaD_reallocstack(L, newsize, raiseerror); return luaD_reallocstack(L, newsize, raiseerror);
else { /* stack overflow */
/* add extra size to be able to handle the error message */
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
if (raiseerror)
luaG_runerror(L, "stack overflow");
return 0;
}
} }
/* else stack overflow */
/* add extra size to be able to handle the error message */
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
if (raiseerror)
luaG_runerror(L, "stack overflow");
return 0;
} }
/*
** Compute how much of the stack is being used, by computing the
** maximum top of all call frames in the stack and the current top.
*/
static int stackinuse (lua_State *L) { static int stackinuse (lua_State *L) {
CallInfo *ci; CallInfo *ci;
int res; int res;
StkId lim = L->top; StkId lim = L->top.p;
for (ci = L->ci; ci != NULL; ci = ci->previous) { for (ci = L->ci; ci != NULL; ci = ci->previous) {
if (lim < ci->top) lim = ci->top; if (lim < ci->top.p) lim = ci->top.p;
} }
lua_assert(lim <= L->stack_last); lua_assert(lim <= L->stack_last.p + EXTRA_STACK);
res = cast_int(lim - L->stack) + 1; /* part of stack in use */ res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */
if (res < LUA_MINSTACK) if (res < LUA_MINSTACK)
res = LUA_MINSTACK; /* ensure a minimum size */ res = LUA_MINSTACK; /* ensure a minimum size */
return res; return res;
@ -273,17 +299,13 @@ static int stackinuse (lua_State *L) {
*/ */
void luaD_shrinkstack (lua_State *L) { void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(L); int inuse = stackinuse(L);
int nsize = inuse * 2; /* proposed new size */ int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3;
int max = inuse * 3; /* maximum "reasonable" size */
if (max > LUAI_MAXSTACK) {
max = LUAI_MAXSTACK; /* respect stack limit */
if (nsize > LUAI_MAXSTACK)
nsize = LUAI_MAXSTACK;
}
/* if thread is currently not handling a stack overflow and its /* if thread is currently not handling a stack overflow and its
size is larger than maximum "reasonable" size, shrink it */ size is larger than maximum "reasonable" size, shrink it */
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) {
int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2;
luaD_reallocstack(L, nsize, 0); /* ok if that fails */ luaD_reallocstack(L, nsize, 0); /* ok if that fails */
}
else /* don't change stack */ else /* don't change stack */
condmovestack(L,{},{}); /* (change only for debugging) */ condmovestack(L,{},{}); /* (change only for debugging) */
luaE_shrinkCI(L); /* shrink CI list */ luaE_shrinkCI(L); /* shrink CI list */
@ -292,7 +314,7 @@ void luaD_shrinkstack (lua_State *L) {
void luaD_inctop (lua_State *L) { void luaD_inctop (lua_State *L) {
luaD_checkstack(L, 1); luaD_checkstack(L, 1);
L->top++; L->top.p++;
} }
/* }================================================================== */ /* }================================================================== */
@ -309,8 +331,8 @@ void luaD_hook (lua_State *L, int event, int line,
if (hook && L->allowhook) { /* make sure there is a hook */ if (hook && L->allowhook) { /* make sure there is a hook */
int mask = CIST_HOOKED; int mask = CIST_HOOKED;
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */ ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */
ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */ ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */
lua_Debug ar; lua_Debug ar;
ar.event = event; ar.event = event;
ar.currentline = line; ar.currentline = line;
@ -320,11 +342,11 @@ void luaD_hook (lua_State *L, int event, int line,
ci->u2.transferinfo.ftransfer = ftransfer; ci->u2.transferinfo.ftransfer = ftransfer;
ci->u2.transferinfo.ntransfer = ntransfer; ci->u2.transferinfo.ntransfer = ntransfer;
} }
if (isLua(ci) && L->top < ci->top) if (isLua(ci) && L->top.p < ci->top.p)
L->top = ci->top; /* protect entire activation register */ L->top.p = ci->top.p; /* protect entire activation register */
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
if (ci->top < L->top + LUA_MINSTACK) if (ci->top.p < L->top.p + LUA_MINSTACK)
ci->top = L->top + LUA_MINSTACK; ci->top.p = L->top.p + LUA_MINSTACK;
L->allowhook = 0; /* cannot call hooks inside a hook */ L->allowhook = 0; /* cannot call hooks inside a hook */
ci->callstatus |= mask; ci->callstatus |= mask;
lua_unlock(L); lua_unlock(L);
@ -332,8 +354,8 @@ void luaD_hook (lua_State *L, int event, int line,
lua_lock(L); lua_lock(L);
lua_assert(!L->allowhook); lua_assert(!L->allowhook);
L->allowhook = 1; L->allowhook = 1;
ci->top = restorestack(L, ci_top); ci->top.p = restorestack(L, ci_top);
L->top = restorestack(L, top); L->top.p = restorestack(L, top);
ci->callstatus &= ~mask; ci->callstatus &= ~mask;
} }
} }
@ -364,7 +386,7 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) {
*/ */
static void rethook (lua_State *L, CallInfo *ci, int nres) { static void rethook (lua_State *L, CallInfo *ci, int nres) {
if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
StkId firstres = L->top - nres; /* index of first result */ StkId firstres = L->top.p - nres; /* index of first result */
int delta = 0; /* correction for vararg functions */ int delta = 0; /* correction for vararg functions */
int ftransfer; int ftransfer;
if (isLua(ci)) { if (isLua(ci)) {
@ -372,10 +394,10 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) {
if (p->is_vararg) if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1; delta = ci->u.l.nextraargs + p->numparams + 1;
} }
ci->func += delta; /* if vararg, back to virtual 'func' */ ci->func.p += delta; /* if vararg, back to virtual 'func' */
ftransfer = cast(unsigned short, firstres - ci->func); ftransfer = cast(unsigned short, firstres - ci->func.p);
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
ci->func -= delta; ci->func.p -= delta;
} }
if (isLua(ci = ci->previous)) if (isLua(ci = ci->previous))
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */ L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */
@ -394,9 +416,9 @@ StkId luaD_tryfuncTM (lua_State *L, StkId func) {
tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */ tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */
if (l_unlikely(ttisnil(tm))) if (l_unlikely(ttisnil(tm)))
luaG_callerror(L, s2v(func)); /* nothing to call */ luaG_callerror(L, s2v(func)); /* nothing to call */
for (p = L->top; p > func; p--) /* open space for metamethod */ for (p = L->top.p; p > func; p--) /* open space for metamethod */
setobjs2s(L, p, p-1); setobjs2s(L, p, p-1);
L->top++; /* stack space pre-allocated by the caller */ L->top.p++; /* stack space pre-allocated by the caller */
setobj2s(L, func, tm); /* metamethod is the new function to be called */ setobj2s(L, func, tm); /* metamethod is the new function to be called */
return func; return func;
} }
@ -413,28 +435,29 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
int i; int i;
switch (wanted) { /* handle typical cases separately */ switch (wanted) { /* handle typical cases separately */
case 0: /* no values needed */ case 0: /* no values needed */
L->top = res; L->top.p = res;
return; return;
case 1: /* one value needed */ case 1: /* one value needed */
if (nres == 0) /* no results? */ if (nres == 0) /* no results? */
setnilvalue(s2v(res)); /* adjust with nil */ setnilvalue(s2v(res)); /* adjust with nil */
else /* at least one result */ else /* at least one result */
setobjs2s(L, res, L->top - nres); /* move it to proper place */ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */
L->top = res + 1; L->top.p = res + 1;
return; return;
case LUA_MULTRET: case LUA_MULTRET:
wanted = nres; /* we want all results */ wanted = nres; /* we want all results */
break; break;
default: /* two/more results and/or to-be-closed variables */ default: /* two/more results and/or to-be-closed variables */
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
ptrdiff_t savedres = savestack(L, res);
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
L->ci->u2.nres = nres; L->ci->u2.nres = nres;
luaF_close(L, res, CLOSEKTOP, 1); res = luaF_close(L, res, CLOSEKTOP, 1);
L->ci->callstatus &= ~CIST_CLSRET; L->ci->callstatus &= ~CIST_CLSRET;
if (L->hookmask) /* if needed, call hook after '__close's */ if (L->hookmask) { /* if needed, call hook after '__close's */
ptrdiff_t savedres = savestack(L, res);
rethook(L, L->ci, nres); rethook(L, L->ci, nres);
res = restorestack(L, savedres); /* close and hook can move stack */ res = restorestack(L, savedres); /* hook can move stack */
}
wanted = decodeNresults(wanted); wanted = decodeNresults(wanted);
if (wanted == LUA_MULTRET) if (wanted == LUA_MULTRET)
wanted = nres; /* we want all results */ wanted = nres; /* we want all results */
@ -442,14 +465,14 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
break; break;
} }
/* generic case */ /* generic case */
firstresult = L->top - nres; /* index of first result */ firstresult = L->top.p - nres; /* index of first result */
if (nres > wanted) /* extra results? */ if (nres > wanted) /* extra results? */
nres = wanted; /* don't need them */ nres = wanted; /* don't need them */
for (i = 0; i < nres; i++) /* move all results to correct place */ for (i = 0; i < nres; i++) /* move all results to correct place */
setobjs2s(L, res + i, firstresult + i); setobjs2s(L, res + i, firstresult + i);
for (; i < wanted; i++) /* complete wanted number of results */ for (; i < wanted; i++) /* complete wanted number of results */
setnilvalue(s2v(res + i)); setnilvalue(s2v(res + i));
L->top = res + wanted; /* top points after the last result */ L->top.p = res + wanted; /* top points after the last result */
} }
@ -464,7 +487,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
rethook(L, ci, nres); rethook(L, ci, nres);
/* move results to proper place */ /* move results to proper place */
moveresults(L, ci->func, nres, wanted); moveresults(L, ci->func.p, nres, wanted);
/* function cannot be in any of these cases when returning */ /* function cannot be in any of these cases when returning */
lua_assert(!(ci->callstatus & lua_assert(!(ci->callstatus &
(CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET))); (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
@ -479,10 +502,10 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret,
int mask, StkId top) { int mask, StkId top) {
CallInfo *ci = L->ci = next_ci(L); /* new frame */ CallInfo *ci = L->ci = next_ci(L); /* new frame */
ci->func = func; ci->func.p = func;
ci->nresults = nret; ci->nresults = nret;
ci->callstatus = mask; ci->callstatus = mask;
ci->top = top; ci->top.p = top;
return ci; return ci;
} }
@ -496,10 +519,10 @@ l_sinline int precallC (lua_State *L, StkId func, int nresults,
CallInfo *ci; CallInfo *ci;
checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
L->ci = ci = prepCallInfo(L, func, nresults, CIST_C, L->ci = ci = prepCallInfo(L, func, nresults, CIST_C,
L->top + LUA_MINSTACK); L->top.p + LUA_MINSTACK);
lua_assert(ci->top <= L->stack_last); lua_assert(ci->top.p <= L->stack_last.p);
if (l_unlikely(L->hookmask & LUA_MASKCALL)) { if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
int narg = cast_int(L->top - func) - 1; int narg = cast_int(L->top.p - func) - 1;
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
} }
lua_unlock(L); lua_unlock(L);
@ -531,17 +554,17 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
int nfixparams = p->numparams; int nfixparams = p->numparams;
int i; int i;
checkstackGCp(L, fsize - delta, func); checkstackGCp(L, fsize - delta, func);
ci->func -= delta; /* restore 'func' (if vararg) */ ci->func.p -= delta; /* restore 'func' (if vararg) */
for (i = 0; i < narg1; i++) /* move down function and arguments */ for (i = 0; i < narg1; i++) /* move down function and arguments */
setobjs2s(L, ci->func + i, func + i); setobjs2s(L, ci->func.p + i, func + i);
func = ci->func; /* moved-down function */ func = ci->func.p; /* moved-down function */
for (; narg1 <= nfixparams; narg1++) for (; narg1 <= nfixparams; narg1++)
setnilvalue(s2v(func + narg1)); /* complete missing arguments */ setnilvalue(s2v(func + narg1)); /* complete missing arguments */
ci->top = func + 1 + fsize; /* top for new function */ ci->top.p = func + 1 + fsize; /* top for new function */
lua_assert(ci->top <= L->stack_last); lua_assert(ci->top.p <= L->stack_last.p);
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus |= CIST_TAIL; ci->callstatus |= CIST_TAIL;
L->top = func + narg1; /* set top */ L->top.p = func + narg1; /* set top */
return -1; return -1;
} }
default: { /* not a function */ default: { /* not a function */
@ -574,15 +597,15 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
case LUA_VLCL: { /* Lua function */ case LUA_VLCL: { /* Lua function */
CallInfo *ci; CallInfo *ci;
Proto *p = clLvalue(s2v(func))->p; Proto *p = clLvalue(s2v(func))->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */ int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */
int nfixparams = p->numparams; int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */ int fsize = p->maxstacksize; /* frame size */
checkstackGCp(L, fsize, func); checkstackGCp(L, fsize, func);
L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize); L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize);
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
for (; narg < nfixparams; narg++) for (; narg < nfixparams; narg++)
setnilvalue(s2v(L->top++)); /* complete missing arguments */ setnilvalue(s2v(L->top.p++)); /* complete missing arguments */
lua_assert(ci->top <= L->stack_last); lua_assert(ci->top.p <= L->stack_last.p);
return ci; return ci;
} }
default: { /* not a function */ default: { /* not a function */
@ -598,12 +621,17 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
** Call a function (C or Lua) through C. 'inc' can be 1 (increment ** Call a function (C or Lua) through C. 'inc' can be 1 (increment
** number of recursive invocations in the C stack) or nyci (the same ** number of recursive invocations in the C stack) or nyci (the same
** plus increment number of non-yieldable calls). ** plus increment number of non-yieldable calls).
** This function can be called with some use of EXTRA_STACK, so it should
** check the stack before doing anything else. 'luaD_precall' already
** does that.
*/ */
l_sinline void ccall (lua_State *L, StkId func, int nResults, int inc) { l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) {
CallInfo *ci; CallInfo *ci;
L->nCcalls += inc; L->nCcalls += inc;
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) {
checkstackp(L, 0, func); /* free any use of EXTRA_STACK */
luaE_checkcstack(L); luaE_checkcstack(L);
}
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
luaV_execute(L, ci); /* call it */ luaV_execute(L, ci); /* call it */
@ -651,8 +679,7 @@ static int finishpcallk (lua_State *L, CallInfo *ci) {
else { /* error */ else { /* error */
StkId func = restorestack(L, ci->u2.funcidx); StkId func = restorestack(L, ci->u2.funcidx);
L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */ L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
luaF_close(L, func, status, 1); /* can yield or raise an error */ func = luaF_close(L, func, status, 1); /* can yield or raise an error */
func = restorestack(L, ci->u2.funcidx); /* stack may be moved */
luaD_seterrorobj(L, status, func); luaD_seterrorobj(L, status, func);
luaD_shrinkstack(L); /* restore stack size in case of overflow */ luaD_shrinkstack(L); /* restore stack size in case of overflow */
setcistrecst(ci, LUA_OK); /* clear original status */ setcistrecst(ci, LUA_OK); /* clear original status */
@ -740,8 +767,8 @@ static CallInfo *findpcall (lua_State *L) {
** coroutine error handler and should not kill the coroutine.) ** coroutine error handler and should not kill the coroutine.)
*/ */
static int resume_error (lua_State *L, const char *msg, int narg) { static int resume_error (lua_State *L, const char *msg, int narg) {
L->top -= narg; /* remove args from the stack */ L->top.p -= narg; /* remove args from the stack */
setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */ setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
return LUA_ERRRUN; return LUA_ERRRUN;
@ -757,7 +784,7 @@ static int resume_error (lua_State *L, const char *msg, int narg) {
*/ */
static void resume (lua_State *L, void *ud) { static void resume (lua_State *L, void *ud) {
int n = *(cast(int*, ud)); /* number of arguments */ int n = *(cast(int*, ud)); /* number of arguments */
StkId firstArg = L->top - n; /* first argument */ StkId firstArg = L->top.p - n; /* first argument */
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
if (L->status == LUA_OK) /* starting a coroutine? */ if (L->status == LUA_OK) /* starting a coroutine? */
ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */ ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */
@ -765,7 +792,7 @@ static void resume (lua_State *L, void *ud) {
lua_assert(L->status == LUA_YIELD); lua_assert(L->status == LUA_YIELD);
L->status = LUA_OK; /* mark that it is running (again) */ L->status = LUA_OK; /* mark that it is running (again) */
if (isLua(ci)) { /* yielded inside a hook? */ if (isLua(ci)) { /* yielded inside a hook? */
L->top = firstArg; /* discard arguments */ L->top.p = firstArg; /* discard arguments */
luaV_execute(L, ci); /* just continue running Lua code */ luaV_execute(L, ci); /* just continue running Lua code */
} }
else { /* 'common' yield */ else { /* 'common' yield */
@ -808,7 +835,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
if (L->status == LUA_OK) { /* may be starting a coroutine */ if (L->status == LUA_OK) { /* may be starting a coroutine */
if (L->ci != &L->base_ci) /* not in base level? */ if (L->ci != &L->base_ci) /* not in base level? */
return resume_error(L, "cannot resume non-suspended coroutine", nargs); return resume_error(L, "cannot resume non-suspended coroutine", nargs);
else if (L->top - (L->ci->func + 1) == nargs) /* no function? */ else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */
return resume_error(L, "cannot resume dead coroutine", nargs); return resume_error(L, "cannot resume dead coroutine", nargs);
} }
else if (L->status != LUA_YIELD) /* ended with errors? */ else if (L->status != LUA_YIELD) /* ended with errors? */
@ -826,11 +853,11 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
lua_assert(status == L->status); /* normal end or yield */ lua_assert(status == L->status); /* normal end or yield */
else { /* unrecoverable error */ else { /* unrecoverable error */
L->status = cast_byte(status); /* mark thread as 'dead' */ L->status = cast_byte(status); /* mark thread as 'dead' */
luaD_seterrorobj(L, status, L->top); /* push error message */ luaD_seterrorobj(L, status, L->top.p); /* push error message */
L->ci->top = L->top; L->ci->top.p = L->top.p;
} }
*nresults = (status == LUA_YIELD) ? L->ci->u2.nyield *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield
: cast_int(L->top - (L->ci->func + 1)); : cast_int(L->top.p - (L->ci->func.p + 1));
lua_unlock(L); lua_unlock(L);
return status; return status;
} }
@ -985,7 +1012,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
p.dyd.label.arr = NULL; p.dyd.label.size = 0; p.dyd.label.arr = NULL; p.dyd.label.size = 0;
luaZ_initbuffer(L, &p.buff); luaZ_initbuffer(L, &p.buff);
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc);
luaZ_freebuffer(L, &p.buff); luaZ_freebuffer(L, &p.buff);
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);

View File

@ -8,6 +8,7 @@
#define ldo_h #define ldo_h
#include "llimits.h"
#include "lobject.h" #include "lobject.h"
#include "lstate.h" #include "lstate.h"
#include "lzio.h" #include "lzio.h"
@ -23,7 +24,7 @@
** at every check. ** at every check.
*/ */
#define luaD_checkstackaux(L,n,pre,pos) \ #define luaD_checkstackaux(L,n,pre,pos) \
if (l_unlikely(L->stack_last - L->top <= (n))) \ if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \
{ pre; luaD_growstack(L, n, 1); pos; } \ { pre; luaD_growstack(L, n, 1); pos; } \
else { condmovestack(L,pre,pos); } else { condmovestack(L,pre,pos); }
@ -32,11 +33,18 @@
#define savestack(L,p) ((char *)(p) - (char *)L->stack) #define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p))
#define restorestack(L,n) ((StkId)((char *)L->stack + (n))) #define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n))
/* macro to check stack size, preserving 'p' */ /* macro to check stack size, preserving 'p' */
#define checkstackp(L,n,p) \
luaD_checkstackaux(L, n, \
ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
/* macro to check stack size and GC, preserving 'p' */
#define checkstackGCp(L,n,p) \ #define checkstackGCp(L,n,p) \
luaD_checkstackaux(L, n, \ luaD_checkstackaux(L, n, \
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
@ -58,7 +66,8 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
int fTransfer, int nTransfer); int fTransfer, int nTransfer);
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1, int delta); LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
int narg1, int delta);
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);

View File

@ -10,6 +10,7 @@
#include "lprefix.h" #include "lprefix.h"
#include <limits.h>
#include <stddef.h> #include <stddef.h>
#include "lua.h" #include "lua.h"
@ -55,8 +56,11 @@ static void dumpByte (DumpState *D, int y) {
} }
/* dumpInt Buff Size */ /*
#define DIBS ((sizeof(size_t) * 8 / 7) + 1) ** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6"
** rounds up the division.)
*/
#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7)
static void dumpSize (DumpState *D, size_t x) { static void dumpSize (DumpState *D, size_t x) {
lu_byte buff[DIBS]; lu_byte buff[DIBS];

View File

@ -50,8 +50,8 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
for (i = 0; i < cl->nupvalues; i++) { for (i = 0; i < cl->nupvalues; i++) {
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
UpVal *uv = gco2upv(o); UpVal *uv = gco2upv(o);
uv->v = &uv->u.value; /* make it closed */ uv->v.p = &uv->u.value; /* make it closed */
setnilvalue(uv->v); setnilvalue(uv->v.p);
cl->upvals[i] = uv; cl->upvals[i] = uv;
luaC_objbarrier(L, cl, uv); luaC_objbarrier(L, cl, uv);
} }
@ -62,12 +62,11 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
** Create a new upvalue at the given level, and link it to the list of ** Create a new upvalue at the given level, and link it to the list of
** open upvalues of 'L' after entry 'prev'. ** open upvalues of 'L' after entry 'prev'.
**/ **/
static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) { static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) {
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
UpVal *uv = gco2upv(o); UpVal *uv = gco2upv(o);
UpVal *next = *prev; UpVal *next = *prev;
uv->v = s2v(level); /* current value lives in the stack */ uv->v.p = s2v(level); /* current value lives in the stack */
uv->tbc = tbc;
uv->u.open.next = next; /* link it to list of open upvalues */ uv->u.open.next = next; /* link it to list of open upvalues */
uv->u.open.previous = prev; uv->u.open.previous = prev;
if (next) if (next)
@ -96,7 +95,7 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
pp = &p->u.open.next; pp = &p->u.open.next;
} }
/* not found: create a new upvalue after 'pp' */ /* not found: create a new upvalue after 'pp' */
return newupval(L, 0, level, pp); return newupval(L, level, pp);
} }
@ -106,12 +105,12 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
** (This function assumes EXTRA_STACK.) ** (This function assumes EXTRA_STACK.)
*/ */
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
StkId top = L->top; StkId top = L->top.p;
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
setobj2s(L, top, tm); /* will call metamethod... */ setobj2s(L, top, tm); /* will call metamethod... */
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
L->top = top + 3; /* add function and arguments */ L->top.p = top + 3; /* add function and arguments */
if (yy) if (yy)
luaD_call(L, top, 0); luaD_call(L, top, 0);
else else
@ -126,7 +125,7 @@ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
static void checkclosemth (lua_State *L, StkId level) { static void checkclosemth (lua_State *L, StkId level) {
const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE); const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
if (ttisnil(tm)) { /* no metamethod? */ if (ttisnil(tm)) { /* no metamethod? */
int idx = cast_int(level - L->ci->func); /* variable index */ int idx = cast_int(level - L->ci->func.p); /* variable index */
const char *vname = luaG_findlocal(L, L->ci, idx, NULL); const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
if (vname == NULL) vname = "?"; if (vname == NULL) vname = "?";
luaG_runerror(L, "variable '%s' got a non-closable value", vname); luaG_runerror(L, "variable '%s' got a non-closable value", vname);
@ -160,23 +159,23 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
** is used.) ** is used.)
*/ */
#define MAXDELTA \ #define MAXDELTA \
((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1) ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1)
/* /*
** Insert a variable in the list of to-be-closed variables. ** Insert a variable in the list of to-be-closed variables.
*/ */
void luaF_newtbcupval (lua_State *L, StkId level) { void luaF_newtbcupval (lua_State *L, StkId level) {
lua_assert(level > L->tbclist); lua_assert(level > L->tbclist.p);
if (l_isfalse(s2v(level))) if (l_isfalse(s2v(level)))
return; /* false doesn't need to be closed */ return; /* false doesn't need to be closed */
checkclosemth(L, level); /* value must have a close method */ checkclosemth(L, level); /* value must have a close method */
while (cast_uint(level - L->tbclist) > MAXDELTA) { while (cast_uint(level - L->tbclist.p) > MAXDELTA) {
L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */ L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */
L->tbclist->tbclist.delta = 0; L->tbclist.p->tbclist.delta = 0;
} }
level->tbclist.delta = cast(unsigned short, level - L->tbclist); level->tbclist.delta = cast(unsigned short, level - L->tbclist.p);
L->tbclist = level; L->tbclist.p = level;
} }
@ -196,10 +195,10 @@ void luaF_closeupval (lua_State *L, StkId level) {
StkId upl; /* stack index pointed by 'uv' */ StkId upl; /* stack index pointed by 'uv' */
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
TValue *slot = &uv->u.value; /* new position for value */ TValue *slot = &uv->u.value; /* new position for value */
lua_assert(uplevel(uv) < L->top); lua_assert(uplevel(uv) < L->top.p);
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
setobj(L, slot, uv->v); /* move value to upvalue slot */ setobj(L, slot, uv->v.p); /* move value to upvalue slot */
uv->v = slot; /* now current value lives here */ uv->v.p = slot; /* now current value lives here */
if (!iswhite(uv)) { /* neither white nor dead? */ if (!iswhite(uv)) { /* neither white nor dead? */
nw2black(uv); /* closed upvalues cannot be gray */ nw2black(uv); /* closed upvalues cannot be gray */
luaC_barrier(L, uv, slot); luaC_barrier(L, uv, slot);
@ -209,31 +208,32 @@ void luaF_closeupval (lua_State *L, StkId level) {
/* /*
** Remove firt element from the tbclist plus its dummy nodes. ** Remove first element from the tbclist plus its dummy nodes.
*/ */
static void poptbclist (lua_State *L) { static void poptbclist (lua_State *L) {
StkId tbc = L->tbclist; StkId tbc = L->tbclist.p;
lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */ lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
tbc -= tbc->tbclist.delta; tbc -= tbc->tbclist.delta;
while (tbc > L->stack && tbc->tbclist.delta == 0) while (tbc > L->stack.p && tbc->tbclist.delta == 0)
tbc -= MAXDELTA; /* remove dummy nodes */ tbc -= MAXDELTA; /* remove dummy nodes */
L->tbclist = tbc; L->tbclist.p = tbc;
} }
/* /*
** Close all upvalues and to-be-closed variables up to the given stack ** Close all upvalues and to-be-closed variables up to the given stack
** level. ** level. Return restored 'level'.
*/ */
void luaF_close (lua_State *L, StkId level, int status, int yy) { StkId luaF_close (lua_State *L, StkId level, int status, int yy) {
ptrdiff_t levelrel = savestack(L, level); ptrdiff_t levelrel = savestack(L, level);
luaF_closeupval(L, level); /* first, close the upvalues */ luaF_closeupval(L, level); /* first, close the upvalues */
while (L->tbclist >= level) { /* traverse tbc's down to that level */ while (L->tbclist.p >= level) { /* traverse tbc's down to that level */
StkId tbc = L->tbclist; /* get variable index */ StkId tbc = L->tbclist.p; /* get variable index */
poptbclist(L); /* remove it from list */ poptbclist(L); /* remove it from list */
prepcallclosemth(L, tbc, status, yy); /* close variable */ prepcallclosemth(L, tbc, status, yy); /* close variable */
level = restorestack(L, levelrel); level = restorestack(L, levelrel);
} }
return level;
} }

View File

@ -29,10 +29,10 @@
#define MAXUPVAL 255 #define MAXUPVAL 255
#define upisopen(up) ((up)->v != &(up)->u.value) #define upisopen(up) ((up)->v.p != &(up)->u.value)
#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v)) #define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p))
/* /*
@ -54,7 +54,7 @@ LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy); LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy);
LUAI_FUNC void luaF_unlinkupval (UpVal *uv); LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,

View File

@ -252,12 +252,13 @@ void luaC_fix (lua_State *L, GCObject *o) {
/* /*
** create a new collectable object (with given type and size) and link ** create a new collectable object (with given type, size, and offset)
** it to 'allgc' list. ** and link it to 'allgc' list.
*/ */
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) {
global_State *g = G(L); global_State *g = G(L);
GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
GCObject *o = cast(GCObject *, p + offset);
o->marked = luaC_white(g); o->marked = luaC_white(g);
o->tt = tt; o->tt = tt;
o->next = g->allgc; o->next = g->allgc;
@ -265,6 +266,11 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
return o; return o;
} }
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
return luaC_newobjdt(L, tt, sz, 0);
}
/* }====================================================== */ /* }====================================================== */
@ -301,7 +307,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
set2gray(uv); /* open upvalues are kept gray */ set2gray(uv); /* open upvalues are kept gray */
else else
set2black(uv); /* closed upvalues are visited here */ set2black(uv); /* closed upvalues are visited here */
markvalue(g, uv->v); /* mark its content */ markvalue(g, uv->v.p); /* mark its content */
break; break;
} }
case LUA_VUSERDATA: { case LUA_VUSERDATA: {
@ -376,7 +382,7 @@ static int remarkupvals (global_State *g) {
work++; work++;
if (!iswhite(uv)) { /* upvalue already visited? */ if (!iswhite(uv)) { /* upvalue already visited? */
lua_assert(upisopen(uv) && isgray(uv)); lua_assert(upisopen(uv) && isgray(uv));
markvalue(g, uv->v); /* mark its value */ markvalue(g, uv->v.p); /* mark its value */
} }
} }
} }
@ -620,19 +626,19 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
*/ */
static int traversethread (global_State *g, lua_State *th) { static int traversethread (global_State *g, lua_State *th) {
UpVal *uv; UpVal *uv;
StkId o = th->stack; StkId o = th->stack.p;
if (isold(th) || g->gcstate == GCSpropagate) if (isold(th) || g->gcstate == GCSpropagate)
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
if (o == NULL) if (o == NULL)
return 1; /* stack not completely built yet */ return 1; /* stack not completely built yet */
lua_assert(g->gcstate == GCSatomic || lua_assert(g->gcstate == GCSatomic ||
th->openupval == NULL || isintwups(th)); th->openupval == NULL || isintwups(th));
for (; o < th->top; o++) /* mark live elements in the stack */ for (; o < th->top.p; o++) /* mark live elements in the stack */
markvalue(g, s2v(o)); markvalue(g, s2v(o));
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
markobject(g, uv); /* open upvalues cannot be collected */ markobject(g, uv); /* open upvalues cannot be collected */
if (g->gcstate == GCSatomic) { /* final traversal? */ if (g->gcstate == GCSatomic) { /* final traversal? */
for (; o < th->stack_last + EXTRA_STACK; o++) for (; o < th->stack_last.p + EXTRA_STACK; o++)
setnilvalue(s2v(o)); /* clear dead stack slice */ setnilvalue(s2v(o)); /* clear dead stack slice */
/* 'remarkupvals' may have removed thread from 'twups' list */ /* 'remarkupvals' may have removed thread from 'twups' list */
if (!isintwups(th) && th->openupval != NULL) { if (!isintwups(th) && th->openupval != NULL) {
@ -892,7 +898,7 @@ static GCObject *udata2finalize (global_State *g) {
static void dothecall (lua_State *L, void *ud) { static void dothecall (lua_State *L, void *ud) {
UNUSED(ud); UNUSED(ud);
luaD_callnoyield(L, L->top - 2, 0); luaD_callnoyield(L, L->top.p - 2, 0);
} }
@ -909,16 +915,16 @@ static void GCTM (lua_State *L) {
int oldgcstp = g->gcstp; int oldgcstp = g->gcstp;
g->gcstp |= GCSTPGC; /* avoid GC steps */ g->gcstp |= GCSTPGC; /* avoid GC steps */
L->allowhook = 0; /* stop debug hooks during GC metamethod */ L->allowhook = 0; /* stop debug hooks during GC metamethod */
setobj2s(L, L->top++, tm); /* push finalizer... */ setobj2s(L, L->top.p++, tm); /* push finalizer... */
setobj2s(L, L->top++, &v); /* ... and its argument */ setobj2s(L, L->top.p++, &v); /* ... and its argument */
L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0);
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
L->allowhook = oldah; /* restore hooks */ L->allowhook = oldah; /* restore hooks */
g->gcstp = oldgcstp; /* restore state */ g->gcstp = oldgcstp; /* restore state */
if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
luaE_warnerror(L, "__gc"); luaE_warnerror(L, "__gc");
L->top--; /* pops error object */ L->top.p--; /* pops error object */
} }
} }
} }
@ -1041,7 +1047,25 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
** ======================================================= ** =======================================================
*/ */
static void setpause (global_State *g);
/*
** Set the "time" to wait before starting a new GC cycle; cycle will
** start when memory use hits the threshold of ('estimate' * pause /
** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
** because Lua cannot even start with less than PAUSEADJ bytes).
*/
static void setpause (global_State *g) {
l_mem threshold, debt;
int pause = getgcparam(g->gcpause);
l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
lua_assert(estimate > 0);
threshold = (pause < MAX_LMEM / estimate) /* overflow? */
? estimate * pause /* no overflow */
: MAX_LMEM; /* overflow; truncate to maximum */
debt = gettotalbytes(g) - threshold;
if (debt > 0) debt = 0;
luaE_setdebt(g, debt);
}
/* /*
@ -1285,6 +1309,15 @@ static void atomic2gen (lua_State *L, global_State *g) {
} }
/*
** Set debt for the next minor collection, which will happen when
** memory grows 'genminormul'%.
*/
static void setminordebt (global_State *g) {
luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
}
/* /*
** Enter generational mode. Must go until the end of an atomic cycle ** Enter generational mode. Must go until the end of an atomic cycle
** to ensure that all objects are correctly marked and weak tables ** to ensure that all objects are correctly marked and weak tables
@ -1297,6 +1330,7 @@ static lu_mem entergen (lua_State *L, global_State *g) {
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
numobjs = atomic(L); /* propagates all and then do the atomic stuff */ numobjs = atomic(L); /* propagates all and then do the atomic stuff */
atomic2gen(L, g); atomic2gen(L, g);
setminordebt(g); /* set debt assuming next cycle will be minor */
return numobjs; return numobjs;
} }
@ -1342,15 +1376,6 @@ static lu_mem fullgen (lua_State *L, global_State *g) {
} }
/*
** Set debt for the next minor collection, which will happen when
** memory grows 'genminormul'%.
*/
static void setminordebt (global_State *g) {
luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
}
/* /*
** Does a major collection after last collection was a "bad collection". ** Does a major collection after last collection was a "bad collection".
** **
@ -1422,8 +1447,8 @@ static void genstep (lua_State *L, global_State *g) {
lu_mem numobjs = fullgen(L, g); /* do a major collection */ lu_mem numobjs = fullgen(L, g); /* do a major collection */
if (gettotalbytes(g) < majorbase + (majorinc / 2)) { if (gettotalbytes(g) < majorbase + (majorinc / 2)) {
/* collected at least half of memory growth since last major /* collected at least half of memory growth since last major
collection; keep doing minor collections */ collection; keep doing minor collections. */
setminordebt(g); lua_assert(g->lastatomic == 0);
} }
else { /* bad collection */ else { /* bad collection */
g->lastatomic = numobjs; /* signal that last collection was bad */ g->lastatomic = numobjs; /* signal that last collection was bad */
@ -1449,26 +1474,6 @@ static void genstep (lua_State *L, global_State *g) {
*/ */
/*
** Set the "time" to wait before starting a new GC cycle; cycle will
** start when memory use hits the threshold of ('estimate' * pause /
** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
** because Lua cannot even start with less than PAUSEADJ bytes).
*/
static void setpause (global_State *g) {
l_mem threshold, debt;
int pause = getgcparam(g->gcpause);
l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
lua_assert(estimate > 0);
threshold = (pause < MAX_LMEM / estimate) /* overflow? */
? estimate * pause /* no overflow */
: MAX_LMEM; /* overflow; truncate to maximum */
debt = gettotalbytes(g) - threshold;
if (debt > 0) debt = 0;
luaE_setdebt(g, debt);
}
/* /*
** Enter first sweep phase. ** Enter first sweep phase.
** The call to 'sweeptolive' makes the pointer point to an object ** The call to 'sweeptolive' makes the pointer point to an object
@ -1676,12 +1681,15 @@ static void incstep (lua_State *L, global_State *g) {
} }
/* /*
** performs a basic GC step if collector is running ** Performs a basic GC step if collector is running. (If collector is
** not running, set a reasonable debt to avoid it being called at
** every single check.)
*/ */
void luaC_step (lua_State *L) { void luaC_step (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
lua_assert(!g->gcemergency); if (!gcrunning(g)) /* not running? */
if (gcrunning(g)) { /* running? */ luaE_setdebt(g, -2000);
else {
if(isdecGCmodegen(g)) if(isdecGCmodegen(g))
genstep(L, g); genstep(L, g);
else else

View File

@ -172,24 +172,27 @@
#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
#define luaC_barrier(L,p,v) ( \
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0))
#define luaC_barrierback(L,p,v) ( \
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
luaC_barrierback_(L,p) : cast_void(0))
#define luaC_objbarrier(L,p,o) ( \ #define luaC_objbarrier(L,p,o) ( \
(isblack(p) && iswhite(o)) ? \ (isblack(p) && iswhite(o)) ? \
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
#define luaC_barrier(L,p,v) ( \
iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0))
#define luaC_objbarrierback(L,p,o) ( \
(isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0))
#define luaC_barrierback(L,p,v) ( \
iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0))
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_freeallobjects (lua_State *L);
LUAI_FUNC void luaC_step (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L);
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz,
size_t offset);
LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);

View File

@ -128,7 +128,7 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
** ensuring there is only one copy of each unique string. The table ** ensuring there is only one copy of each unique string. The table
** here is used as a set: the string enters as the key, while its value ** here is used as a set: the string enters as the key, while its value
** is irrelevant. We use the string itself as the value only because it ** is irrelevant. We use the string itself as the value only because it
** is a TValue readly available. Later, the code generation can change ** is a TValue readily available. Later, the code generation can change
** this value. ** this value.
*/ */
TString *luaX_newstring (LexState *ls, const char *str, size_t l) { TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
@ -138,12 +138,12 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
if (!ttisnil(o)) /* string already present? */ if (!ttisnil(o)) /* string already present? */
ts = keystrval(nodefromval(o)); /* get saved copy */ ts = keystrval(nodefromval(o)); /* get saved copy */
else { /* not in use yet */ else { /* not in use yet */
TValue *stv = s2v(L->top++); /* reserve stack space for string */ TValue *stv = s2v(L->top.p++); /* reserve stack space for string */
setsvalue(L, stv, ts); /* temporarily anchor the string */ setsvalue(L, stv, ts); /* temporarily anchor the string */
luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */ luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
/* table is not a metatable, so it does not need to invalidate cache */ /* table is not a metatable, so it does not need to invalidate cache */
luaC_checkGC(L); luaC_checkGC(L);
L->top--; /* remove string from stack */ L->top.p--; /* remove string from stack */
} }
return ts; return ts;
} }

View File

@ -71,11 +71,24 @@ typedef signed char ls_byte;
/* /*
** conversion of pointer to unsigned integer: ** conversion of pointer to unsigned integer: this is for hashing only;
** this is for hashing only; there is no problem if the integer ** there is no problem if the integer cannot hold the whole pointer
** cannot hold the whole pointer value ** value. (In strict ISO C this may cause undefined behavior, but no
** actual machine seems to bother.)
*/ */
#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX)) #if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
__STDC_VERSION__ >= 199901L
#include <stdint.h>
#if defined(UINTPTR_MAX) /* even in C99 this type is optional */
#define L_P2I uintptr_t
#else /* no 'intptr'? */
#define L_P2I uintmax_t /* use the largest available integer */
#endif
#else /* C89 option */
#define L_P2I size_t
#endif
#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX))

View File

@ -267,7 +267,7 @@ static int math_type (lua_State *L) {
/* try to find an integer type with at least 64 bits */ /* try to find an integer type with at least 64 bits */
#if (ULONG_MAX >> 31 >> 31) >= 3 #if ((ULONG_MAX >> 31) >> 31) >= 3
/* 'long' has at least 64 bits */ /* 'long' has at least 64 bits */
#define Rand64 unsigned long #define Rand64 unsigned long
@ -277,9 +277,9 @@ static int math_type (lua_State *L) {
/* there is a 'long long' type (which must have at least 64 bits) */ /* there is a 'long long' type (which must have at least 64 bits) */
#define Rand64 unsigned long long #define Rand64 unsigned long long
#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3 #elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
/* 'lua_Integer' has at least 64 bits */ /* 'lua_Unsigned' has at least 64 bits */
#define Rand64 lua_Unsigned #define Rand64 lua_Unsigned
#endif #endif
@ -500,12 +500,12 @@ static lua_Number I2d (Rand64 x) {
/* convert a 'Rand64' to a 'lua_Unsigned' */ /* convert a 'Rand64' to a 'lua_Unsigned' */
static lua_Unsigned I2UInt (Rand64 x) { static lua_Unsigned I2UInt (Rand64 x) {
return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l); return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l);
} }
/* convert a 'lua_Unsigned' to a 'Rand64' */ /* convert a 'lua_Unsigned' to a 'Rand64' */
static Rand64 Int2I (lua_Unsigned n) { static Rand64 Int2I (lua_Unsigned n) {
return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n); return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n);
} }
#endif /* } */ #endif /* } */

View File

@ -22,25 +22,6 @@
#include "lstate.h" #include "lstate.h"
#if defined(EMERGENCYGCTESTS)
/*
** First allocation will fail whenever not building initial state.
** (This fail will trigger 'tryagain' and a full GC cycle at every
** allocation.)
*/
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
if (completestate(g) && ns > 0) /* frees never fail */
return NULL; /* fail */
else /* normal allocation */
return (*g->frealloc)(g->ud, block, os, ns);
}
#else
#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
#endif
/* /*
** About the realloc function: ** About the realloc function:
@ -60,6 +41,43 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
*/ */
/*
** Macro to call the allocation function.
*/
#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
/*
** When an allocation fails, it will try again after an emergency
** collection, except when it cannot run a collection. The GC should
** not be called while the state is not fully built, as the collector
** is not yet fully initialized. Also, it should not be called when
** 'gcstopem' is true, because then the interpreter is in the middle of
** a collection step.
*/
#define cantryagain(g) (completestate(g) && !g->gcstopem)
#if defined(EMERGENCYGCTESTS)
/*
** First allocation will fail except when freeing a block (frees never
** fail) and when it cannot try again; this fail will trigger 'tryagain'
** and a full GC cycle at every allocation.
*/
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
if (ns > 0 && cantryagain(g))
return NULL; /* fail */
else /* normal allocation */
return callfrealloc(g, block, os, ns);
}
#else
#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns)
#endif
/* /*
@ -132,7 +150,7 @@ l_noret luaM_toobig (lua_State *L) {
void luaM_free_ (lua_State *L, void *block, size_t osize) { void luaM_free_ (lua_State *L, void *block, size_t osize) {
global_State *g = G(L); global_State *g = G(L);
lua_assert((osize == 0) == (block == NULL)); lua_assert((osize == 0) == (block == NULL));
(*g->frealloc)(g->ud, block, osize, 0); callfrealloc(g, block, osize, 0);
g->GCdebt -= osize; g->GCdebt -= osize;
} }
@ -140,19 +158,15 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
/* /*
** In case of allocation fail, this function will do an emergency ** In case of allocation fail, this function will do an emergency
** collection to free some memory and then try the allocation again. ** collection to free some memory and then try the allocation again.
** The GC should not be called while state is not fully built, as the
** collector is not yet fully initialized. Also, it should not be called
** when 'gcstopem' is true, because then the interpreter is in the
** middle of a collection step.
*/ */
static void *tryagain (lua_State *L, void *block, static void *tryagain (lua_State *L, void *block,
size_t osize, size_t nsize) { size_t osize, size_t nsize) {
global_State *g = G(L); global_State *g = G(L);
if (completestate(g) && !g->gcstopem) { if (cantryagain(g)) {
luaC_fullgc(L, 1); /* try to free some memory... */ luaC_fullgc(L, 1); /* try to free some memory... */
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ return callfrealloc(g, block, osize, nsize); /* try again */
} }
else return NULL; /* cannot free any memory without a full state */ else return NULL; /* cannot run an emergency collection */
} }

View File

@ -708,8 +708,13 @@ static const luaL_Reg ll_funcs[] = {
static void createsearcherstable (lua_State *L) { static void createsearcherstable (lua_State *L) {
static const lua_CFunction searchers[] = static const lua_CFunction searchers[] = {
{searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; searcher_preload,
searcher_Lua,
searcher_C,
searcher_Croot,
NULL
};
int i; int i;
/* create 'searchers' table */ /* create 'searchers' table */
lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);

View File

@ -62,7 +62,7 @@ static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
case LUA_OPBOR: return intop(|, v1, v2); case LUA_OPBOR: return intop(|, v1, v2);
case LUA_OPBXOR: return intop(^, v1, v2); case LUA_OPBXOR: return intop(^, v1, v2);
case LUA_OPSHL: return luaV_shiftl(v1, v2); case LUA_OPSHL: return luaV_shiftl(v1, v2);
case LUA_OPSHR: return luaV_shiftl(v1, -v2); case LUA_OPSHR: return luaV_shiftr(v1, v2);
case LUA_OPUNM: return intop(-, 0, v1); case LUA_OPUNM: return intop(-, 0, v1);
case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
default: lua_assert(0); return 0; default: lua_assert(0); return 0;
@ -386,29 +386,39 @@ void luaO_tostring (lua_State *L, TValue *obj) {
** =================================================================== ** ===================================================================
*/ */
/* size for buffer space used by 'luaO_pushvfstring' */ /*
#define BUFVFS 200 ** Size for buffer space used by 'luaO_pushvfstring'. It should be
** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
** so that 'luaG_addinfo' can work directly on the buffer.
*/
#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95)
/* buffer used by 'luaO_pushvfstring' */ /* buffer used by 'luaO_pushvfstring' */
typedef struct BuffFS { typedef struct BuffFS {
lua_State *L; lua_State *L;
int pushed; /* number of string pieces already on the stack */ int pushed; /* true if there is a part of the result on the stack */
int blen; /* length of partial string in 'space' */ int blen; /* length of partial string in 'space' */
char space[BUFVFS]; /* holds last part of the result */ char space[BUFVFS]; /* holds last part of the result */
} BuffFS; } BuffFS;
/* /*
** Push given string to the stack, as part of the buffer, and ** Push given string to the stack, as part of the result, and
** join the partial strings in the stack into one. ** join it to previous partial result if there is one.
** It may call 'luaV_concat' while using one slot from EXTRA_STACK.
** This call cannot invoke metamethods, as both operands must be
** strings. It can, however, raise an error if the result is too
** long. In that case, 'luaV_concat' frees the extra slot before
** raising the error.
*/ */
static void pushstr (BuffFS *buff, const char *str, size_t l) { static void pushstr (BuffFS *buff, const char *str, size_t lstr) {
lua_State *L = buff->L; lua_State *L = buff->L;
setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr));
L->top++; /* may use one extra slot */ L->top.p++; /* may use one slot from EXTRA_STACK */
buff->pushed++; if (!buff->pushed) /* no previous string on the stack? */
luaV_concat(L, buff->pushed); /* join partial results into one */ buff->pushed = 1; /* now there is one */
buff->pushed = 1; else /* join previous string with new one */
luaV_concat(L, 2);
} }
@ -454,7 +464,7 @@ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
/* /*
** Add a number to the buffer. ** Add a numeral to the buffer.
*/ */
static void addnum2buff (BuffFS *buff, TValue *num) { static void addnum2buff (BuffFS *buff, TValue *num) {
char *numbuff = getbuff(buff, MAXNUMBER2STR); char *numbuff = getbuff(buff, MAXNUMBER2STR);
@ -532,7 +542,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
clearbuff(&buff); /* empty buffer into the stack */ clearbuff(&buff); /* empty buffer into the stack */
lua_assert(buff.pushed == 1); lua_assert(buff.pushed == 1);
return svalue(s2v(L->top - 1)); return svalue(s2v(L->top.p - 1));
} }

View File

@ -52,6 +52,8 @@ typedef union Value {
lua_CFunction f; /* light C functions */ lua_CFunction f; /* light C functions */
lua_Integer i; /* integer numbers */ lua_Integer i; /* integer numbers */
lua_Number n; /* float numbers */ lua_Number n; /* float numbers */
/* not used, but may avoid warnings for uninitialized value */
lu_byte ub;
} Value; } Value;
@ -155,6 +157,17 @@ typedef union StackValue {
/* index to stack elements */ /* index to stack elements */
typedef StackValue *StkId; typedef StackValue *StkId;
/*
** When reallocating the stack, change all pointers to the stack into
** proper offsets.
*/
typedef union {
StkId p; /* actual pointer */
ptrdiff_t offset; /* used while the stack is being reallocated */
} StkIdRel;
/* convert a 'StackValue' to a 'TValue' */ /* convert a 'StackValue' to a 'TValue' */
#define s2v(o) (&(o)->val) #define s2v(o) (&(o)->val)
@ -615,8 +628,10 @@ typedef struct Proto {
*/ */
typedef struct UpVal { typedef struct UpVal {
CommonHeader; CommonHeader;
lu_byte tbc; /* true if it represents a to-be-closed variable */ union {
TValue *v; /* points to stack or to its own value */ TValue *p; /* points to stack or to its own value */
ptrdiff_t offset; /* used while the stack is being reallocated */
} v;
union { union {
struct { /* (when open) */ struct { /* (when open) */
struct UpVal *next; /* linked list */ struct UpVal *next; /* linked list */

View File

@ -21,7 +21,7 @@ iABC C(8) | B(8) |k| A(8) | Op(7) |
iABx Bx(17) | A(8) | Op(7) | iABx Bx(17) | A(8) | Op(7) |
iAsBx sBx (signed)(17) | A(8) | Op(7) | iAsBx sBx (signed)(17) | A(8) | Op(7) |
iAx Ax(25) | Op(7) | iAx Ax(25) | Op(7) |
isJ sJ(25) | Op(7) | isJ sJ (signed)(25) | Op(7) |
A signed argument is represented in excess K: the represented value is A signed argument is represented in excess K: the represented value is
the written unsigned value minus K, where K is half the maximum for the the written unsigned value minus K, where K is half the maximum for the

View File

@ -30,23 +30,14 @@
*/ */
#if !defined(LUA_STRFTIMEOPTIONS) /* { */ #if !defined(LUA_STRFTIMEOPTIONS) /* { */
/* options for ANSI C 89 (only 1-char options) */
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%"
/* options for ISO C 99 and POSIX */
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
/* options for Windows */
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
#if defined(LUA_USE_WINDOWS) #if defined(LUA_USE_WINDOWS)
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
#elif defined(LUA_USE_C89) "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89 #elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
#else /* C99 specification */ #else /* C99 specification */
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99 #define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
#endif #endif
#endif /* } */ #endif /* } */
@ -138,21 +129,27 @@
/* }================================================================== */ /* }================================================================== */
#if !defined(l_system)
#if defined(LUA_USE_IOS)
/* Despite claiming to be ISO C, iOS does not implement 'system'. */
#define l_system(cmd) ((cmd) == NULL ? 0 : -1)
#else
#define l_system(cmd) system(cmd) /* default definition */
#endif
#endif
static int os_execute (lua_State *L) { static int os_execute (lua_State *L) {
#if !defined(__APPLE__)
const char *cmd = luaL_optstring(L, 1, NULL); const char *cmd = luaL_optstring(L, 1, NULL);
int stat; int stat;
errno = 0; errno = 0;
stat = system(cmd); stat = l_system(cmd);
if (cmd != NULL) if (cmd != NULL)
return luaL_execresult(L, stat); return luaL_execresult(L, stat);
else { else {
lua_pushboolean(L, stat); /* true if there is a shell */ lua_pushboolean(L, stat); /* true if there is a shell */
return 1; return 1;
} }
#else
return 0;
#endif
} }
@ -263,9 +260,7 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
res = d; res = d;
} }
else { else {
/* unsigned avoids overflow when lua_Integer has 32 bits */ if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res))
if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta
: (lua_Integer)INT_MIN + delta <= res))
return luaL_error(L, "field '%s' is out-of-bound", key); return luaL_error(L, "field '%s' is out-of-bound", key);
res -= delta; res -= delta;
} }

View File

@ -468,6 +468,7 @@ static void singlevar (LexState *ls, expdesc *var) {
expdesc key; expdesc key;
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
lua_assert(var->k != VVOID); /* this one must exist */ lua_assert(var->k != VVOID); /* this one must exist */
luaK_exp2anyregup(fs, var); /* but could be a constant */
codestring(&key, varname); /* key is variable name */ codestring(&key, varname); /* key is variable name */
luaK_indexed(fs, var, &key); /* env[varname] */ luaK_indexed(fs, var, &key); /* env[varname] */
} }
@ -520,12 +521,12 @@ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
/* /*
** Solves the goto at index 'g' to given 'label' and removes it ** Solves the goto at index 'g' to given 'label' and removes it
** from the list of pending goto's. ** from the list of pending gotos.
** If it jumps into the scope of some variable, raises an error. ** If it jumps into the scope of some variable, raises an error.
*/ */
static void solvegoto (LexState *ls, int g, Labeldesc *label) { static void solvegoto (LexState *ls, int g, Labeldesc *label) {
int i; int i;
Labellist *gl = &ls->dyd->gt; /* list of goto's */ Labellist *gl = &ls->dyd->gt; /* list of gotos */
Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
lua_assert(eqstr(gt->name, label->name)); lua_assert(eqstr(gt->name, label->name));
if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
@ -579,7 +580,7 @@ static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
/* /*
** Solves forward jumps. Check whether new label 'lb' matches any ** Solves forward jumps. Check whether new label 'lb' matches any
** pending gotos in current block and solves them. Return true ** pending gotos in current block and solves them. Return true
** if any of the goto's need to close upvalues. ** if any of the gotos need to close upvalues.
*/ */
static int solvegotos (LexState *ls, Labeldesc *lb) { static int solvegotos (LexState *ls, Labeldesc *lb) {
Labellist *gl = &ls->dyd->gt; Labellist *gl = &ls->dyd->gt;
@ -600,7 +601,7 @@ static int solvegotos (LexState *ls, Labeldesc *lb) {
/* /*
** Create a new label with the given 'name' at the given 'line'. ** Create a new label with the given 'name' at the given 'line'.
** 'last' tells whether label is the last non-op statement in its ** 'last' tells whether label is the last non-op statement in its
** block. Solves all pending goto's to this new label and adds ** block. Solves all pending gotos to this new label and adds
** a close instruction if necessary. ** a close instruction if necessary.
** Returns true iff it added a close instruction. ** Returns true iff it added a close instruction.
*/ */
@ -673,19 +674,19 @@ static void leaveblock (FuncState *fs) {
LexState *ls = fs->ls; LexState *ls = fs->ls;
int hasclose = 0; int hasclose = 0;
int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */ int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
if (bl->isloop) /* fix pending breaks? */ removevars(fs, bl->nactvar); /* remove block locals */
lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */
if (bl->isloop) /* has to fix pending breaks? */
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
if (!hasclose && bl->previous && bl->upval) if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */
luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
fs->bl = bl->previous;
removevars(fs, bl->nactvar);
lua_assert(bl->nactvar == fs->nactvar);
fs->freereg = stklevel; /* free registers */ fs->freereg = stklevel; /* free registers */
ls->dyd->label.n = bl->firstlabel; /* remove local labels */ ls->dyd->label.n = bl->firstlabel; /* remove local labels */
if (bl->previous) /* inner block? */ fs->bl = bl->previous; /* current block now is previous one */
movegotosout(fs, bl); /* update pending gotos to outer block */ if (bl->previous) /* was it a nested block? */
movegotosout(fs, bl); /* update pending gotos to enclosing block */
else { else {
if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */
undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
} }
} }
@ -1943,10 +1944,10 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
LexState lexstate; LexState lexstate;
FuncState funcstate; FuncState funcstate;
LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */ setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
luaD_inctop(L); luaD_inctop(L);
lexstate.h = luaH_new(L); /* create table for scanner */ lexstate.h = luaH_new(L); /* create table for scanner */
sethvalue2s(L, L->top, lexstate.h); /* anchor it */ sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */
luaD_inctop(L); luaD_inctop(L);
funcstate.f = cl->p = luaF_newproto(L); funcstate.f = cl->p = luaF_newproto(L);
luaC_objbarrier(L, cl, cl->p); luaC_objbarrier(L, cl, cl->p);
@ -1960,7 +1961,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
/* all scopes should be correctly finished */ /* all scopes should be correctly finished */
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
L->top--; /* remove scanner's table */ L->top.p--; /* remove scanner's table */
return cl; /* closure is on the stack, too */ return cl; /* closure is on the stack, too */
} }

View File

@ -180,33 +180,33 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) {
static void stack_init (lua_State *L1, lua_State *L) { static void stack_init (lua_State *L1, lua_State *L) {
int i; CallInfo *ci; int i; CallInfo *ci;
/* initialize stack array */ /* initialize stack array */
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
L1->tbclist = L1->stack; L1->tbclist.p = L1->stack.p;
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
setnilvalue(s2v(L1->stack + i)); /* erase new stack */ setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
L1->top = L1->stack; L1->top.p = L1->stack.p;
L1->stack_last = L1->stack + BASIC_STACK_SIZE; L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
/* initialize first ci */ /* initialize first ci */
ci = &L1->base_ci; ci = &L1->base_ci;
ci->next = ci->previous = NULL; ci->next = ci->previous = NULL;
ci->callstatus = CIST_C; ci->callstatus = CIST_C;
ci->func = L1->top; ci->func.p = L1->top.p;
ci->u.c.k = NULL; ci->u.c.k = NULL;
ci->nresults = 0; ci->nresults = 0;
setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */ setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */
L1->top++; L1->top.p++;
ci->top = L1->top + LUA_MINSTACK; ci->top.p = L1->top.p + LUA_MINSTACK;
L1->ci = ci; L1->ci = ci;
} }
static void freestack (lua_State *L) { static void freestack (lua_State *L) {
if (L->stack == NULL) if (L->stack.p == NULL)
return; /* stack not completely built yet */ return; /* stack not completely built yet */
L->ci = &L->base_ci; /* free the entire 'ci' list */ L->ci = &L->base_ci; /* free the entire 'ci' list */
luaE_freeCI(L); luaE_freeCI(L);
lua_assert(L->nci == 0); lua_assert(L->nci == 0);
luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */ luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */
} }
@ -248,7 +248,7 @@ static void f_luaopen (lua_State *L, void *ud) {
*/ */
static void preinit_thread (lua_State *L, global_State *g) { static void preinit_thread (lua_State *L, global_State *g) {
G(L) = g; G(L) = g;
L->stack = NULL; L->stack.p = NULL;
L->ci = NULL; L->ci = NULL;
L->nci = 0; L->nci = 0;
L->twups = L; /* thread has no upvalues */ L->twups = L; /* thread has no upvalues */
@ -284,20 +284,16 @@ static void close_state (lua_State *L) {
LUA_API lua_State *lua_newthread (lua_State *L) { LUA_API lua_State *lua_newthread (lua_State *L) {
global_State *g; global_State *g = G(L);
GCObject *o;
lua_State *L1; lua_State *L1;
lua_lock(L); lua_lock(L);
g = G(L);
luaC_checkGC(L); luaC_checkGC(L);
/* create new thread */ /* create new thread */
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l));
L1->marked = luaC_white(g); L1 = gco2th(o);
L1->tt = LUA_VTHREAD;
/* link it on list 'allgc' */
L1->next = g->allgc;
g->allgc = obj2gco(L1);
/* anchor it on L stack */ /* anchor it on L stack */
setthvalue2s(L, L->top, L1); setthvalue2s(L, L->top.p, L1);
api_incr_top(L); api_incr_top(L);
preinit_thread(L1, g); preinit_thread(L1, g);
L1->hookmask = L->hookmask; L1->hookmask = L->hookmask;
@ -316,7 +312,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
void luaE_freethread (lua_State *L, lua_State *L1) { void luaE_freethread (lua_State *L, lua_State *L1) {
LX *l = fromstate(L1); LX *l = fromstate(L1);
luaF_closeupval(L1, L1->stack); /* close all upvalues */ luaF_closeupval(L1, L1->stack.p); /* close all upvalues */
lua_assert(L1->openupval == NULL); lua_assert(L1->openupval == NULL);
luai_userstatefree(L, L1); luai_userstatefree(L, L1);
freestack(L1); freestack(L1);
@ -326,32 +322,41 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
int luaE_resetthread (lua_State *L, int status) { int luaE_resetthread (lua_State *L, int status) {
CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */ setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */
ci->func = L->stack; ci->func.p = L->stack.p;
ci->callstatus = CIST_C; ci->callstatus = CIST_C;
if (status == LUA_YIELD) if (status == LUA_YIELD)
status = LUA_OK; status = LUA_OK;
L->status = LUA_OK; /* so it can run __close metamethods */ L->status = LUA_OK; /* so it can run __close metamethods */
status = luaD_closeprotected(L, 1, status); status = luaD_closeprotected(L, 1, status);
if (status != LUA_OK) /* errors? */ if (status != LUA_OK) /* errors? */
luaD_seterrorobj(L, status, L->stack + 1); luaD_seterrorobj(L, status, L->stack.p + 1);
else else
L->top = L->stack + 1; L->top.p = L->stack.p + 1;
ci->top = L->top + LUA_MINSTACK; ci->top.p = L->top.p + LUA_MINSTACK;
luaD_reallocstack(L, cast_int(ci->top - L->stack), 0); luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0);
return status; return status;
} }
LUA_API int lua_resetthread (lua_State *L) { LUA_API int lua_closethread (lua_State *L, lua_State *from) {
int status; int status;
lua_lock(L); lua_lock(L);
L->nCcalls = (from) ? getCcalls(from) : 0;
status = luaE_resetthread(L, L->status); status = luaE_resetthread(L, L->status);
lua_unlock(L); lua_unlock(L);
return status; return status;
} }
/*
** Deprecated! Use 'lua_closethread' instead.
*/
LUA_API int lua_resetthread (lua_State *L) {
return lua_closethread(L, NULL);
}
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i; int i;
lua_State *L; lua_State *L;
@ -426,7 +431,7 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
** Generate a warning from an error message ** Generate a warning from an error message
*/ */
void luaE_warnerror (lua_State *L, const char *where) { void luaE_warnerror (lua_State *L, const char *where) {
TValue *errobj = s2v(L->top - 1); /* error object */ TValue *errobj = s2v(L->top.p - 1); /* error object */
const char *msg = (ttisstring(errobj)) const char *msg = (ttisstring(errobj))
? svalue(errobj) ? svalue(errobj)
: "error object is not a string"; : "error object is not a string";

View File

@ -9,6 +9,11 @@
#include "lua.h" #include "lua.h"
/* Some header files included here need this definition */
typedef struct CallInfo CallInfo;
#include "lobject.h" #include "lobject.h"
#include "ltm.h" #include "ltm.h"
#include "lzio.h" #include "lzio.h"
@ -139,7 +144,7 @@ struct lua_longjmp; /* defined in ldo.c */
#define BASIC_STACK_SIZE (2*LUA_MINSTACK) #define BASIC_STACK_SIZE (2*LUA_MINSTACK)
#define stacksize(th) cast_int((th)->stack_last - (th)->stack) #define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p)
/* kinds of Garbage Collection */ /* kinds of Garbage Collection */
@ -169,9 +174,9 @@ typedef struct stringtable {
** - field 'transferinfo' is used only during call/returnhooks, ** - field 'transferinfo' is used only during call/returnhooks,
** before the function starts or after it ends. ** before the function starts or after it ends.
*/ */
typedef struct CallInfo { struct CallInfo {
StkId func; /* function index in the stack */ StkIdRel func; /* function index in the stack */
StkId top; /* top for this function */ StkIdRel top; /* top for this function */
struct CallInfo *previous, *next; /* dynamic call link */ struct CallInfo *previous, *next; /* dynamic call link */
union { union {
struct { /* only for Lua functions */ struct { /* only for Lua functions */
@ -196,7 +201,7 @@ typedef struct CallInfo {
} u2; } u2;
short nresults; /* expected number of results from this function */ short nresults; /* expected number of results from this function */
unsigned short callstatus; unsigned short callstatus;
} CallInfo; };
/* /*
@ -291,7 +296,7 @@ typedef struct global_State {
struct lua_State *mainthread; struct lua_State *mainthread;
TString *memerrmsg; /* message for memory-allocation errors */ TString *memerrmsg; /* message for memory-allocation errors */
TString *tmname[TM_N]; /* array with tag-method names */ TString *tmname[TM_N]; /* array with tag-method names */
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
lua_WarnFunction warnf; /* warning function */ lua_WarnFunction warnf; /* warning function */
void *ud_warn; /* auxiliary data to 'warnf' */ void *ud_warn; /* auxiliary data to 'warnf' */
@ -306,13 +311,13 @@ struct lua_State {
lu_byte status; lu_byte status;
lu_byte allowhook; lu_byte allowhook;
unsigned short nci; /* number of items in 'ci' list */ unsigned short nci; /* number of items in 'ci' list */
StkId top; /* first free slot in the stack */ StkIdRel top; /* first free slot in the stack */
global_State *l_G; global_State *l_G;
CallInfo *ci; /* call info for current function */ CallInfo *ci; /* call info for current function */
StkId stack_last; /* end of stack (last element + 1) */ StkIdRel stack_last; /* end of stack (last element + 1) */
StkId stack; /* stack base */ StkIdRel stack; /* stack base */
UpVal *openupval; /* list of open upvalues in this stack */ UpVal *openupval; /* list of open upvalues in this stack */
StkId tbclist; /* list of to-be-closed variables */ StkIdRel tbclist; /* list of to-be-closed variables */
GCObject *gclist; GCObject *gclist;
struct lua_State *twups; /* list of threads with open upvalues */ struct lua_State *twups; /* list of threads with open upvalues */
struct lua_longjmp *errorJmp; /* current error recover point */ struct lua_longjmp *errorJmp; /* current error recover point */

View File

@ -570,7 +570,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) {
static const char *match (MatchState *ms, const char *s, const char *p) { static const char *match (MatchState *ms, const char *s, const char *p) {
if (l_unlikely(ms->matchdepth-- == 0)) if (l_unlikely(ms->matchdepth-- == 0))
luaL_error(ms->L, "pattern too complex"); luaL_error(ms->L, "pattern too complex");
init: /* using goto's to optimize tail recursion */ init: /* using goto to optimize tail recursion */
if (p != ms->p_end) { /* end of pattern? */ if (p != ms->p_end) { /* end of pattern? */
switch (*p) { switch (*p) {
case '(': { /* start capture */ case '(': { /* start capture */

View File

@ -107,7 +107,7 @@ static const TValue absentkey = {ABSTKEYCONSTANT};
*/ */
static Node *hashint (const Table *t, lua_Integer i) { static Node *hashint (const Table *t, lua_Integer i) {
lua_Unsigned ui = l_castS2U(i); lua_Unsigned ui = l_castS2U(i);
if (ui <= (unsigned int)INT_MAX) if (ui <= cast_uint(INT_MAX))
return hashmod(t, cast_int(ui)); return hashmod(t, cast_int(ui));
else else
return hashmod(t, ui); return hashmod(t, ui);
@ -257,9 +257,11 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
size |= (size >> 2); size |= (size >> 2);
size |= (size >> 4); size |= (size >> 4);
size |= (size >> 8); size |= (size >> 8);
#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */
size |= (size >> 16); size |= (size >> 16);
#if (UINT_MAX >> 30) > 3 #if (UINT_MAX >> 30) > 3
size |= (size >> 32); /* unsigned int has more than 32 bits */ size |= (size >> 32); /* unsigned int has more than 32 bits */
#endif
#endif #endif
size++; size++;
lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size);
@ -488,7 +490,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) {
luaG_runerror(L, "table overflow"); luaG_runerror(L, "table overflow");
size = twoto(lsize); size = twoto(lsize);
t->node = luaM_newvector(L, size, Node); t->node = luaM_newvector(L, size, Node);
for (i = 0; i < (int)size; i++) { for (i = 0; i < cast_int(size); i++) {
Node *n = gnode(t, i); Node *n = gnode(t, i);
gnext(n) = 0; gnext(n) = 0;
setnilkey(n); setnilkey(n);
@ -975,6 +977,4 @@ Node *luaH_mainposition (const Table *t, const TValue *key) {
return mainpositionTV(t, key); return mainpositionTV(t, key);
} }
int luaH_isdummy (const Table *t) { return isdummy(t); }
#endif #endif

View File

@ -59,7 +59,6 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t);
#if defined(LUA_DEBUG) #if defined(LUA_DEBUG)
LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
LUAI_FUNC int luaH_isdummy (const Table *t);
#endif #endif

View File

@ -93,7 +93,7 @@ static int tremove (lua_State *L) {
lua_Integer pos = luaL_optinteger(L, 2, size); lua_Integer pos = luaL_optinteger(L, 2, size);
if (pos != size) /* validate 'pos' if given */ if (pos != size) /* validate 'pos' if given */
/* check whether 'pos' is in [1, size + 1] */ /* check whether 'pos' is in [1, size + 1] */
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1, luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2,
"position out of bounds"); "position out of bounds");
lua_geti(L, 1, pos); /* result = t[pos] */ lua_geti(L, 1, pos); /* result = t[pos] */
for ( ; pos < size; pos++) { for ( ; pos < size; pos++) {

View File

@ -102,12 +102,12 @@ const char *luaT_objtypename (lua_State *L, const TValue *o) {
void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
const TValue *p2, const TValue *p3) { const TValue *p2, const TValue *p3) {
StkId func = L->top; StkId func = L->top.p;
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 1, p1); /* 1st argument */
setobj2s(L, func + 2, p2); /* 2nd argument */ setobj2s(L, func + 2, p2); /* 2nd argument */
setobj2s(L, func + 3, p3); /* 3rd argument */ setobj2s(L, func + 3, p3); /* 3rd argument */
L->top = func + 4; L->top.p = func + 4;
/* metamethod may yield only when called from Lua code */ /* metamethod may yield only when called from Lua code */
if (isLuacode(L->ci)) if (isLuacode(L->ci))
luaD_call(L, func, 0); luaD_call(L, func, 0);
@ -119,18 +119,18 @@ void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
const TValue *p2, StkId res) { const TValue *p2, StkId res) {
ptrdiff_t result = savestack(L, res); ptrdiff_t result = savestack(L, res);
StkId func = L->top; StkId func = L->top.p;
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 1, p1); /* 1st argument */
setobj2s(L, func + 2, p2); /* 2nd argument */ setobj2s(L, func + 2, p2); /* 2nd argument */
L->top += 3; L->top.p += 3;
/* metamethod may yield only when called from Lua code */ /* metamethod may yield only when called from Lua code */
if (isLuacode(L->ci)) if (isLuacode(L->ci))
luaD_call(L, func, 1); luaD_call(L, func, 1);
else else
luaD_callnoyield(L, func, 1); luaD_callnoyield(L, func, 1);
res = restorestack(L, result); res = restorestack(L, result);
setobjs2s(L, res, --L->top); /* move result to its place */ setobjs2s(L, res, --L->top.p); /* move result to its place */
} }
@ -165,7 +165,7 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
void luaT_tryconcatTM (lua_State *L) { void luaT_tryconcatTM (lua_State *L) {
StkId top = L->top; StkId top = L->top.p;
if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
TM_CONCAT))) TM_CONCAT)))
luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
@ -200,15 +200,15 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
*/ */
int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
TMS event) { TMS event) {
if (callbinTM(L, p1, p2, L->top, event)) /* try original event */ if (callbinTM(L, p1, p2, L->top.p, event)) /* try original event */
return !l_isfalse(s2v(L->top)); return !l_isfalse(s2v(L->top.p));
#if defined(LUA_COMPAT_LT_LE) #if defined(LUA_COMPAT_LT_LE)
else if (event == TM_LE) { else if (event == TM_LE) {
/* try '!(p2 < p1)' for '(p1 <= p2)' */ /* try '!(p2 < p1)' for '(p1 <= p2)' */
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
if (callbinTM(L, p2, p1, L->top, TM_LT)) { if (callbinTM(L, p2, p1, L->top.p, TM_LT)) {
L->ci->callstatus ^= CIST_LEQ; /* clear mark */ L->ci->callstatus ^= CIST_LEQ; /* clear mark */
return l_isfalse(s2v(L->top)); return l_isfalse(s2v(L->top.p));
} }
/* else error will remove this 'ci'; no need to clear mark */ /* else error will remove this 'ci'; no need to clear mark */
} }
@ -238,20 +238,20 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci, void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
const Proto *p) { const Proto *p) {
int i; int i;
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */ int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */
int nextra = actual - nfixparams; /* number of extra arguments */ int nextra = actual - nfixparams; /* number of extra arguments */
ci->u.l.nextraargs = nextra; ci->u.l.nextraargs = nextra;
luaD_checkstack(L, p->maxstacksize + 1); luaD_checkstack(L, p->maxstacksize + 1);
/* copy function to the top of the stack */ /* copy function to the top of the stack */
setobjs2s(L, L->top++, ci->func); setobjs2s(L, L->top.p++, ci->func.p);
/* move fixed parameters to the top of the stack */ /* move fixed parameters to the top of the stack */
for (i = 1; i <= nfixparams; i++) { for (i = 1; i <= nfixparams; i++) {
setobjs2s(L, L->top++, ci->func + i); setobjs2s(L, L->top.p++, ci->func.p + i);
setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */ setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
} }
ci->func += actual + 1; ci->func.p += actual + 1;
ci->top += actual + 1; ci->top.p += actual + 1;
lua_assert(L->top <= ci->top && ci->top <= L->stack_last); lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
} }
@ -261,10 +261,10 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
if (wanted < 0) { if (wanted < 0) {
wanted = nextra; /* get all extra arguments available */ wanted = nextra; /* get all extra arguments available */
checkstackGCp(L, nextra, where); /* ensure stack space */ checkstackGCp(L, nextra, where); /* ensure stack space */
L->top = where + nextra; /* next instruction will need top */ L->top.p = where + nextra; /* next instruction will need top */
} }
for (i = 0; i < wanted && i < nextra; i++) for (i = 0; i < wanted && i < nextra; i++)
setobjs2s(L, where + i, ci->func - nextra + i); setobjs2s(L, where + i, ci->func.p - nextra + i);
for (; i < wanted; i++) /* complete required results with nil */ for (; i < wanted; i++) /* complete required results with nil */
setnilvalue(s2v(where + i)); setnilvalue(s2v(where + i));
} }

View File

@ -9,6 +9,7 @@
#include "lobject.h" #include "lobject.h"
#include "lstate.h"
/* /*
@ -95,8 +96,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, int isfloat, TMS event); int inv, int isfloat, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
struct CallInfo *ci, const Proto *p); CallInfo *ci, const Proto *p);
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci,
StkId where, int wanted); StkId where, int wanted);

View File

@ -177,10 +177,11 @@ static void print_version (void) {
** to the script (everything after 'script') go to positive indices; ** to the script (everything after 'script') go to positive indices;
** other arguments (before the script name) go to negative indices. ** other arguments (before the script name) go to negative indices.
** If there is no script name, assume interpreter's name as base. ** If there is no script name, assume interpreter's name as base.
** (If there is no interpreter's name either, 'script' is -1, so
** table sizes are zero.)
*/ */
static void createargtable (lua_State *L, char **argv, int argc, int script) { static void createargtable (lua_State *L, char **argv, int argc, int script) {
int i, narg; int i, narg;
if (script == argc) script = 0; /* no script name? */
narg = argc - (script + 1); /* number of positive indices */ narg = argc - (script + 1); /* number of positive indices */
lua_createtable(L, narg, script + 1); lua_createtable(L, narg, script + 1);
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
@ -268,14 +269,23 @@ static int handle_script (lua_State *L, char **argv) {
/* /*
** Traverses all arguments from 'argv', returning a mask with those ** Traverses all arguments from 'argv', returning a mask with those
** needed before running any Lua code (or an error code if it finds ** needed before running any Lua code or an error code if it finds any
** any invalid argument). 'first' returns the first not-handled argument ** invalid argument. In case of error, 'first' is the index of the bad
** (either the script name or a bad argument in case of error). ** argument. Otherwise, 'first' is -1 if there is no program name,
** 0 if there is no script name, or the index of the script name.
*/ */
static int collectargs (char **argv, int *first) { static int collectargs (char **argv, int *first) {
int args = 0; int args = 0;
int i; int i;
for (i = 1; argv[i] != NULL; i++) { if (argv[0] != NULL) { /* is there a program name? */
if (argv[0][0]) /* not empty? */
progname = argv[0]; /* save it */
}
else { /* no program name */
*first = -1;
return 0;
}
for (i = 1; argv[i] != NULL; i++) { /* handle arguments */
*first = i; *first = i;
if (argv[i][0] != '-') /* not an option? */ if (argv[i][0] != '-') /* not an option? */
return args; /* stop handling options */ return args; /* stop handling options */
@ -316,7 +326,7 @@ static int collectargs (char **argv, int *first) {
return has_error; return has_error;
} }
} }
*first = i; /* no script name */ *first = 0; /* no script name */
return args; return args;
} }
@ -609,8 +619,8 @@ static int pmain (lua_State *L) {
char **argv = (char **)lua_touserdata(L, 2); char **argv = (char **)lua_touserdata(L, 2);
int script; int script;
int args = collectargs(argv, &script); int args = collectargs(argv, &script);
int optlim = (script > 0) ? script : argc; /* first argv not an option */
luaL_checkversion(L); /* check that interpreter has correct version */ luaL_checkversion(L); /* check that interpreter has correct version */
if (argv[0] && argv[0][0]) progname = argv[0];
if (args == has_error) { /* bad arg? */ if (args == has_error) { /* bad arg? */
print_usage(argv[script]); /* 'script' has index of bad arg. */ print_usage(argv[script]); /* 'script' has index of bad arg. */
return 0; return 0;
@ -623,19 +633,21 @@ static int pmain (lua_State *L) {
} }
luaL_openlibs(L); /* open standard libraries */ luaL_openlibs(L); /* open standard libraries */
createargtable(L, argv, argc, script); /* create table 'arg' */ createargtable(L, argv, argc, script); /* create table 'arg' */
lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */ lua_gc(L, LUA_GCRESTART); /* start GC... */
lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */
if (!(args & has_E)) { /* no option '-E'? */ if (!(args & has_E)) { /* no option '-E'? */
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
return 0; /* error running LUA_INIT */ return 0; /* error running LUA_INIT */
} }
if (!runargs(L, argv, script)) /* execute arguments -e and -l */ if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
return 0; /* something failed */ return 0; /* something failed */
if (script < argc && /* execute main script (if there is one) */ if (script > 0) { /* execute main script (if there is one) */
handle_script(L, argv + script) != LUA_OK) if (handle_script(L, argv + script) != LUA_OK)
return 0; return 0; /* interrupt in case of error */
}
if (args & has_i) /* -i option? */ if (args & has_i) /* -i option? */
doREPL(L); /* do read-eval-print loop */ doREPL(L); /* do read-eval-print loop */
else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */ else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */
if (lua_stdin_is_tty()) { /* running in interactive mode? */ if (lua_stdin_is_tty()) { /* running in interactive mode? */
print_version(); print_version();
doREPL(L); /* do read-eval-print loop */ doREPL(L); /* do read-eval-print loop */
@ -654,6 +666,7 @@ int main (int argc, char **argv) {
l_message(argv[0], "cannot create state: not enough memory"); l_message(argv[0], "cannot create state: not enough memory");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
lua_gc(L, LUA_GCSTOP); /* stop GC while building state */
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
lua_pushinteger(L, argc); /* 1st argument */ lua_pushinteger(L, argc); /* 1st argument */
lua_pushlightuserdata(L, argv); /* 2nd argument */ lua_pushlightuserdata(L, argv); /* 2nd argument */

View File

@ -18,14 +18,14 @@
#define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4" #define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "4" #define LUA_VERSION_RELEASE "6"
#define LUA_VERSION_NUM 504 #define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 4) #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2022 Lua.org, PUC-Rio" #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@ -131,6 +131,16 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont); typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
/*
** Type used by the debug API to collect debug information
*/
typedef struct lua_Debug lua_Debug;
/*
** Functions to be called by the debugger in specific events
*/
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
/* /*
@ -153,7 +163,8 @@ extern const char lua_ident[];
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API void (lua_close) (lua_State *L); LUA_API void (lua_close) (lua_State *L);
LUA_API lua_State *(lua_newthread) (lua_State *L); LUA_API lua_State *(lua_newthread) (lua_State *L);
LUA_API int (lua_resetthread) (lua_State *L); LUA_API int (lua_closethread) (lua_State *L, lua_State *from);
LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
@ -442,12 +453,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx);
#define LUA_MASKLINE (1 << LUA_HOOKLINE) #define LUA_MASKLINE (1 << LUA_HOOKLINE)
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) #define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
typedef struct lua_Debug lua_Debug; /* activation record */
/* Functions to be called by the debugger in specific events */
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
@ -492,7 +497,7 @@ struct lua_Debug {
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2022 Lua.org, PUC-Rio. * Copyright (C) 1994-2023 Lua.org, PUC-Rio.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the

View File

@ -121,7 +121,7 @@ static int doargs(int argc, char* argv[])
return i; return i;
} }
#define FUNCTION "(function()end)();" #define FUNCTION "(function()end)();\n"
static const char* reader(lua_State* L, void* ud, size_t* size) static const char* reader(lua_State* L, void* ud, size_t* size)
{ {
@ -138,7 +138,7 @@ static const char* reader(lua_State* L, void* ud, size_t* size)
} }
} }
#define toproto(L,i) getproto(s2v(L->top+(i))) #define toproto(L,i) getproto(s2v(L->top.p+(i)))
static const Proto* combine(lua_State* L, int n) static const Proto* combine(lua_State* L, int n)
{ {
@ -155,8 +155,6 @@ static const Proto* combine(lua_State* L, int n)
f->p[i]=toproto(L,i-n-1); f->p[i]=toproto(L,i-n-1);
if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0; if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0;
} }
luaM_freearray(L,f->lineinfo,f->sizelineinfo);
f->sizelineinfo=0;
return f; return f;
} }
} }

View File

@ -70,6 +70,12 @@
#endif #endif
#if defined(LUA_USE_IOS)
#define LUA_USE_POSIX
#define LUA_USE_DLOPEN
#endif
/* /*
@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. @@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits.
*/ */
@ -728,7 +734,7 @@
** CHANGE it if you need a different limit. This limit is arbitrary; ** CHANGE it if you need a different limit. This limit is arbitrary;
** its only purpose is to stop Lua from consuming unlimited stack ** its only purpose is to stop Lua from consuming unlimited stack
** space (and to reserve some numbers for pseudo-indices). ** space (and to reserve some numbers for pseudo-indices).
** (It must fit into max(size_t)/32.) ** (It must fit into max(size_t)/32 and max(int)/2.)
*/ */
#if LUAI_IS32INT #if LUAI_IS32INT
#define LUAI_MAXSTACK 1000000 #define LUAI_MAXSTACK 1000000
@ -747,14 +753,15 @@
/* /*
@@ LUA_IDSIZE gives the maximum size for the description of the source @@ LUA_IDSIZE gives the maximum size for the description of the source
@@ of a function in debug information. ** of a function in debug information.
** CHANGE it if you want a different size. ** CHANGE it if you want a different size.
*/ */
#define LUA_IDSIZE 60 #define LUA_IDSIZE 60
/* /*
@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. @@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib
** buffer system.
*/ */
#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) #define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))

View File

@ -120,10 +120,10 @@ static TString *loadStringN (LoadState *S, Proto *p) {
} }
else { /* long string */ else { /* long string */
ts = luaS_createlngstrobj(L, size); /* create string */ ts = luaS_createlngstrobj(L, size); /* create string */
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */ setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */
luaD_inctop(L); luaD_inctop(L);
loadVector(S, getstr(ts), size); /* load directly in final place */ loadVector(S, getstr(ts), size); /* load directly in final place */
L->top--; /* pop string */ L->top.p--; /* pop string */
} }
luaC_objbarrier(L, p, ts); luaC_objbarrier(L, p, ts);
return ts; return ts;
@ -248,6 +248,8 @@ static void loadDebug (LoadState *S, Proto *f) {
f->locvars[i].endpc = loadInt(S); f->locvars[i].endpc = loadInt(S);
} }
n = loadInt(S); n = loadInt(S);
if (n != 0) /* does it have debug information? */
n = f->sizeupvalues; /* must be this many */
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
f->upvalues[i].name = loadStringN(S, f); f->upvalues[i].name = loadStringN(S, f);
} }
@ -321,7 +323,7 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
S.Z = Z; S.Z = Z;
checkHeader(&S); checkHeader(&S);
cl = luaF_newLclosure(L, loadByte(&S)); cl = luaF_newLclosure(L, loadByte(&S));
setclLvalue2s(L, L->top, cl); setclLvalue2s(L, L->top.p, cl);
luaD_inctop(L); luaD_inctop(L);
cl->p = luaF_newproto(L); cl->p = luaF_newproto(L);
luaC_objbarrier(L, cl, cl->p); luaC_objbarrier(L, cl, cl->p);

View File

@ -25,6 +25,9 @@
#define MAXUTF 0x7FFFFFFFu #define MAXUTF 0x7FFFFFFFu
#define MSGInvalid "invalid UTF-8 code"
/* /*
** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. ** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
*/ */
@ -35,7 +38,8 @@ typedef unsigned long utfint;
#endif #endif
#define iscont(p) ((*(p) & 0xC0) == 0x80) #define iscont(c) (((c) & 0xC0) == 0x80)
#define iscontp(p) iscont(*(p))
/* from strlib */ /* from strlib */
@ -65,7 +69,7 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) {
int count = 0; /* to count number of continuation bytes */ int count = 0; /* to count number of continuation bytes */
for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
unsigned int cc = (unsigned char)s[++count]; /* read next byte */ unsigned int cc = (unsigned char)s[++count]; /* read next byte */
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ if (!iscont(cc)) /* not a continuation byte? */
return NULL; /* invalid byte sequence */ return NULL; /* invalid byte sequence */
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
} }
@ -140,7 +144,7 @@ static int codepoint (lua_State *L) {
utfint code; utfint code;
s = utf8_decode(s, &code, !lax); s = utf8_decode(s, &code, !lax);
if (s == NULL) if (s == NULL)
return luaL_error(L, "invalid UTF-8 code"); return luaL_error(L, MSGInvalid);
lua_pushinteger(L, code); lua_pushinteger(L, code);
n++; n++;
} }
@ -190,16 +194,16 @@ static int byteoffset (lua_State *L) {
"position out of bounds"); "position out of bounds");
if (n == 0) { if (n == 0) {
/* find beginning of current byte sequence */ /* find beginning of current byte sequence */
while (posi > 0 && iscont(s + posi)) posi--; while (posi > 0 && iscontp(s + posi)) posi--;
} }
else { else {
if (iscont(s + posi)) if (iscontp(s + posi))
return luaL_error(L, "initial position is a continuation byte"); return luaL_error(L, "initial position is a continuation byte");
if (n < 0) { if (n < 0) {
while (n < 0 && posi > 0) { /* move back */ while (n < 0 && posi > 0) { /* move back */
do { /* find beginning of previous character */ do { /* find beginning of previous character */
posi--; posi--;
} while (posi > 0 && iscont(s + posi)); } while (posi > 0 && iscontp(s + posi));
n++; n++;
} }
} }
@ -208,7 +212,7 @@ static int byteoffset (lua_State *L) {
while (n > 0 && posi < (lua_Integer)len) { while (n > 0 && posi < (lua_Integer)len) {
do { /* find beginning of next character */ do { /* find beginning of next character */
posi++; posi++;
} while (iscont(s + posi)); /* (cannot pass final '\0') */ } while (iscontp(s + posi)); /* (cannot pass final '\0') */
n--; n--;
} }
} }
@ -226,15 +230,15 @@ static int iter_aux (lua_State *L, int strict) {
const char *s = luaL_checklstring(L, 1, &len); const char *s = luaL_checklstring(L, 1, &len);
lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2); lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2);
if (n < len) { if (n < len) {
while (iscont(s + n)) n++; /* skip continuation bytes */ while (iscontp(s + n)) n++; /* go to next character */
} }
if (n >= len) /* (also handles original 'n' being negative) */ if (n >= len) /* (also handles original 'n' being negative) */
return 0; /* no more codepoints */ return 0; /* no more codepoints */
else { else {
utfint code; utfint code;
const char *next = utf8_decode(s + n, &code, strict); const char *next = utf8_decode(s + n, &code, strict);
if (next == NULL) if (next == NULL || iscontp(next))
return luaL_error(L, "invalid UTF-8 code"); return luaL_error(L, MSGInvalid);
lua_pushinteger(L, n + 1); lua_pushinteger(L, n + 1);
lua_pushinteger(L, code); lua_pushinteger(L, code);
return 2; return 2;
@ -253,7 +257,8 @@ static int iter_auxlax (lua_State *L) {
static int iter_codes (lua_State *L) { static int iter_codes (lua_State *L) {
int lax = lua_toboolean(L, 2); int lax = lua_toboolean(L, 2);
luaL_checkstring(L, 1); const char *s = luaL_checkstring(L, 1);
luaL_argcheck(L, !iscontp(s), 1, MSGInvalid);
lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
lua_pushvalue(L, 1); lua_pushvalue(L, 1);
lua_pushinteger(L, 0); lua_pushinteger(L, 0);

View File

@ -608,8 +608,8 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
if (tm == NULL) /* no TM? */ if (tm == NULL) /* no TM? */
return 0; /* objects are different */ return 0; /* objects are different */
else { else {
luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */ luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */
return !l_isfalse(s2v(L->top)); return !l_isfalse(s2v(L->top.p));
} }
} }
@ -633,17 +633,17 @@ static void copy2buff (StkId top, int n, char *buff) {
/* /*
** Main operation for concatenation: concat 'total' values in the stack, ** Main operation for concatenation: concat 'total' values in the stack,
** from 'L->top - total' up to 'L->top - 1'. ** from 'L->top.p - total' up to 'L->top.p - 1'.
*/ */
void luaV_concat (lua_State *L, int total) { void luaV_concat (lua_State *L, int total) {
if (total == 1) if (total == 1)
return; /* "all" values already concatenated */ return; /* "all" values already concatenated */
do { do {
StkId top = L->top; StkId top = L->top.p;
int n = 2; /* number of elements handled in this pass (at least 2) */ int n = 2; /* number of elements handled in this pass (at least 2) */
if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) ||
!tostring(L, s2v(top - 1))) !tostring(L, s2v(top - 1)))
luaT_tryconcatTM(L); luaT_tryconcatTM(L); /* may invalidate 'top' */
else if (isemptystr(s2v(top - 1))) /* second operand is empty? */ else if (isemptystr(s2v(top - 1))) /* second operand is empty? */
cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ cast_void(tostring(L, s2v(top - 2))); /* result is first operand */
else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */
@ -656,8 +656,10 @@ void luaV_concat (lua_State *L, int total) {
/* collect total length and number of strings */ /* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
size_t l = vslen(s2v(top - n - 1)); size_t l = vslen(s2v(top - n - 1));
if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) {
L->top.p = top - total; /* pop strings to avoid wasting stack */
luaG_runerror(L, "string length overflow"); luaG_runerror(L, "string length overflow");
}
tl += l; tl += l;
} }
if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */
@ -671,8 +673,8 @@ void luaV_concat (lua_State *L, int total) {
} }
setsvalue2s(L, top - n, ts); /* create result */ setsvalue2s(L, top - n, ts); /* create result */
} }
total -= n-1; /* got 'n' strings to create 1 new */ total -= n - 1; /* got 'n' strings to create one new */
L->top -= n-1; /* popped 'n' strings and pushed one */ L->top.p -= n - 1; /* popped 'n' strings and pushed one */
} while (total > 1); /* repeat until only 1 result left */ } while (total > 1); /* repeat until only 1 result left */
} }
@ -763,12 +765,10 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) {
/* number of bits in an integer */ /* number of bits in an integer */
#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) #define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT)
/* /*
** Shift left operation. (Shift right just negates 'y'.) ** Shift left operation. (Shift right just negates 'y'.)
*/ */
#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y))
lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) {
if (y < 0) { /* shift right? */ if (y < 0) { /* shift right? */
if (y <= -NBITS) return 0; if (y <= -NBITS) return 0;
@ -808,26 +808,26 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
*/ */
void luaV_finishOp (lua_State *L) { void luaV_finishOp (lua_State *L) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
StkId base = ci->func + 1; StkId base = ci->func.p + 1;
Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */
OpCode op = GET_OPCODE(inst); OpCode op = GET_OPCODE(inst);
switch (op) { /* finish its execution */ switch (op) { /* finish its execution */
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top); setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p);
break; break;
} }
case OP_UNM: case OP_BNOT: case OP_LEN: case OP_UNM: case OP_BNOT: case OP_LEN:
case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: case OP_GETTABUP: case OP_GETTABLE: case OP_GETI:
case OP_GETFIELD: case OP_SELF: { case OP_GETFIELD: case OP_SELF: {
setobjs2s(L, base + GETARG_A(inst), --L->top); setobjs2s(L, base + GETARG_A(inst), --L->top.p);
break; break;
} }
case OP_LT: case OP_LE: case OP_LT: case OP_LE:
case OP_LTI: case OP_LEI: case OP_LTI: case OP_LEI:
case OP_GTI: case OP_GEI: case OP_GTI: case OP_GEI:
case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
int res = !l_isfalse(s2v(L->top - 1)); int res = !l_isfalse(s2v(L->top.p - 1));
L->top--; L->top.p--;
#if defined(LUA_COMPAT_LT_LE) #if defined(LUA_COMPAT_LT_LE)
if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
ci->callstatus ^= CIST_LEQ; /* clear mark */ ci->callstatus ^= CIST_LEQ; /* clear mark */
@ -840,11 +840,11 @@ void luaV_finishOp (lua_State *L) {
break; break;
} }
case OP_CONCAT: { case OP_CONCAT: {
StkId top = L->top - 1; /* top when 'luaT_tryconcatTM' was called */ StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */
int a = GETARG_A(inst); /* first element to concatenate */ int a = GETARG_A(inst); /* first element to concatenate */
int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
setobjs2s(L, top - 2, top); /* put TM result in proper position */ setobjs2s(L, top - 2, top); /* put TM result in proper position */
L->top = top - 1; /* top is one after last element (at top-2) */ L->top.p = top - 1; /* top is one after last element (at top-2) */
luaV_concat(L, total); /* concat them (may yield again) */ luaV_concat(L, total); /* concat them (may yield again) */
break; break;
} }
@ -856,7 +856,7 @@ void luaV_finishOp (lua_State *L) {
StkId ra = base + GETARG_A(inst); StkId ra = base + GETARG_A(inst);
/* adjust top to signal correct number of returns, in case the /* adjust top to signal correct number of returns, in case the
return is "up to top" ('isIT') */ return is "up to top" ('isIT') */
L->top = ra + ci->u2.nres; L->top.p = ra + ci->u2.nres;
/* repeat instruction to close other vars. and complete the return */ /* repeat instruction to close other vars. and complete the return */
ci->u.l.savedpc--; ci->u.l.savedpc--;
break; break;
@ -898,6 +898,7 @@ void luaV_finishOp (lua_State *L) {
** operation, 'fop' is the float operation. ** operation, 'fop' is the float operation.
*/ */
#define op_arithI(L,iop,fop) { \ #define op_arithI(L,iop,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
int imm = GETARG_sC(i); \ int imm = GETARG_sC(i); \
if (ttisinteger(v1)) { \ if (ttisinteger(v1)) { \
@ -926,6 +927,7 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations over floats and others with register operands. ** Arithmetic operations over floats and others with register operands.
*/ */
#define op_arithf(L,fop) { \ #define op_arithf(L,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \ TValue *v2 = vRC(i); \
op_arithf_aux(L, v1, v2, fop); } op_arithf_aux(L, v1, v2, fop); }
@ -935,6 +937,7 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations with K operands for floats. ** Arithmetic operations with K operands for floats.
*/ */
#define op_arithfK(L,fop) { \ #define op_arithfK(L,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
op_arithf_aux(L, v1, v2, fop); } op_arithf_aux(L, v1, v2, fop); }
@ -944,6 +947,7 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations over integers and floats. ** Arithmetic operations over integers and floats.
*/ */
#define op_arith_aux(L,v1,v2,iop,fop) { \ #define op_arith_aux(L,v1,v2,iop,fop) { \
StkId ra = RA(i); \
if (ttisinteger(v1) && ttisinteger(v2)) { \ if (ttisinteger(v1) && ttisinteger(v2)) { \
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
@ -973,6 +977,7 @@ void luaV_finishOp (lua_State *L) {
** Bitwise operations with constant operand. ** Bitwise operations with constant operand.
*/ */
#define op_bitwiseK(L,op) { \ #define op_bitwiseK(L,op) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \ TValue *v2 = KC(i); \
lua_Integer i1; \ lua_Integer i1; \
@ -986,6 +991,7 @@ void luaV_finishOp (lua_State *L) {
** Bitwise operations with register operands. ** Bitwise operations with register operands.
*/ */
#define op_bitwise(L,op) { \ #define op_bitwise(L,op) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \ TValue *v2 = vRC(i); \
lua_Integer i1; lua_Integer i2; \ lua_Integer i1; lua_Integer i2; \
@ -1000,18 +1006,19 @@ void luaV_finishOp (lua_State *L) {
** integers. ** integers.
*/ */
#define op_order(L,opi,opn,other) { \ #define op_order(L,opi,opn,other) { \
int cond; \ StkId ra = RA(i); \
TValue *rb = vRB(i); \ int cond; \
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ TValue *rb = vRB(i); \
lua_Integer ia = ivalue(s2v(ra)); \ if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
lua_Integer ib = ivalue(rb); \ lua_Integer ia = ivalue(s2v(ra)); \
cond = opi(ia, ib); \ lua_Integer ib = ivalue(rb); \
} \ cond = opi(ia, ib); \
else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ } \
cond = opn(s2v(ra), rb); \ else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
else \ cond = opn(s2v(ra), rb); \
Protect(cond = other(L, s2v(ra), rb)); \ else \
docondjump(); } Protect(cond = other(L, s2v(ra), rb)); \
docondjump(); }
/* /*
@ -1019,20 +1026,21 @@ void luaV_finishOp (lua_State *L) {
** always small enough to have an exact representation as a float.) ** always small enough to have an exact representation as a float.)
*/ */
#define op_orderI(L,opi,opf,inv,tm) { \ #define op_orderI(L,opi,opf,inv,tm) { \
int cond; \ StkId ra = RA(i); \
int im = GETARG_sB(i); \ int cond; \
if (ttisinteger(s2v(ra))) \ int im = GETARG_sB(i); \
cond = opi(ivalue(s2v(ra)), im); \ if (ttisinteger(s2v(ra))) \
else if (ttisfloat(s2v(ra))) { \ cond = opi(ivalue(s2v(ra)), im); \
lua_Number fa = fltvalue(s2v(ra)); \ else if (ttisfloat(s2v(ra))) { \
lua_Number fim = cast_num(im); \ lua_Number fa = fltvalue(s2v(ra)); \
cond = opf(fa, fim); \ lua_Number fim = cast_num(im); \
} \ cond = opf(fa, fim); \
else { \ } \
int isf = GETARG_C(i); \ else { \
Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ int isf = GETARG_C(i); \
} \ Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
docondjump(); } } \
docondjump(); }
/* }================================================================== */ /* }================================================================== */
@ -1061,7 +1069,7 @@ void luaV_finishOp (lua_State *L) {
#define updatetrap(ci) (trap = ci->u.l.trap) #define updatetrap(ci) (trap = ci->u.l.trap)
#define updatebase(ci) (base = ci->func + 1) #define updatebase(ci) (base = ci->func.p + 1)
#define updatestack(ci) \ #define updatestack(ci) \
@ -1096,7 +1104,7 @@ void luaV_finishOp (lua_State *L) {
** Whenever code can raise errors, the global 'pc' and the global ** Whenever code can raise errors, the global 'pc' and the global
** 'top' must be correct to report occasional errors. ** 'top' must be correct to report occasional errors.
*/ */
#define savestate(L,ci) (savepc(L), L->top = ci->top) #define savestate(L,ci) (savepc(L), L->top.p = ci->top.p)
/* /*
@ -1116,7 +1124,7 @@ void luaV_finishOp (lua_State *L) {
/* 'c' is the limit of live values in the stack */ /* 'c' is the limit of live values in the stack */
#define checkGC(L,c) \ #define checkGC(L,c) \
{ luaC_condGC(L, (savepc(L), L->top = (c)), \ { luaC_condGC(L, (savepc(L), L->top.p = (c)), \
updatetrap(ci)); \ updatetrap(ci)); \
luai_threadyield(L); } luai_threadyield(L); }
@ -1128,7 +1136,6 @@ void luaV_finishOp (lua_State *L) {
updatebase(ci); /* correct stack */ \ updatebase(ci); /* correct stack */ \
} \ } \
i = *(pc++); \ i = *(pc++); \
ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \
} }
#define vmdispatch(o) switch(o) #define vmdispatch(o) switch(o)
@ -1148,7 +1155,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
startfunc: startfunc:
trap = L->hookmask; trap = L->hookmask;
returning: /* trap already set */ returning: /* trap already set */
cl = clLvalue(s2v(ci->func)); cl = clLvalue(s2v(ci->func.p));
k = cl->p->k; k = cl->p->k;
pc = ci->u.l.savedpc; pc = ci->u.l.savedpc;
if (l_unlikely(trap)) { if (l_unlikely(trap)) {
@ -1160,60 +1167,68 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
} }
ci->u.l.trap = 1; /* assume trap is on, for now */ ci->u.l.trap = 1; /* assume trap is on, for now */
} }
base = ci->func + 1; base = ci->func.p + 1;
/* main loop of interpreter */ /* main loop of interpreter */
for (;;) { for (;;) {
Instruction i; /* instruction being executed */ Instruction i; /* instruction being executed */
StkId ra; /* instruction's A register */
vmfetch(); vmfetch();
#if 0 #if 0
/* low-level line tracing for debugging Lua */ /* low-level line tracing for debugging Lua */
printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p))); printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
#endif #endif
lua_assert(base == ci->func + 1); lua_assert(base == ci->func.p + 1);
lua_assert(base <= L->top && L->top < L->stack_last); lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p);
/* invalidate top for instructions not expecting it */ /* invalidate top for instructions not expecting it */
lua_assert(isIT(i) || (cast_void(L->top = base), 1)); lua_assert(isIT(i) || (cast_void(L->top.p = base), 1));
vmdispatch (GET_OPCODE(i)) { vmdispatch (GET_OPCODE(i)) {
vmcase(OP_MOVE) { vmcase(OP_MOVE) {
StkId ra = RA(i);
setobjs2s(L, ra, RB(i)); setobjs2s(L, ra, RB(i));
vmbreak; vmbreak;
} }
vmcase(OP_LOADI) { vmcase(OP_LOADI) {
StkId ra = RA(i);
lua_Integer b = GETARG_sBx(i); lua_Integer b = GETARG_sBx(i);
setivalue(s2v(ra), b); setivalue(s2v(ra), b);
vmbreak; vmbreak;
} }
vmcase(OP_LOADF) { vmcase(OP_LOADF) {
StkId ra = RA(i);
int b = GETARG_sBx(i); int b = GETARG_sBx(i);
setfltvalue(s2v(ra), cast_num(b)); setfltvalue(s2v(ra), cast_num(b));
vmbreak; vmbreak;
} }
vmcase(OP_LOADK) { vmcase(OP_LOADK) {
StkId ra = RA(i);
TValue *rb = k + GETARG_Bx(i); TValue *rb = k + GETARG_Bx(i);
setobj2s(L, ra, rb); setobj2s(L, ra, rb);
vmbreak; vmbreak;
} }
vmcase(OP_LOADKX) { vmcase(OP_LOADKX) {
StkId ra = RA(i);
TValue *rb; TValue *rb;
rb = k + GETARG_Ax(*pc); pc++; rb = k + GETARG_Ax(*pc); pc++;
setobj2s(L, ra, rb); setobj2s(L, ra, rb);
vmbreak; vmbreak;
} }
vmcase(OP_LOADFALSE) { vmcase(OP_LOADFALSE) {
StkId ra = RA(i);
setbfvalue(s2v(ra)); setbfvalue(s2v(ra));
vmbreak; vmbreak;
} }
vmcase(OP_LFALSESKIP) { vmcase(OP_LFALSESKIP) {
StkId ra = RA(i);
setbfvalue(s2v(ra)); setbfvalue(s2v(ra));
pc++; /* skip next instruction */ pc++; /* skip next instruction */
vmbreak; vmbreak;
} }
vmcase(OP_LOADTRUE) { vmcase(OP_LOADTRUE) {
StkId ra = RA(i);
setbtvalue(s2v(ra)); setbtvalue(s2v(ra));
vmbreak; vmbreak;
} }
vmcase(OP_LOADNIL) { vmcase(OP_LOADNIL) {
StkId ra = RA(i);
int b = GETARG_B(i); int b = GETARG_B(i);
do { do {
setnilvalue(s2v(ra++)); setnilvalue(s2v(ra++));
@ -1221,19 +1236,22 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_GETUPVAL) { vmcase(OP_GETUPVAL) {
StkId ra = RA(i);
int b = GETARG_B(i); int b = GETARG_B(i);
setobj2s(L, ra, cl->upvals[b]->v); setobj2s(L, ra, cl->upvals[b]->v.p);
vmbreak; vmbreak;
} }
vmcase(OP_SETUPVAL) { vmcase(OP_SETUPVAL) {
StkId ra = RA(i);
UpVal *uv = cl->upvals[GETARG_B(i)]; UpVal *uv = cl->upvals[GETARG_B(i)];
setobj(L, uv->v, s2v(ra)); setobj(L, uv->v.p, s2v(ra));
luaC_barrier(L, uv, s2v(ra)); luaC_barrier(L, uv, s2v(ra));
vmbreak; vmbreak;
} }
vmcase(OP_GETTABUP) { vmcase(OP_GETTABUP) {
StkId ra = RA(i);
const TValue *slot; const TValue *slot;
TValue *upval = cl->upvals[GETARG_B(i)]->v; TValue *upval = cl->upvals[GETARG_B(i)]->v.p;
TValue *rc = KC(i); TValue *rc = KC(i);
TString *key = tsvalue(rc); /* key must be a string */ TString *key = tsvalue(rc); /* key must be a string */
if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
@ -1244,6 +1262,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_GETTABLE) { vmcase(OP_GETTABLE) {
StkId ra = RA(i);
const TValue *slot; const TValue *slot;
TValue *rb = vRB(i); TValue *rb = vRB(i);
TValue *rc = vRC(i); TValue *rc = vRC(i);
@ -1258,6 +1277,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_GETI) { vmcase(OP_GETI) {
StkId ra = RA(i);
const TValue *slot; const TValue *slot;
TValue *rb = vRB(i); TValue *rb = vRB(i);
int c = GETARG_C(i); int c = GETARG_C(i);
@ -1272,6 +1292,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_GETFIELD) { vmcase(OP_GETFIELD) {
StkId ra = RA(i);
const TValue *slot; const TValue *slot;
TValue *rb = vRB(i); TValue *rb = vRB(i);
TValue *rc = KC(i); TValue *rc = KC(i);
@ -1285,7 +1306,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
} }
vmcase(OP_SETTABUP) { vmcase(OP_SETTABUP) {
const TValue *slot; const TValue *slot;
TValue *upval = cl->upvals[GETARG_A(i)]->v; TValue *upval = cl->upvals[GETARG_A(i)]->v.p;
TValue *rb = KB(i); TValue *rb = KB(i);
TValue *rc = RKC(i); TValue *rc = RKC(i);
TString *key = tsvalue(rb); /* key must be a string */ TString *key = tsvalue(rb); /* key must be a string */
@ -1297,6 +1318,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_SETTABLE) { vmcase(OP_SETTABLE) {
StkId ra = RA(i);
const TValue *slot; const TValue *slot;
TValue *rb = vRB(i); /* key (table is in 'ra') */ TValue *rb = vRB(i); /* key (table is in 'ra') */
TValue *rc = RKC(i); /* value */ TValue *rc = RKC(i); /* value */
@ -1311,6 +1333,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_SETI) { vmcase(OP_SETI) {
StkId ra = RA(i);
const TValue *slot; const TValue *slot;
int c = GETARG_B(i); int c = GETARG_B(i);
TValue *rc = RKC(i); TValue *rc = RKC(i);
@ -1325,6 +1348,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_SETFIELD) { vmcase(OP_SETFIELD) {
StkId ra = RA(i);
const TValue *slot; const TValue *slot;
TValue *rb = KB(i); TValue *rb = KB(i);
TValue *rc = RKC(i); TValue *rc = RKC(i);
@ -1337,6 +1361,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_NEWTABLE) { vmcase(OP_NEWTABLE) {
StkId ra = RA(i);
int b = GETARG_B(i); /* log2(hash size) + 1 */ int b = GETARG_B(i); /* log2(hash size) + 1 */
int c = GETARG_C(i); /* array size */ int c = GETARG_C(i); /* array size */
Table *t; Table *t;
@ -1346,7 +1371,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
if (TESTARG_k(i)) /* non-zero extra argument? */ if (TESTARG_k(i)) /* non-zero extra argument? */
c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */ c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */
pc++; /* skip extra argument */ pc++; /* skip extra argument */
L->top = ra + 1; /* correct top in case of emergency GC */ L->top.p = ra + 1; /* correct top in case of emergency GC */
t = luaH_new(L); /* memory allocation */ t = luaH_new(L); /* memory allocation */
sethvalue2s(L, ra, t); sethvalue2s(L, ra, t);
if (b != 0 || c != 0) if (b != 0 || c != 0)
@ -1355,6 +1380,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_SELF) { vmcase(OP_SELF) {
StkId ra = RA(i);
const TValue *slot; const TValue *slot;
TValue *rb = vRB(i); TValue *rb = vRB(i);
TValue *rc = RKC(i); TValue *rc = RKC(i);
@ -1384,6 +1410,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_MODK) { vmcase(OP_MODK) {
savestate(L, ci); /* in case of division by 0 */
op_arithK(L, luaV_mod, luaV_modf); op_arithK(L, luaV_mod, luaV_modf);
vmbreak; vmbreak;
} }
@ -1396,6 +1423,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_IDIVK) { vmcase(OP_IDIVK) {
savestate(L, ci); /* in case of division by 0 */
op_arithK(L, luaV_idiv, luai_numidiv); op_arithK(L, luaV_idiv, luai_numidiv);
vmbreak; vmbreak;
} }
@ -1412,6 +1440,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_SHRI) { vmcase(OP_SHRI) {
StkId ra = RA(i);
TValue *rb = vRB(i); TValue *rb = vRB(i);
int ic = GETARG_sC(i); int ic = GETARG_sC(i);
lua_Integer ib; lua_Integer ib;
@ -1421,6 +1450,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_SHLI) { vmcase(OP_SHLI) {
StkId ra = RA(i);
TValue *rb = vRB(i); TValue *rb = vRB(i);
int ic = GETARG_sC(i); int ic = GETARG_sC(i);
lua_Integer ib; lua_Integer ib;
@ -1442,6 +1472,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_MOD) { vmcase(OP_MOD) {
savestate(L, ci); /* in case of division by 0 */
op_arith(L, luaV_mod, luaV_modf); op_arith(L, luaV_mod, luaV_modf);
vmbreak; vmbreak;
} }
@ -1454,6 +1485,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_IDIV) { /* floor division */ vmcase(OP_IDIV) { /* floor division */
savestate(L, ci); /* in case of division by 0 */
op_arith(L, luaV_idiv, luai_numidiv); op_arith(L, luaV_idiv, luai_numidiv);
vmbreak; vmbreak;
} }
@ -1478,6 +1510,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_MMBIN) { vmcase(OP_MMBIN) {
StkId ra = RA(i);
Instruction pi = *(pc - 2); /* original arith. expression */ Instruction pi = *(pc - 2); /* original arith. expression */
TValue *rb = vRB(i); TValue *rb = vRB(i);
TMS tm = (TMS)GETARG_C(i); TMS tm = (TMS)GETARG_C(i);
@ -1487,6 +1520,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_MMBINI) { vmcase(OP_MMBINI) {
StkId ra = RA(i);
Instruction pi = *(pc - 2); /* original arith. expression */ Instruction pi = *(pc - 2); /* original arith. expression */
int imm = GETARG_sB(i); int imm = GETARG_sB(i);
TMS tm = (TMS)GETARG_C(i); TMS tm = (TMS)GETARG_C(i);
@ -1496,6 +1530,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_MMBINK) { vmcase(OP_MMBINK) {
StkId ra = RA(i);
Instruction pi = *(pc - 2); /* original arith. expression */ Instruction pi = *(pc - 2); /* original arith. expression */
TValue *imm = KB(i); TValue *imm = KB(i);
TMS tm = (TMS)GETARG_C(i); TMS tm = (TMS)GETARG_C(i);
@ -1505,6 +1540,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_UNM) { vmcase(OP_UNM) {
StkId ra = RA(i);
TValue *rb = vRB(i); TValue *rb = vRB(i);
lua_Number nb; lua_Number nb;
if (ttisinteger(rb)) { if (ttisinteger(rb)) {
@ -1519,6 +1555,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_BNOT) { vmcase(OP_BNOT) {
StkId ra = RA(i);
TValue *rb = vRB(i); TValue *rb = vRB(i);
lua_Integer ib; lua_Integer ib;
if (tointegerns(rb, &ib)) { if (tointegerns(rb, &ib)) {
@ -1529,6 +1566,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_NOT) { vmcase(OP_NOT) {
StkId ra = RA(i);
TValue *rb = vRB(i); TValue *rb = vRB(i);
if (l_isfalse(rb)) if (l_isfalse(rb))
setbtvalue(s2v(ra)); setbtvalue(s2v(ra));
@ -1537,21 +1575,25 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_LEN) { vmcase(OP_LEN) {
StkId ra = RA(i);
Protect(luaV_objlen(L, ra, vRB(i))); Protect(luaV_objlen(L, ra, vRB(i)));
vmbreak; vmbreak;
} }
vmcase(OP_CONCAT) { vmcase(OP_CONCAT) {
StkId ra = RA(i);
int n = GETARG_B(i); /* number of elements to concatenate */ int n = GETARG_B(i); /* number of elements to concatenate */
L->top = ra + n; /* mark the end of concat operands */ L->top.p = ra + n; /* mark the end of concat operands */
ProtectNT(luaV_concat(L, n)); ProtectNT(luaV_concat(L, n));
checkGC(L, L->top); /* 'luaV_concat' ensures correct top */ checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */
vmbreak; vmbreak;
} }
vmcase(OP_CLOSE) { vmcase(OP_CLOSE) {
StkId ra = RA(i);
Protect(luaF_close(L, ra, LUA_OK, 1)); Protect(luaF_close(L, ra, LUA_OK, 1));
vmbreak; vmbreak;
} }
vmcase(OP_TBC) { vmcase(OP_TBC) {
StkId ra = RA(i);
/* create new to-be-closed upvalue */ /* create new to-be-closed upvalue */
halfProtect(luaF_newtbcupval(L, ra)); halfProtect(luaF_newtbcupval(L, ra));
vmbreak; vmbreak;
@ -1561,6 +1603,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_EQ) { vmcase(OP_EQ) {
StkId ra = RA(i);
int cond; int cond;
TValue *rb = vRB(i); TValue *rb = vRB(i);
Protect(cond = luaV_equalobj(L, s2v(ra), rb)); Protect(cond = luaV_equalobj(L, s2v(ra), rb));
@ -1576,6 +1619,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_EQK) { vmcase(OP_EQK) {
StkId ra = RA(i);
TValue *rb = KB(i); TValue *rb = KB(i);
/* basic types do not use '__eq'; we can use raw equality */ /* basic types do not use '__eq'; we can use raw equality */
int cond = luaV_rawequalobj(s2v(ra), rb); int cond = luaV_rawequalobj(s2v(ra), rb);
@ -1583,6 +1627,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_EQI) { vmcase(OP_EQI) {
StkId ra = RA(i);
int cond; int cond;
int im = GETARG_sB(i); int im = GETARG_sB(i);
if (ttisinteger(s2v(ra))) if (ttisinteger(s2v(ra)))
@ -1611,11 +1656,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_TEST) { vmcase(OP_TEST) {
StkId ra = RA(i);
int cond = !l_isfalse(s2v(ra)); int cond = !l_isfalse(s2v(ra));
docondjump(); docondjump();
vmbreak; vmbreak;
} }
vmcase(OP_TESTSET) { vmcase(OP_TESTSET) {
StkId ra = RA(i);
TValue *rb = vRB(i); TValue *rb = vRB(i);
if (l_isfalse(rb) == GETARG_k(i)) if (l_isfalse(rb) == GETARG_k(i))
pc++; pc++;
@ -1626,11 +1673,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_CALL) { vmcase(OP_CALL) {
StkId ra = RA(i);
CallInfo *newci; CallInfo *newci;
int b = GETARG_B(i); int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1; int nresults = GETARG_C(i) - 1;
if (b != 0) /* fixed number of arguments? */ if (b != 0) /* fixed number of arguments? */
L->top = ra + b; /* top signals number of arguments */ L->top.p = ra + b; /* top signals number of arguments */
/* else previous instruction set top */ /* else previous instruction set top */
savepc(L); /* in case of errors */ savepc(L); /* in case of errors */
if ((newci = luaD_precall(L, ra, nresults)) == NULL) if ((newci = luaD_precall(L, ra, nresults)) == NULL)
@ -1642,54 +1690,57 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_TAILCALL) { vmcase(OP_TAILCALL) {
StkId ra = RA(i);
int b = GETARG_B(i); /* number of arguments + 1 (function) */ int b = GETARG_B(i); /* number of arguments + 1 (function) */
int n; /* number of results when calling a C function */ int n; /* number of results when calling a C function */
int nparams1 = GETARG_C(i); int nparams1 = GETARG_C(i);
/* delta is virtual 'func' - real 'func' (vararg functions) */ /* delta is virtual 'func' - real 'func' (vararg functions) */
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
if (b != 0) if (b != 0)
L->top = ra + b; L->top.p = ra + b;
else /* previous instruction set top */ else /* previous instruction set top */
b = cast_int(L->top - ra); b = cast_int(L->top.p - ra);
savepc(ci); /* several calls here can raise errors */ savepc(ci); /* several calls here can raise errors */
if (TESTARG_k(i)) { if (TESTARG_k(i)) {
luaF_closeupval(L, base); /* close upvalues from current call */ luaF_closeupval(L, base); /* close upvalues from current call */
lua_assert(L->tbclist < base); /* no pending tbc variables */ lua_assert(L->tbclist.p < base); /* no pending tbc variables */
lua_assert(base == ci->func + 1); lua_assert(base == ci->func.p + 1);
} }
if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */ if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */
goto startfunc; /* execute the callee */ goto startfunc; /* execute the callee */
else { /* C function? */ else { /* C function? */
ci->func -= delta; /* restore 'func' (if vararg) */ ci->func.p -= delta; /* restore 'func' (if vararg) */
luaD_poscall(L, ci, n); /* finish caller */ luaD_poscall(L, ci, n); /* finish caller */
updatetrap(ci); /* 'luaD_poscall' can change hooks */ updatetrap(ci); /* 'luaD_poscall' can change hooks */
goto ret; /* caller returns after the tail call */ goto ret; /* caller returns after the tail call */
} }
} }
vmcase(OP_RETURN) { vmcase(OP_RETURN) {
StkId ra = RA(i);
int n = GETARG_B(i) - 1; /* number of results */ int n = GETARG_B(i) - 1; /* number of results */
int nparams1 = GETARG_C(i); int nparams1 = GETARG_C(i);
if (n < 0) /* not fixed? */ if (n < 0) /* not fixed? */
n = cast_int(L->top - ra); /* get what is available */ n = cast_int(L->top.p - ra); /* get what is available */
savepc(ci); savepc(ci);
if (TESTARG_k(i)) { /* may there be open upvalues? */ if (TESTARG_k(i)) { /* may there be open upvalues? */
ci->u2.nres = n; /* save number of returns */ ci->u2.nres = n; /* save number of returns */
if (L->top < ci->top) if (L->top.p < ci->top.p)
L->top = ci->top; L->top.p = ci->top.p;
luaF_close(L, base, CLOSEKTOP, 1); luaF_close(L, base, CLOSEKTOP, 1);
updatetrap(ci); updatetrap(ci);
updatestack(ci); updatestack(ci);
} }
if (nparams1) /* vararg function? */ if (nparams1) /* vararg function? */
ci->func -= ci->u.l.nextraargs + nparams1; ci->func.p -= ci->u.l.nextraargs + nparams1;
L->top = ra + n; /* set call for 'luaD_poscall' */ L->top.p = ra + n; /* set call for 'luaD_poscall' */
luaD_poscall(L, ci, n); luaD_poscall(L, ci, n);
updatetrap(ci); /* 'luaD_poscall' can change hooks */ updatetrap(ci); /* 'luaD_poscall' can change hooks */
goto ret; goto ret;
} }
vmcase(OP_RETURN0) { vmcase(OP_RETURN0) {
if (l_unlikely(L->hookmask)) { if (l_unlikely(L->hookmask)) {
L->top = ra; StkId ra = RA(i);
L->top.p = ra;
savepc(ci); savepc(ci);
luaD_poscall(L, ci, 0); /* no hurry... */ luaD_poscall(L, ci, 0); /* no hurry... */
trap = 1; trap = 1;
@ -1697,15 +1748,16 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
else { /* do the 'poscall' here */ else { /* do the 'poscall' here */
int nres; int nres;
L->ci = ci->previous; /* back to caller */ L->ci = ci->previous; /* back to caller */
L->top = base - 1; L->top.p = base - 1;
for (nres = ci->nresults; l_unlikely(nres > 0); nres--) for (nres = ci->nresults; l_unlikely(nres > 0); nres--)
setnilvalue(s2v(L->top++)); /* all results are nil */ setnilvalue(s2v(L->top.p++)); /* all results are nil */
} }
goto ret; goto ret;
} }
vmcase(OP_RETURN1) { vmcase(OP_RETURN1) {
if (l_unlikely(L->hookmask)) { if (l_unlikely(L->hookmask)) {
L->top = ra + 1; StkId ra = RA(i);
L->top.p = ra + 1;
savepc(ci); savepc(ci);
luaD_poscall(L, ci, 1); /* no hurry... */ luaD_poscall(L, ci, 1); /* no hurry... */
trap = 1; trap = 1;
@ -1714,12 +1766,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
int nres = ci->nresults; int nres = ci->nresults;
L->ci = ci->previous; /* back to caller */ L->ci = ci->previous; /* back to caller */
if (nres == 0) if (nres == 0)
L->top = base - 1; /* asked for no results */ L->top.p = base - 1; /* asked for no results */
else { else {
StkId ra = RA(i);
setobjs2s(L, base - 1, ra); /* at least this result */ setobjs2s(L, base - 1, ra); /* at least this result */
L->top = base; L->top.p = base;
for (; l_unlikely(nres > 1); nres--) for (; l_unlikely(nres > 1); nres--)
setnilvalue(s2v(L->top++)); /* complete missing results */ setnilvalue(s2v(L->top.p++)); /* complete missing results */
} }
} }
ret: /* return from a Lua function */ ret: /* return from a Lua function */
@ -1731,6 +1784,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
} }
} }
vmcase(OP_FORLOOP) { vmcase(OP_FORLOOP) {
StkId ra = RA(i);
if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ if (ttisinteger(s2v(ra + 2))) { /* integer loop? */
lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1))); lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1)));
if (count > 0) { /* still more iterations? */ if (count > 0) { /* still more iterations? */
@ -1749,12 +1803,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_FORPREP) { vmcase(OP_FORPREP) {
StkId ra = RA(i);
savestate(L, ci); /* in case of errors */ savestate(L, ci); /* in case of errors */
if (forprep(L, ra)) if (forprep(L, ra))
pc += GETARG_Bx(i) + 1; /* skip the loop */ pc += GETARG_Bx(i) + 1; /* skip the loop */
vmbreak; vmbreak;
} }
vmcase(OP_TFORPREP) { vmcase(OP_TFORPREP) {
StkId ra = RA(i);
/* create to-be-closed upvalue (if needed) */ /* create to-be-closed upvalue (if needed) */
halfProtect(luaF_newtbcupval(L, ra + 3)); halfProtect(luaF_newtbcupval(L, ra + 3));
pc += GETARG_Bx(i); pc += GETARG_Bx(i);
@ -1763,7 +1819,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
goto l_tforcall; goto l_tforcall;
} }
vmcase(OP_TFORCALL) { vmcase(OP_TFORCALL) {
l_tforcall: l_tforcall: {
StkId ra = RA(i);
/* 'ra' has the iterator function, 'ra + 1' has the state, /* 'ra' has the iterator function, 'ra + 1' has the state,
'ra + 2' has the control variable, and 'ra + 3' has the 'ra + 2' has the control variable, and 'ra + 3' has the
to-be-closed variable. The call will use the stack after to-be-closed variable. The call will use the stack after
@ -1771,29 +1828,31 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
*/ */
/* push function, state, and control variable */ /* push function, state, and control variable */
memcpy(ra + 4, ra, 3 * sizeof(*ra)); memcpy(ra + 4, ra, 3 * sizeof(*ra));
L->top = ra + 4 + 3; L->top.p = ra + 4 + 3;
ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
updatestack(ci); /* stack may have changed */ updatestack(ci); /* stack may have changed */
i = *(pc++); /* go to next instruction */ i = *(pc++); /* go to next instruction */
lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i));
goto l_tforloop; goto l_tforloop;
} }}
vmcase(OP_TFORLOOP) { vmcase(OP_TFORLOOP) {
l_tforloop: l_tforloop: {
StkId ra = RA(i);
if (!ttisnil(s2v(ra + 4))) { /* continue loop? */ if (!ttisnil(s2v(ra + 4))) { /* continue loop? */
setobjs2s(L, ra + 2, ra + 4); /* save control variable */ setobjs2s(L, ra + 2, ra + 4); /* save control variable */
pc -= GETARG_Bx(i); /* jump back */ pc -= GETARG_Bx(i); /* jump back */
} }
vmbreak; vmbreak;
} }}
vmcase(OP_SETLIST) { vmcase(OP_SETLIST) {
StkId ra = RA(i);
int n = GETARG_B(i); int n = GETARG_B(i);
unsigned int last = GETARG_C(i); unsigned int last = GETARG_C(i);
Table *h = hvalue(s2v(ra)); Table *h = hvalue(s2v(ra));
if (n == 0) if (n == 0)
n = cast_int(L->top - ra) - 1; /* get up to the top */ n = cast_int(L->top.p - ra) - 1; /* get up to the top */
else else
L->top = ci->top; /* correct top in case of emergency GC */ L->top.p = ci->top.p; /* correct top in case of emergency GC */
last += n; last += n;
if (TESTARG_k(i)) { if (TESTARG_k(i)) {
last += GETARG_Ax(*pc) * (MAXARG_C + 1); last += GETARG_Ax(*pc) * (MAXARG_C + 1);
@ -1810,12 +1869,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_CLOSURE) { vmcase(OP_CLOSURE) {
StkId ra = RA(i);
Proto *p = cl->p->p[GETARG_Bx(i)]; Proto *p = cl->p->p[GETARG_Bx(i)];
halfProtect(pushclosure(L, p, cl->upvals, base, ra)); halfProtect(pushclosure(L, p, cl->upvals, base, ra));
checkGC(L, ra + 1); checkGC(L, ra + 1);
vmbreak; vmbreak;
} }
vmcase(OP_VARARG) { vmcase(OP_VARARG) {
StkId ra = RA(i);
int n = GETARG_C(i) - 1; /* required results */ int n = GETARG_C(i) - 1; /* required results */
Protect(luaT_getvarargs(L, ci, ra, n)); Protect(luaT_getvarargs(L, ci, ra, n));
vmbreak; vmbreak;

View File

@ -110,6 +110,11 @@ typedef enum {
luaC_barrierback(L, gcvalue(t), v); } luaC_barrierback(L, gcvalue(t), v); }
/*
** Shift right is the same as shift left with a negative 'y'
*/
#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y))
LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2);