1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 08:47:17 +01:00

Compare commits

...

12 Commits

Author SHA1 Message Date
Sandu Liviu Catalin
69a4d305a5 Update MaxmindDB to current git. 2021-08-22 20:15:19 +03:00
Sandu Liviu Catalin
0008869ddd Update cppfmt to current git. 2021-08-22 20:11:41 +03:00
Sandu Liviu Catalin
fa4644d00f Update ZMQ to current git. 2021-08-22 20:09:16 +03:00
Sandu Liviu Catalin
b78b3e8ede Change MySQL header include path. 2021-08-22 19:16:31 +03:00
Sandu Liviu Catalin
21c9797d1a Update CMakeLists.txt 2021-08-22 19:05:32 +03:00
Sandu Liviu Catalin
94f4459d5b Update cmake.yml 2021-08-22 18:59:20 +03:00
Sandu Liviu Catalin
88b084422d Update cmake.yml 2021-08-22 18:46:19 +03:00
Sandu Liviu Catalin
954f28c2dc Update Common.cpp 2021-08-22 18:40:54 +03:00
Sandu Liviu Catalin
9ca62af730 Update CMakeLists.txt 2021-08-22 18:33:18 +03:00
Sandu Liviu Catalin
3c6c9bc47b Deal with file name case sensitivity issue. 2021-08-22 18:29:10 +03:00
Sandu Liviu Catalin
acadc852c4 Update ICMPv4PacketImpl.h 2021-08-22 18:07:20 +03:00
Sandu Liviu Catalin
7a3d92d1d1 Update POCO to 1.11.0 2021-08-22 18:07:06 +03:00
630 changed files with 29878 additions and 35143 deletions

View File

@ -31,6 +31,12 @@ jobs:
- name: Install Postgre SQL - name: Install Postgre SQL
run: sudo apt-get -y install libpq-dev run: sudo apt-get -y install libpq-dev
- name: Install Sodium
run: sudo apt-get -y install libsodium-dev
- name: Install GNUTLS
run: sudo apt-get -y install libgnutls28-dev
- name: Install ZLib - name: Install ZLib
run: sudo apt-get -y install zlib1g-dev run: sudo apt-get -y install zlib1g-dev

2
.gitignore vendored
View File

@ -7,7 +7,7 @@
/bin/* /bin/*
# Exclude # Exclude
!/bin/demo !/vendor
# Hidden files and folders # Hidden files and folders
.* .*

View File

@ -149,7 +149,7 @@ void SqThrowLastF(const SQChar * msg, ...)
// Now it's safe to throw the error // Now it's safe to throw the error
SqThrowF(fmt::runtime("{} [{}]"), b.Data(), message); SqThrowF(fmt::runtime("{} [{}]"), b.Data(), message);
#else #else
SqThrowF("{} [{}]", b.Data(), std::strerror(errno)); SqThrowF(fmt::runtime("{} [{}]"), b.Data(), std::strerror(errno));
#endif // SQMOD_OS_WINDOWS #endif // SQMOD_OS_WINDOWS
} }

View File

@ -18,7 +18,7 @@
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
#ifdef SQMOD_POCO_HAS_MYSQL #ifdef SQMOD_POCO_HAS_MYSQL
#include <mysql.h> #include <mysql/mysql.h>
#else #else
#error Enable MySQL support in order to compile this library. #error Enable MySQL support in order to compile this library.
#endif #endif

View File

@ -13,7 +13,7 @@
#ifdef SQMOD_POCO_HAS_MYSQL #ifdef SQMOD_POCO_HAS_MYSQL
#include <Poco/Data/MySQL/Connector.h> #include <Poco/Data/MySQL/Connector.h>
// Used for string escape functionality // Used for string escape functionality
#include <mysql.h> #include <mysql/mysql.h>
#endif #endif
#ifdef SQMOD_POCO_HAS_POSTGRESQL #ifdef SQMOD_POCO_HAS_POSTGRESQL
#include <Poco/Data/PostgreSQL/Connector.h> #include <Poco/Data/PostgreSQL/Connector.h>

View File

@ -86,7 +86,7 @@ set(FMT_CAN_MODULE OFF)
if (CMAKE_CXX_STANDARD GREATER 17 AND if (CMAKE_CXX_STANDARD GREATER 17 AND
# msvc 16.10-pre4 # msvc 16.10-pre4
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035) MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
set(FMT_CAN_MODULE ON) set(FMT_CAN_MODULE OFF)
endif () endif ()
if (NOT FMT_CAN_MODULE) if (NOT FMT_CAN_MODULE)
set(FMT_MODULE OFF) set(FMT_MODULE OFF)

View File

@ -387,6 +387,9 @@
Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_. Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_.
* The undocumented support for specializing ``formatter`` for pointer types
has been removed.
* Fixed ``fmt::formatted_size`` with format string compilation * Fixed ``fmt::formatted_size`` with format string compilation
(`#2141 <https://github.com/fmtlib/fmt/pull/2141>`_, (`#2141 <https://github.com/fmtlib/fmt/pull/2141>`_,
`#2161 <https://github.com/fmtlib/fmt/pull/2161>`_). `#2161 <https://github.com/fmtlib/fmt/pull/2161>`_).

View File

@ -10,7 +10,7 @@
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg .. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v .. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true
:target: https://ci.appveyor.com/project/vitaut/fmt :target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg .. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
@ -143,10 +143,10 @@ Output::
.. code:: c++ .. code:: c++
std::string s = fmt::format(FMT_STRING("{:d}"), "I am not a number"); std::string s = fmt::format("{:d}", "I am not a number");
This gives a compile-time error because ``d`` is an invalid format specifier for This gives a compile-time error in C++20 because ``d`` is an invalid format
a string. specifier for a string.
**Write a file from a single thread** **Write a file from a single thread**

28
vendor/Fmt/doc/_static/breathe.css vendored Normal file
View File

@ -0,0 +1,28 @@
/* -- breathe specific styles ----------------------------------------------- */
/* So enum value descriptions are displayed inline to the item */
.breatheenumvalues li tt + p {
display: inline;
}
/* So parameter descriptions are displayed inline to the item */
.breatheparameterlist li tt + p {
display: inline;
}
.container .breathe-sectiondef {
width: inherit;
}
.github-btn {
border: 0;
overflow: hidden;
}
.jumbotron {
background-size: 100% 4px;
background-repeat: repeat-y;
color: white;
text-align: center;
}

View File

@ -350,8 +350,8 @@ allocator::
custom_string vformat(custom_allocator alloc, fmt::string_view format_str, custom_string vformat(custom_allocator alloc, fmt::string_view format_str,
fmt::format_args args) { fmt::format_args args) {
custom_memory_buffer buf(alloc); auto buf = custom_memory_buffer(alloc);
fmt::vformat_to(buf, format_str, args); fmt::vformat_to(std::back_inserter(buf), format_str, args);
return custom_string(buf.data(), buf.size(), alloc); return custom_string(buf.data(), buf.size(), alloc);
} }

View File

@ -1,544 +0,0 @@
.. _string-formatting-api:
*************
API Reference
*************
The {fmt} library API consists of the following parts:
* :ref:`fmt/core.h <core-api>`: the core API providing main formatting functions
for ``char``/UTF-8 with compile-time checks and minimal dependencies
* :ref:`fmt/format.h <format-api>`: the full format API providing additional
formatting functions and locale support
* :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
* :ref:`fmt/compile.h <compile-api>`: format string compilation
* :ref:`fmt/color.h <color-api>`: terminal color and text style
* :ref:`fmt/os.h <os-api>`: system APIs
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
* :ref:`fmt/xchar.h <xchar-api>`: optional ``wchar_t`` support
All functions and types provided by the library reside in namespace ``fmt`` and
macros have prefix ``FMT_``.
.. _core-api:
Core API
========
``fmt/core.h`` defines the core API which provides main formatting functions for
``char``/UTF-8 with compile-time checks. It has minimal include dependencies for
better compile times. This header is only beneficial when using {fmt} as a
library and not in the header-only mode.
The following functions use :ref:`format string syntax <syntax>`
similar to that of Python's `str.format
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
They take *fmt* and *args* as arguments.
*fmt* is a format string that contains literal text and replacement
fields surrounded by braces ``{}``. The fields are replaced with formatted
arguments in the resulting string. A function taking *fmt* doesn't
participate in an overload resolution if the latter is not a string.
*args* is an argument list representing objects to be formatted.
.. _format:
.. doxygenfunction:: format(format_string<T...> fmt, T&&... args) -> std::string
.. doxygenfunction:: vformat(string_view fmt, format_args args) -> std::string
.. doxygenfunction:: format_to(OutputIt out, format_string<T...> fmt, T&&... args) -> OutputIt
.. doxygenfunction:: format_to_n(OutputIt out, size_t n, format_string<T...> fmt, const T&... args) -> format_to_n_result<OutputIt>
.. doxygenfunction:: formatted_size(format_string<T...> fmt, T&&... args) -> size_t
.. doxygenstruct:: fmt::format_to_n_result
:members:
.. _print:
.. doxygenfunction:: fmt::print(format_string<T...> fmt, T&&... args)
.. doxygenfunction:: vprint(string_view fmt, format_args args)
.. doxygenfunction:: print(std::FILE *f, format_string<T...> fmt, T&&... args)
.. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args)
Compile-time Format String Checks
---------------------------------
Compile-time checks are enabled when using ``FMT_STRING``. They support built-in
and string types as well as user-defined types with ``constexpr`` ``parse``
functions in their ``formatter`` specializations.
.. doxygendefine:: FMT_STRING
To force the use of compile-time checks, define the preprocessor variable
``FMT_ENFORCE_COMPILE_STRING``. When set, functions accepting ``FMT_STRING``
will fail to compile with regular strings. Runtime-checked
formatting is still possible using ``fmt::vformat``, ``fmt::vprint``, etc.
Named Arguments
---------------
.. doxygenfunction:: fmt::arg(const S&, const T&)
Named arguments are not supported in compile-time checks at the moment.
Argument Lists
--------------
You can create your own formatting function with compile-time checks and small
binary footprint, for example (https://godbolt.org/z/oba4Mc):
.. code:: c++
#include <fmt/format.h>
void vlog(const char* file, int line, fmt::string_view format,
fmt::format_args args) {
fmt::print("{}: {}: ", file, line);
fmt::vprint(format, args);
}
template <typename S, typename... Args>
void log(const char* file, int line, const S& format, Args&&... args) {
vlog(file, line, format,
fmt::make_args_checked<Args...>(format, args...));
}
#define MY_LOG(format, ...) \
log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__)
MY_LOG("invalid squishiness: {}", 42);
Note that ``vlog`` is not parameterized on argument types which improves compile
times and reduces binary code size compared to a fully parameterized version.
.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t<Args>&...)
.. doxygenfunction:: fmt::make_format_args(const Args&...)
.. doxygenclass:: fmt::format_arg_store
:members:
.. doxygenclass:: fmt::dynamic_format_arg_store
:members:
.. doxygenclass:: fmt::basic_format_args
:members:
.. doxygentypedef:: fmt::format_args
.. doxygenclass:: fmt::basic_format_arg
:members:
.. doxygenclass:: fmt::basic_format_context
:members:
.. doxygentypedef:: fmt::format_context
Compatibility
-------------
.. doxygenclass:: fmt::basic_string_view
:members:
.. doxygentypedef:: fmt::string_view
Locale
------
All formatting is locale-independent by default. Use the ``'L'`` format
specifier to insert the appropriate number separator characters from the
locale::
#include <fmt/core.h>
#include <locale>
std::locale::global(std::locale("en_US.UTF-8"));
auto s = fmt::format("{:L}", 1000000); // s == "1,000,000"
.. _format-api:
Format API
==========
``fmt/format.h`` defines the full format API providing additional formatting
functions and locale support.
.. _udt:
Formatting User-defined Types
-----------------------------
To make a user-defined type formattable, specialize the ``formatter<T>`` struct
template and implement ``parse`` and ``format`` methods::
#include <fmt/format.h>
struct point { double x, y; };
template <> struct fmt::formatter<point> {
// Presentation format: 'f' - fixed, 'e' - exponential.
char presentation = 'f';
// Parses format specifications of the form ['f' | 'e'].
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
// [ctx.begin(), ctx.end()) is a character range that contains a part of
// the format string starting from the format specifications to be parsed,
// e.g. in
//
// fmt::format("{:f} - point of interest", point{1, 2});
//
// the range will contain "f} - point of interest". The formatter should
// parse specifiers until '}' or the end of the range. In this example
// the formatter should parse the 'f' specifier and return an iterator
// pointing to '}'.
// Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
// Check if reached the end of the range:
if (it != end && *it != '}')
throw format_error("invalid format");
// Return an iterator past the end of the parsed range:
return it;
}
// Formats the point p using the parsed format specification (presentation)
// stored in this formatter.
template <typename FormatContext>
auto format(const point& p, FormatContext& ctx) -> decltype(ctx.out()) {
// ctx.out() is an output iterator to write to.
return format_to(
ctx.out(),
presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})",
p.x, p.y);
}
};
Then you can pass objects of type ``point`` to any formatting function::
point p = {1, 2};
std::string s = fmt::format("{:f}", p);
// s == "(1.0, 2.0)"
You can also reuse existing formatters via inheritance or composition, for
example::
enum class color {red, green, blue};
template <> struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext& ctx) {
string_view name = "unknown";
switch (c) {
case color::red: name = "red"; break;
case color::green: name = "green"; break;
case color::blue: name = "blue"; break;
}
return formatter<string_view>::format(name, ctx);
}
};
Since ``parse`` is inherited from ``formatter<string_view>`` it will recognize
all string format specifications, for example
.. code-block:: c++
fmt::format("{:>10}", color::blue)
will return ``" blue"``.
You can also write a formatter for a hierarchy of classes::
#include <type_traits>
#include <fmt/format.h>
struct A {
virtual ~A() {}
virtual std::string name() const { return "A"; }
};
struct B : A {
virtual std::string name() const { return "B"; }
};
template <typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> {
template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) {
return fmt::formatter<std::string>::format(a.name(), ctx);
}
};
int main() {
B b;
A& a = b;
fmt::print("{}", a); // prints "B"
}
If a type provides both a ``formatter`` specialization and an implicit
conversion to a formattable type, the specialization takes precedence over the
conversion.
.. doxygenclass:: fmt::basic_format_parse_context
:members:
Literal-based API
-----------------
The following user-defined literals are defined in ``fmt/format.h``.
.. doxygenfunction:: operator""_format(const char *s, size_t n) -> detail::udl_formatter<char>
.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg<char>
Utilities
---------
.. doxygenfunction:: fmt::ptr(T p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::to_string(const T &value) -> std::string
.. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view<Char>
.. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>
.. doxygenfunction:: fmt::join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>
.. doxygenclass:: fmt::detail::buffer
:members:
.. doxygenclass:: fmt::basic_memory_buffer
:protected-members:
:members:
System Errors
-------------
{fmt} does not use ``errno`` to communicate errors to the user, but it may call
system functions which set ``errno``. Users should not make any assumptions
about the value of ``errno`` being preserved by library functions.
.. doxygenfunction:: fmt::system_error
.. doxygenfunction:: fmt::format_system_error
Custom Allocators
-----------------
The {fmt} library supports custom dynamic memory allocators.
A custom allocator class can be specified as a template argument to
:class:`fmt::basic_memory_buffer`::
using custom_memory_buffer =
fmt::basic_memory_buffer<char, fmt::inline_buffer_size, custom_allocator>;
It is also possible to write a formatting function that uses a custom
allocator::
using custom_string =
std::basic_string<char, std::char_traits<char>, custom_allocator>;
custom_string vformat(custom_allocator alloc, fmt::string_view format_str,
fmt::format_args args) {
custom_memory_buffer buf(alloc);
fmt::vformat_to(buf, format_str, args);
return custom_string(buf.data(), buf.size(), alloc);
}
template <typename ...Args>
inline custom_string format(custom_allocator alloc,
fmt::string_view format_str,
const Args& ... args) {
return vformat(alloc, format_str, fmt::make_format_args(args...));
}
The allocator will be used for the output container only. Formatting functions
normally don't do any allocations for built-in and string types except for
non-default floating-point formatting that occasionally falls back on
``sprintf``.
.. _ranges-api:
Ranges and Tuple Formatting
===========================
The library also supports convenient formatting of ranges and tuples::
#include <fmt/ranges.h>
std::tuple<char, int, float> t{'a', 1, 2.0f};
// Prints "('a', 1, 2.0)"
fmt::print("{}", t);
NOTE: currently, the overload of ``fmt::join`` for iterables exists in the main
``format.h`` header, but expect this to change in the future.
Using ``fmt::join``, you can separate tuple elements with a custom separator::
#include <fmt/ranges.h>
std::tuple<int, char> t = {1, 'a'};
// Prints "1, a"
fmt::print("{}", fmt::join(t, ", "));
.. _chrono-api:
Date and Time Formatting
========================
``fmt/chrono.h`` provides formatters for
* `std::chrono::duration <https://en.cppreference.com/w/cpp/chrono/duration>`_
* `std::chrono::time_point
<https://en.cppreference.com/w/cpp/chrono/time_point>`_
* `std::tm <https://en.cppreference.com/w/cpp/chrono/c/tm>`_
The format syntax is described in :ref:`chrono-specs`.
**Example**::
#include <fmt/chrono.h>
int main() {
std::time_t t = std::time(nullptr);
// Prints "The date is 2020-11-07." (with the current date):
fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
using namespace std::literals::chrono_literals;
// Prints "Default format: 42s 100ms":
fmt::print("Default format: {} {}\n", 42s, 100ms);
// Prints "strftime-like format: 03:15:30":
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
}
.. doxygenfunction:: localtime(std::time_t time)
.. doxygenfunction:: gmtime(std::time_t time)
.. _compile-api:
Format string compilation
=========================
``fmt/compile.h`` provides format string compilation support when using
``FMT_COMPILE``. Format strings are parsed, checked and converted into efficient
formatting code at compile-time. This supports arguments of built-in and string
types as well as user-defined types with ``constexpr`` ``parse`` functions in
their ``formatter`` specializations. Format string compilation can generate more
binary code compared to the default API and is only recommended in places where
formatting is a performance bottleneck.
.. doxygendefine:: FMT_COMPILE
.. _color-api:
Terminal color and text style
=============================
``fmt/color.h`` provides support for terminal color and text style output.
.. doxygenfunction:: print(const text_style &ts, const S &format_str, const Args&... args)
.. doxygenfunction:: fg(detail::color_type)
.. doxygenfunction:: bg(detail::color_type)
.. _os-api:
System APIs
===========
.. doxygenclass:: fmt::ostream
:members:
.. doxygenfunction:: fmt::windows_error
:members:
.. _ostream-api:
``std::ostream`` Support
========================
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
user-defined types that have an overloaded insertion operator (``operator<<``)::
#include <fmt/ostream.h>
class date {
int year_, month_, day_;
public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream& operator<<(std::ostream& os, const date& d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
std::string s = fmt::format("The date is {}", date(2012, 12, 9));
// s == "The date is 2012-12-9"
{fmt} only supports insertion operators that are defined in the same namespaces
as the types they format and can be found with the argument-dependent lookup.
.. doxygenfunction:: print(std::basic_ostream<Char> &os, const S &format_str, Args&&... args)
.. _printf-api:
``printf`` Formatting
=====================
The header ``fmt/printf.h`` provides ``printf``-like formatting functionality.
The following functions use `printf format string syntax
<https://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html>`_ with
the POSIX extension for positional arguments. Unlike their standard
counterparts, the ``fmt`` functions are type-safe and throw an exception if an
argument type doesn't match its format specification.
.. doxygenfunction:: printf(const S &format_str, const T&... args)
.. doxygenfunction:: fprintf(std::FILE *f, const S &fmt, const T&... args) -> int
.. doxygenfunction:: sprintf(const S&, const T&...)
.. _xchar-api:
``wchar_t`` Support
===================
The optional header ``fmt/wchar_t.h`` provides support for ``wchar_t`` and
exotic character types.
.. doxygenstruct:: fmt::is_char
.. doxygentypedef:: fmt::wstring_view
.. doxygentypedef:: fmt::wformat_context
.. doxygenfunction:: fmt::to_wstring(const T &value)
Compatibility with C++20 ``std::format``
========================================
{fmt} implements nearly all of the `C++20 formatting library
<https://en.cppreference.com/w/cpp/utility/format>`_ with the following
differences:
* Names are defined in the ``fmt`` namespace instead of ``std`` to avoid
collisions with standard library implementations.
* Width calculation doesn't use grapheme clusterization. The latter has been
implemented in a separate branch but hasn't been integrated yet.
* Most C++20 chrono types are not supported yet.

View File

@ -1,10 +0,0 @@
########
Contents
########
.. toctree::
:maxdepth: 2
usage
api
syntax

View File

@ -1,198 +0,0 @@
Overview
========
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
.. raw:: html
<div class="panel panel-default">
<div class="panel-heading">What users say:</div>
<div class="panel-body">
Thanks for creating this library. Its been a hole in C++ for
a long time. Ive used both <code>boost::format</code> and
<code>loki::SPrintf</code>, and neither felt like the right answer.
This does.
</div>
</div>
.. _format-api-intro:
Format API
----------
The format API is similar in spirit to the C ``printf`` family of function but
is safer, simpler and several times `faster
<https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
than common standard library implementations.
The `format string syntax <syntax.html>`_ is similar to the one used by
`str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_ in
Python:
.. code:: c++
std::string s = fmt::format("The answer is {}.", 42);
The ``fmt::format`` function returns a string "The answer is 42.". You can use
``fmt::memory_buffer`` to avoid constructing ``std::string``:
.. code:: c++
auto out = fmt::memory_buffer();
format_to(std::back_inserter(out),
"For a moment, {} happened.", "nothing");
auto data = out.data(); // pointer to the formatted data
auto size = out.size(); // size of the formatted data
The ``fmt::print`` function performs formatting and writes the result to a stream:
.. code:: c++
fmt::print(stderr, "System error code = {}\n", errno);
If you omit the file argument the function will print to ``stdout``:
.. code:: c++
fmt::print("Don't {}\n", "panic");
The format API also supports positional arguments useful for localization:
.. code:: c++
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
You can pass named arguments with ``fmt::arg``:
.. code:: c++
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
fmt::arg("name", "World"), fmt::arg("number", 42));
If your compiler supports C++11 user-defined literals, the suffix ``_a`` offers
an alternative, slightly terser syntax for named arguments:
.. code:: c++
using namespace fmt::literals;
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
"name"_a="World", "number"_a=42);
.. _safety:
Safety
------
The library is fully type safe, automatic memory management prevents buffer
overflow, errors in format strings are reported using exceptions or at compile
time. For example, the code
.. code:: c++
fmt::format("The answer is {:d}", "forty-two");
throws the ``format_error`` exception because the argument ``"forty-two"`` is a
string while the format code ``d`` only applies to integers.
The code
.. code:: c++
format(FMT_STRING("The answer is {:d}"), "forty-two");
reports a compile-time error on compilers that support relaxed ``constexpr``.
See `here <api.html#c.fmt>`_ for details.
The following code
.. code:: c++
fmt::format("Cyrillic letter {}", L'\x42e');
produces a compile-time error because wide character ``L'\x42e'`` cannot be
formatted into a narrow string. For comparison, writing a wide character to
``std::ostream`` results in its numeric value being written to the stream
(i.e. 1070 instead of letter 'ю' which is represented by ``L'\x42e'`` if we
use Unicode) which is rarely desirable.
Compact Binary Code
-------------------
The library produces compact per-call compiled code. For example
(`godbolt <https://godbolt.org/g/TZU4KF>`_),
.. code:: c++
#include <fmt/core.h>
int main() {
fmt::print("The answer is {}.", 42);
}
compiles to just
.. code:: asm
main: # @main
sub rsp, 24
mov qword ptr [rsp], 42
mov rcx, rsp
mov edi, offset .L.str
mov esi, 17
mov edx, 1
call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args)
xor eax, eax
add rsp, 24
ret
.L.str:
.asciz "The answer is {}."
.. _portability:
Portability
-----------
The library is highly portable and relies only on a small set of C++11 features:
* variadic templates
* type traits
* rvalue references
* decltype
* trailing return types
* deleted functions
* alias templates
These are available in GCC 4.8, Clang 3.4, MSVC 19.0 (2015) and more recent
compiler version. For older compilers use {fmt} `version 4.x
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which is maintained and
only requires C++98.
The output of all formatting functions is consistent across platforms.
For example,
.. code::
fmt::print("{}", std::numeric_limits<double>::infinity());
always prints ``inf`` while the output of ``printf`` is platform-dependent.
.. _ease-of-use:
Ease of Use
-----------
{fmt} has a small self-contained code base with the core library consisting of
just three header files and no external dependencies.
A permissive MIT `license <https://github.com/fmtlib/fmt#license>`_ allows
using the library both in open-source and commercial projects.
`Learn more... <contents.html>`_
.. raw:: html
<a class="btn btn-success" href="https://github.com/fmtlib/fmt">GitHub Repository</a>
<div class="section footer">
<iframe src="https://ghbtns.com/github-btn.html?user=fmtlib&amp;repo=fmt&amp;type=watch&amp;count=true"
class="github-btn" width="100" height="20"></iframe>
</div>

View File

@ -1,486 +0,0 @@
.. _syntax:
********************
Format String Syntax
********************
Formatting functions such as :ref:`fmt::format() <format>` and
:ref:`fmt::print() <print>` use the same format string syntax described in this
section.
Format strings contain "replacement fields" surrounded by curly braces ``{}``.
Anything that is not contained in braces is considered literal text, which is
copied unchanged to the output. If you need to include a brace character in the
literal text, it can be escaped by doubling: ``{{`` and ``}}``.
The grammar for a replacement field is as follows:
.. productionlist:: sf
replacement_field: "{" [`arg_id`] [":" (`format_spec` | `chrono_format_spec`)] "}"
arg_id: `integer` | `identifier`
integer: `digit`+
digit: "0"..."9"
identifier: `id_start` `id_continue`*
id_start: "a"..."z" | "A"..."Z" | "_"
id_continue: `id_start` | `digit`
In less formal terms, the replacement field can start with an *arg_id*
that specifies the argument whose value is to be formatted and inserted into
the output instead of the replacement field.
The *arg_id* is optionally followed by a *format_spec*, which is preceded by a
colon ``':'``. These specify a non-default format for the replacement value.
See also the :ref:`formatspec` section.
If the numerical arg_ids in a format string are 0, 1, 2, ... in sequence,
they can all be omitted (not just some) and the numbers 0, 1, 2, ... will be
automatically inserted in that order.
Named arguments can be referred to by their names or indices.
Some simple format string examples::
"First, thou shalt count to {0}" // References the first argument
"Bring me a {}" // Implicitly references the first argument
"From {} to {}" // Same as "From {0} to {1}"
The *format_spec* field contains a specification of how the value should be
presented, including such details as field width, alignment, padding, decimal
precision and so on. Each value type can define its own "formatting
mini-language" or interpretation of the *format_spec*.
Most built-in types support a common formatting mini-language, which is
described in the next section.
A *format_spec* field can also include nested replacement fields in certain
positions within it. These nested replacement fields can contain only an
argument id; format specifications are not allowed. This allows the formatting
of a value to be dynamically specified.
See the :ref:`formatexamples` section for some examples.
.. _formatspec:
Format Specification Mini-Language
==================================
"Format specifications" are used within replacement fields contained within a
format string to define how individual values are presented (see
:ref:`syntax`). Each formattable type may define how the format
specification is to be interpreted.
Most built-in types implement the following options for format specifications,
although some of the formatting options are only supported by the numeric types.
The general form of a *standard format specifier* is:
.. productionlist:: sf
format_spec: [[`fill`]`align`][`sign`]["#"]["0"][`width`]["." `precision`]["L"][`type`]
fill: <a character other than '{' or '}'>
align: "<" | ">" | "^"
sign: "+" | "-" | " "
width: `integer` | "{" [`arg_id`] "}"
precision: `integer` | "{" [`arg_id`] "}"
type: "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" |
: "o" | "p" | "s" | "x" | "X"
The *fill* character can be any Unicode code point other than ``'{'`` or
``'}'``. The presence of a fill character is signaled by the character following
it, which must be one of the alignment options. If the second character of
*format_spec* is not a valid alignment option, then it is assumed that both the
fill character and the alignment option are absent.
The meaning of the various alignment options is as follows:
+---------+----------------------------------------------------------+
| Option | Meaning |
+=========+==========================================================+
| ``'<'`` | Forces the field to be left-aligned within the available |
| | space (this is the default for most objects). |
+---------+----------------------------------------------------------+
| ``'>'`` | Forces the field to be right-aligned within the |
| | available space (this is the default for numbers). |
+---------+----------------------------------------------------------+
| ``'^'`` | Forces the field to be centered within the available |
| | space. |
+---------+----------------------------------------------------------+
Note that unless a minimum field width is defined, the field width will always
be the same size as the data to fill it, so that the alignment option has no
meaning in this case.
The *sign* option is only valid for number types, and can be one of the
following:
+---------+----------------------------------------------------------+
| Option | Meaning |
+=========+==========================================================+
| ``'+'`` | indicates that a sign should be used for both |
| | positive as well as negative numbers. |
+---------+----------------------------------------------------------+
| ``'-'`` | indicates that a sign should be used only for negative |
| | numbers (this is the default behavior). |
+---------+----------------------------------------------------------+
| space | indicates that a leading space should be used on |
| | positive numbers, and a minus sign on negative numbers. |
+---------+----------------------------------------------------------+
The ``'#'`` option causes the "alternate form" to be used for the
conversion. The alternate form is defined differently for different
types. This option is only valid for integer and floating-point types.
For integers, when binary, octal, or hexadecimal output is used, this
option adds the prefix respective ``"0b"`` (``"0B"``), ``"0"``, or
``"0x"`` (``"0X"``) to the output value. Whether the prefix is
lower-case or upper-case is determined by the case of the type
specifier, for example, the prefix ``"0x"`` is used for the type ``'x'``
and ``"0X"`` is used for ``'X'``. For floating-point numbers the
alternate form causes the result of the conversion to always contain a
decimal-point character, even if no digits follow it. Normally, a
decimal-point character appears in the result of these conversions
only if a digit follows it. In addition, for ``'g'`` and ``'G'``
conversions, trailing zeros are not removed from the result.
.. ifconfig:: False
The ``','`` option signals the use of a comma for a thousands separator.
For a locale aware separator, use the ``'L'`` integer presentation type
instead.
*width* is a decimal integer defining the minimum field width. If not
specified, then the field width will be determined by the content.
Preceding the *width* field by a zero (``'0'``) character enables sign-aware
zero-padding for numeric types. It forces the padding to be placed after the
sign or base (if any) but before the digits. This is used for printing fields in
the form '+000000120'. This option is only valid for numeric types and it has no
effect on formatting of infinity and NaN.
The *precision* is a decimal number indicating how many digits should be
displayed after the decimal point for a floating-point value formatted with
``'f'`` and ``'F'``, or before and after the decimal point for a floating-point
value formatted with ``'g'`` or ``'G'``. For non-number types the field
indicates the maximum field size - in other words, how many characters will be
used from the field content. The *precision* is not allowed for integer,
character, Boolean, and pointer values.
The ``'L'`` option uses the current locale setting to insert the appropriate
number separator characters. This option is only valid for numeric types.
Finally, the *type* determines how the data should be presented.
The available string presentation types are:
+---------+----------------------------------------------------------+
| Type | Meaning |
+=========+==========================================================+
| ``'s'`` | String format. This is the default type for strings and |
| | may be omitted. |
+---------+----------------------------------------------------------+
| none | The same as ``'s'``. |
+---------+----------------------------------------------------------+
The available character presentation types are:
+---------+----------------------------------------------------------+
| Type | Meaning |
+=========+==========================================================+
| ``'c'`` | Character format. This is the default type for |
| | characters and may be omitted. |
+---------+----------------------------------------------------------+
| none | The same as ``'c'``. |
+---------+----------------------------------------------------------+
The available integer presentation types are:
+---------+----------------------------------------------------------+
| Type | Meaning |
+=========+==========================================================+
| ``'b'`` | Binary format. Outputs the number in base 2. Using the |
| | ``'#'`` option with this type adds the prefix ``"0b"`` |
| | to the output value. |
+---------+----------------------------------------------------------+
| ``'B'`` | Binary format. Outputs the number in base 2. Using the |
| | ``'#'`` option with this type adds the prefix ``"0B"`` |
| | to the output value. |
+---------+----------------------------------------------------------+
| ``'c'`` | Character format. Outputs the number as a character. |
+---------+----------------------------------------------------------+
| ``'d'`` | Decimal integer. Outputs the number in base 10. |
+---------+----------------------------------------------------------+
| ``'o'`` | Octal format. Outputs the number in base 8. |
+---------+----------------------------------------------------------+
| ``'x'`` | Hex format. Outputs the number in base 16, using |
| | lower-case letters for the digits above 9. Using the |
| | ``'#'`` option with this type adds the prefix ``"0x"`` |
| | to the output value. |
+---------+----------------------------------------------------------+
| ``'X'`` | Hex format. Outputs the number in base 16, using |
| | upper-case letters for the digits above 9. Using the |
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
| | to the output value. |
+---------+----------------------------------------------------------+
| none | The same as ``'d'``. |
+---------+----------------------------------------------------------+
Integer presentation types can also be used with character and Boolean values.
Boolean values are formatted using textual representation, either ``true`` or
``false``, if the presentation type is not specified.
The available presentation types for floating-point values are:
+---------+----------------------------------------------------------+
| Type | Meaning |
+=========+==========================================================+
| ``'a'`` | Hexadecimal floating point format. Prints the number in |
| | base 16 with prefix ``"0x"`` and lower-case letters for |
| | digits above 9. Uses ``'p'`` to indicate the exponent. |
+---------+----------------------------------------------------------+
| ``'A'`` | Same as ``'a'`` except it uses upper-case letters for |
| | the prefix, digits above 9 and to indicate the exponent. |
+---------+----------------------------------------------------------+
| ``'e'`` | Exponent notation. Prints the number in scientific |
| | notation using the letter 'e' to indicate the exponent. |
+---------+----------------------------------------------------------+
| ``'E'`` | Exponent notation. Same as ``'e'`` except it uses an |
| | upper-case ``'E'`` as the separator character. |
+---------+----------------------------------------------------------+
| ``'f'`` | Fixed point. Displays the number as a fixed-point |
| | number. |
+---------+----------------------------------------------------------+
| ``'F'`` | Fixed point. Same as ``'f'``, but converts ``nan`` to |
| | ``NAN`` and ``inf`` to ``INF``. |
+---------+----------------------------------------------------------+
| ``'g'`` | General format. For a given precision ``p >= 1``, |
| | this rounds the number to ``p`` significant digits and |
| | then formats the result in either fixed-point format |
| | or in scientific notation, depending on its magnitude. |
| | |
| | A precision of ``0`` is treated as equivalent to a |
| | precision of ``1``. |
+---------+----------------------------------------------------------+
| ``'G'`` | General format. Same as ``'g'`` except switches to |
| | ``'E'`` if the number gets too large. The |
| | representations of infinity and NaN are uppercased, too. |
+---------+----------------------------------------------------------+
| none | Similar to ``'g'``, except that the default precision is |
| | as high as needed to represent the particular value. |
+---------+----------------------------------------------------------+
.. ifconfig:: False
+---------+----------------------------------------------------------+
| | The precise rules are as follows: suppose that the |
| | result formatted with presentation type ``'e'`` and |
| | precision ``p-1`` would have exponent ``exp``. Then |
| | if ``-4 <= exp < p``, the number is formatted |
| | with presentation type ``'f'`` and precision |
| | ``p-1-exp``. Otherwise, the number is formatted |
| | with presentation type ``'e'`` and precision ``p-1``. |
| | In both cases insignificant trailing zeros are removed |
| | from the significand, and the decimal point is also |
| | removed if there are no remaining digits following it. |
| | |
| | Positive and negative infinity, positive and negative |
| | zero, and nans, are formatted as ``inf``, ``-inf``, |
| | ``0``, ``-0`` and ``nan`` respectively, regardless of |
| | the precision. |
| | |
+---------+----------------------------------------------------------+
The available presentation types for pointers are:
+---------+----------------------------------------------------------+
| Type | Meaning |
+=========+==========================================================+
| ``'p'`` | Pointer format. This is the default type for |
| | pointers and may be omitted. |
+---------+----------------------------------------------------------+
| none | The same as ``'p'``. |
+---------+----------------------------------------------------------+
.. _chrono-specs:
Chrono Format Specifications
============================
Format specifications for chrono types have the following syntax:
.. productionlist:: sf
chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`]
chrono_specs: [`chrono_specs`] `conversion_spec` | `chrono_specs` `literal_char`
conversion_spec: "%" [`modifier`] `chrono_type`
literal_char: <a character other than '{', '}' or '%'>
modifier: "E" | "O"
chrono_type: "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" | "F" |
: "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" | "n" | "p" |
: "q" | "Q" | "r" | "R" | "S" | "t" | "T" | "u" | "U" | "V" |
: "w" | "W" | "x" | "X" | "y" | "Y" | "z" | "Z" | "%"
Literal chars are copied unchanged to the output. Precision is valid only for
``std::chrono::duration`` types with a floating-point representation type.
The available presentation types (*chrono_type*) for chrono durations and time
points are:
+---------+--------------------------------------------------------------------+
| Type | Meaning |
+=========+====================================================================+
| ``'H'`` | The hour (24-hour clock) as a decimal number. If the result is a |
| | single digit, it is prefixed with 0. The modified command ``%OH`` |
| | produces the locale's alternative representation. |
+---------+--------------------------------------------------------------------+
| ``'M'`` | The minute as a decimal number. If the result is a single digit, |
| | it is prefixed with 0. The modified command ``%OM`` produces the |
| | locale's alternative representation. |
+---------+--------------------------------------------------------------------+
| ``'S'`` | Seconds as a decimal number. If the number of seconds is less than |
| | 10, the result is prefixed with 0. If the precision of the input |
| | cannot be exactly represented with seconds, then the format is a |
| | decimal floating-point number with a fixed format and a precision |
| | matching that of the precision of the input (or to a microseconds |
| | precision if the conversion to floating-point decimal seconds |
| | cannot be made within 18 fractional digits). The character for the |
| | decimal point is localized according to the locale. The modified |
| | command ``%OS`` produces the locale's alternative representation. |
+---------+--------------------------------------------------------------------+
Specifiers that have a calendaric component such as `'d'` (the day of month)
are valid only for ``std::tm`` and not durations or time points.
``std::tm`` uses the system's `strftime
<https://en.cppreference.com/w/cpp/chrono/c/strftime>`_ so refer to its
documentation for details on supported conversion specifiers.
.. _formatexamples:
Format Examples
===============
This section contains examples of the format syntax and comparison with
the printf formatting.
In most of the cases the syntax is similar to the printf formatting, with the
addition of the ``{}`` and with ``:`` used instead of ``%``.
For example, ``"%03.2f"`` can be translated to ``"{:03.2f}"``.
The new format syntax also supports new and different options, shown in the
following examples.
Accessing arguments by position::
fmt::format("{0}, {1}, {2}", 'a', 'b', 'c');
// Result: "a, b, c"
fmt::format("{}, {}, {}", 'a', 'b', 'c');
// Result: "a, b, c"
fmt::format("{2}, {1}, {0}", 'a', 'b', 'c');
// Result: "c, b, a"
fmt::format("{0}{1}{0}", "abra", "cad"); // arguments' indices can be repeated
// Result: "abracadabra"
Aligning the text and specifying a width::
fmt::format("{:<30}", "left aligned");
// Result: "left aligned "
fmt::format("{:>30}", "right aligned");
// Result: " right aligned"
fmt::format("{:^30}", "centered");
// Result: " centered "
fmt::format("{:*^30}", "centered"); // use '*' as a fill char
// Result: "***********centered***********"
Dynamic width::
fmt::format("{:<{}}", "left aligned", 30);
// Result: "left aligned "
Dynamic precision::
fmt::format("{:.{}f}", 3.14, 1);
// Result: "3.1"
Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign::
fmt::format("{:+f}; {:+f}", 3.14, -3.14); // show it always
// Result: "+3.140000; -3.140000"
fmt::format("{: f}; {: f}", 3.14, -3.14); // show a space for positive numbers
// Result: " 3.140000; -3.140000"
fmt::format("{:-f}; {:-f}", 3.14, -3.14); // show only the minus -- same as '{:f}; {:f}'
// Result: "3.140000; -3.140000"
Replacing ``%x`` and ``%o`` and converting the value to different bases::
fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
// Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
// with 0x or 0 or 0b as prefix:
fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
Padded hex byte with prefix and always prints both hex characters::
fmt::format("{:#04x}", 0);
// Result: "0x00"
Box drawing using Unicode fill::
fmt::print(
"┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n", "", "Hello, world!", 20);
prints::
┌────────────────────┐
│ Hello, world! │
└────────────────────┘
Using type-specific formatting::
#include <fmt/chrono.h>
auto t = tm();
t.tm_year = 2010 - 1900;
t.tm_mon = 7;
t.tm_mday = 4;
t.tm_hour = 12;
t.tm_min = 15;
t.tm_sec = 58;
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
// Prints: 2010-08-04 12:15:58
Using the comma as a thousands separator::
#include <fmt/locale.h>
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890"
.. ifconfig:: False
Nesting arguments and more complex examples::
>>> for align, text in zip('<^>', ['left', 'center', 'right']):
... '{0:{fill}{align}16}") << text, fill=align, align=align)
...
'left<<<<<<<<<<<<'
'^^^^^center^^^^^'
'>>>>>>>>>>>right'
>>>
>>> octets = [192, 168, 0, 1]
Format("{:02X}{:02X}{:02X}{:02X}") << *octets)
'C0A80001'
>>> int(_, 16)
3232235521
>>>
>>> width = 5
>>> for num in range(5,12):
... for base in 'dXob':
... print('{0:{width}{base}}") << num, base=base, width=width), end=' ')
... print()
...
5 5 5 101
6 6 6 110
7 7 7 111
8 8 10 1000
9 9 11 1001
10 A 12 1010
11 B 13 1011

View File

@ -1,212 +0,0 @@
*****
Usage
*****
To use the {fmt} library, add :file:`fmt/core.h`, :file:`fmt/format.h`,
:file:`fmt/format-inl.h`, :file:`src/format.cc` and optionally other headers
from a `release archive <https://github.com/fmtlib/fmt/releases/latest>`_ or
the `Git repository <https://github.com/fmtlib/fmt>`_ to your project.
Alternatively, you can :ref:`build the library with CMake <building>`.
.. _building:
Building the Library
====================
The included `CMake build script`__ can be used to build the fmt
library on a wide range of platforms. CMake is freely available for
download from https://www.cmake.org/download/.
__ https://github.com/fmtlib/fmt/blob/master/CMakeLists.txt
CMake works by generating native makefiles or project files that can
be used in the compiler environment of your choice. The typical
workflow starts with::
mkdir build # Create a directory to hold the build output.
cd build
cmake .. # Generate native build scripts.
where :file:`{<path/to/fmt>}` is a path to the ``fmt`` repository.
If you are on a \*nix system, you should now see a Makefile in the
current directory. Now you can build the library by running :command:`make`.
Once the library has been built you can invoke :command:`make test` to run
the tests.
You can control generation of the make ``test`` target with the ``FMT_TEST``
CMake option. This can be useful if you include fmt as a subdirectory in
your project but don't want to add fmt's tests to your ``test`` target.
If you use Windows and have Visual Studio installed, a :file:`FMT.sln`
file and several :file:`.vcproj` files will be created. You can then build them
using Visual Studio or msbuild.
On Mac OS X with Xcode installed, an :file:`.xcodeproj` file will be generated.
To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to
``TRUE``::
cmake -DBUILD_SHARED_LIBS=TRUE ...
__ https://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
To build a `static library` with position independent code (required if the main
consumer of the fmt library is a shared library i.e. a Python extension) set the
``CMAKE_POSITION_INDEPENDENT_CODE`` CMake variable to ``TRUE``::
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE ...
Installing the Library
======================
After building the library you can install it on a Unix-like system by running
:command:`sudo make install`.
Usage with CMake
================
You can add the ``fmt`` library directory into your project and include it in
your ``CMakeLists.txt`` file::
add_subdirectory(fmt)
or
::
add_subdirectory(fmt EXCLUDE_FROM_ALL)
to exclude it from ``make``, ``make all``, or ``cmake --build .``.
You can detect and use an installed version of {fmt} as follows::
find_package(fmt)
target_link_libraries(<your-target> fmt::fmt)
Setting up your target to use a header-only version of ``fmt`` is equally easy::
target_link_libraries(<your-target> PRIVATE fmt::fmt-header-only)
Usage with build2
=================
You can use `build2 <https://build2.org>`_, a dependency manager and a
build-system combined, to use ``fmt``.
Currently this package is available in these package repositories:
- **https://cppget.org/fmt/** for released and published versions.
- `The git repository with the sources of the build2 package of fmt <https://github.com/build2-packaging/fmt.git>`_
for unreleased or custom revisions of ``fmt``.
**Usage:**
- ``build2`` package name: ``fmt``
- Library target name : ``lib{fmt}``
For example, to make your ``build2`` project depend on ``fmt``:
- Add one of the repositories to your configurations, or in your
``repositories.manifest``, if not already there::
:
role: prerequisite
location: https://pkg.cppget.org/1/stable
- Add this package as a dependency to your ``./manifest`` file
(example for ``v7.0.x``)::
depends: fmt ~7.0.0
- Import the target and use it as a prerequisite to your own target
using `fmt` in the appropriate ``buildfile``::
import fmt = fmt%lib{fmt}
lib{mylib} : cxx{**} ... $fmt
Then build your project as usual with `b` or `bdep update`.
For ``build2`` newcomers or to get more details and use cases, you can read the
``build2``
`toolchain introduction <https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml>`_.
Building the Documentation
==========================
To build the documentation you need the following software installed on your
system:
* `Python <https://www.python.org/>`_ with pip and virtualenv
* `Doxygen <http://www.stack.nl/~dimitri/doxygen/>`_
* `Less <http://lesscss.org/>`_ with ``less-plugin-clean-css``.
Ubuntu doesn't package the ``clean-css`` plugin so you should use ``npm``
instead of ``apt`` to install both ``less`` and the plugin::
sudo npm install -g less less-plugin-clean-css.
First generate makefiles or project files using CMake as described in
the previous section. Then compile the ``doc`` target/project, for example::
make doc
This will generate the HTML documentation in ``doc/html``.
Conda
=====
fmt can be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__, using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__, as follows::
conda install -c conda-forge fmt
Vcpkg
=====
You can download and install fmt using the `vcpkg
<https://github.com/Microsoft/vcpkg>`__ dependency manager::
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install fmt
The fmt port in vcpkg is kept up to date by Microsoft team members and community
contributors. If the version is out of date, please `create an issue or pull
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
LHelper
=======
You can download and install fmt using
`lhelper <https://github.com/franko/lhelper>`__ dependency manager::
lhelper activate <some-environment>
lhelper install fmt
All the recipes for lhelper are kept in the
`lhelper's recipe <https://github.com/franko/lhelper-recipes>`__ repository.
Android NDK
===========
fmt provides `Android.mk file`__ that can be used to build the library
with `Android NDK <https://developer.android.com/tools/sdk/ndk/index.html>`_.
For an example of using fmt with Android NDK, see the
`android-ndk-example <https://github.com/fmtlib/android-ndk-example>`_
repository.
__ https://github.com/fmtlib/fmt/blob/master/support/Android.mk
Homebrew
========
fmt can be installed on OS X using `Homebrew <https://brew.sh/>`_::
brew install fmt

View File

@ -1,856 +0,0 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li div.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 450px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
a.brackets:before,
span.brackets > a:before{
content: "[";
}
a.brackets:after,
span.brackets > a:after {
content: "]";
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
table.footnote td, table.footnote th {
border: 0 !important;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption {
padding: 0.3em;
}
div.figure p.caption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
dl.footnote > dt,
dl.citation > dt {
float: left;
margin-right: 0.5em;
}
dl.footnote > dd,
dl.citation > dd {
margin-bottom: 0em;
}
dl.footnote > dd:after,
dl.citation > dd:after {
content: "";
clear: both;
}
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dt:after {
content: ":";
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0.5em;
content: ":";
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
code.descclassname {
background-color: transparent;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,316 +0,0 @@
/*
* doctools.js
* ~~~~~~~~~~~
*
* Sphinx JavaScript utilities for all documentation.
*
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/**
* select a different prefix for underscore
*/
$u = _.noConflict();
/**
* make the code below compatible with browsers without
* an installed firebug like debugger
if (!window.console || !console.firebug) {
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
"dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
"profile", "profileEnd"];
window.console = {};
for (var i = 0; i < names.length; ++i)
window.console[names[i]] = function() {};
}
*/
/**
* small helper function to urldecode strings
*/
jQuery.urldecode = function(x) {
return decodeURIComponent(x).replace(/\+/g, ' ');
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}
/**
* Small JavaScript module for the documentation.
*/
var Documentation = {
init : function() {
this.fixFirefoxAnchorBug();
this.highlightSearchWords();
this.initIndexTable();
if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
this.initOnKeyListeners();
}
},
/**
* i18n support
*/
TRANSLATIONS : {},
PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; },
LOCALE : 'unknown',
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext : function(string) {
var translated = Documentation.TRANSLATIONS[string];
if (typeof translated === 'undefined')
return string;
return (typeof translated === 'string') ? translated : translated[0];
},
ngettext : function(singular, plural, n) {
var translated = Documentation.TRANSLATIONS[singular];
if (typeof translated === 'undefined')
return (n == 1) ? singular : plural;
return translated[Documentation.PLURALEXPR(n)];
},
addTranslations : function(catalog) {
for (var key in catalog.messages)
this.TRANSLATIONS[key] = catalog.messages[key];
this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
this.LOCALE = catalog.locale;
},
/**
* add context elements like header anchor links
*/
addContextElements : function() {
$('div[id] > :header:first').each(function() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this headline')).
appendTo(this);
});
$('dt[id]').each(function() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this definition')).
appendTo(this);
});
},
/**
* workaround a firefox stupidity
* see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
*/
fixFirefoxAnchorBug : function() {
if (document.location.hash && $.browser.mozilla)
window.setTimeout(function() {
document.location.href += '';
}, 10);
},
/**
* highlight the search words provided in the url in the text
*/
highlightSearchWords : function() {
var params = $.getQueryParameters();
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
if (terms.length) {
var body = $('div.body');
if (!body.length) {
body = $('body');
}
window.setTimeout(function() {
$.each(terms, function() {
body.highlightText(this.toLowerCase(), 'highlighted');
});
}, 10);
$('<p class="highlight-link"><a href="javascript:Documentation.' +
'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
.appendTo($('#searchbox'));
}
},
/**
* init the domain index toggle buttons
*/
initIndexTable : function() {
var togglers = $('img.toggler').click(function() {
var src = $(this).attr('src');
var idnum = $(this).attr('id').substr(7);
$('tr.cg-' + idnum).toggle();
if (src.substr(-9) === 'minus.png')
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
else
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
}).css('display', '');
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
togglers.click();
}
},
/**
* helper function to hide the search marks again
*/
hideSearchWords : function() {
$('#searchbox .highlight-link').fadeOut(300);
$('span.highlighted').removeClass('highlighted');
},
/**
* make the url absolute
*/
makeURL : function(relativeURL) {
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
},
/**
* get the current relative url
*/
getCurrentURL : function() {
var path = document.location.pathname;
var parts = path.split(/\//);
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
if (this === '..')
parts.pop();
});
var url = parts.join('/');
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
},
initOnKeyListeners: function() {
$(document).keydown(function(event) {
var activeElementType = document.activeElement.tagName;
// don't navigate when in search box, textarea, dropdown or button
if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT'
&& activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey
&& !event.shiftKey) {
switch (event.keyCode) {
case 37: // left
var prevHref = $('link[rel="prev"]').prop('href');
if (prevHref) {
window.location.href = prevHref;
return false;
}
case 39: // right
var nextHref = $('link[rel="next"]').prop('href');
if (nextHref) {
window.location.href = nextHref;
return false;
}
}
}
});
}
};
// quick alias for translations
_ = Documentation.gettext;
$(document).ready(function() {
Documentation.init();
});

View File

@ -1,12 +0,0 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '8.0.1',
LANGUAGE: 'None',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 B

File diff suppressed because it is too large Load Diff

View File

@ -1,229 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata></metadata>
<defs>
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
<font-face units-per-em="1200" ascent="960" descent="-240" />
<missing-glyph horiz-adv-x="500" />
<glyph />
<glyph />
<glyph unicode="&#xd;" />
<glyph unicode=" " />
<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
<glyph unicode="&#xa0;" />
<glyph unicode="&#x2000;" horiz-adv-x="652" />
<glyph unicode="&#x2001;" horiz-adv-x="1304" />
<glyph unicode="&#x2002;" horiz-adv-x="652" />
<glyph unicode="&#x2003;" horiz-adv-x="1304" />
<glyph unicode="&#x2004;" horiz-adv-x="434" />
<glyph unicode="&#x2005;" horiz-adv-x="326" />
<glyph unicode="&#x2006;" horiz-adv-x="217" />
<glyph unicode="&#x2007;" horiz-adv-x="217" />
<glyph unicode="&#x2008;" horiz-adv-x="163" />
<glyph unicode="&#x2009;" horiz-adv-x="260" />
<glyph unicode="&#x200a;" horiz-adv-x="72" />
<glyph unicode="&#x202f;" horiz-adv-x="260" />
<glyph unicode="&#x205f;" horiz-adv-x="326" />
<glyph unicode="&#x20ac;" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
<glyph unicode="&#x2212;" d="M200 400h900v300h-900v-300z" />
<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
<glyph unicode="&#x2601;" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
<glyph unicode="&#x2709;" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
<glyph unicode="&#x270f;" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
<glyph unicode="&#xe001;" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
<glyph unicode="&#xe002;" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
<glyph unicode="&#xe003;" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
<glyph unicode="&#xe005;" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
<glyph unicode="&#xe006;" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
<glyph unicode="&#xe007;" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
<glyph unicode="&#xe008;" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
<glyph unicode="&#xe009;" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
<glyph unicode="&#xe010;" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe011;" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe012;" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe013;" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
<glyph unicode="&#xe014;" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
<glyph unicode="&#xe015;" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
<glyph unicode="&#xe016;" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
<glyph unicode="&#xe017;" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
<glyph unicode="&#xe018;" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
<glyph unicode="&#xe019;" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
<glyph unicode="&#xe020;" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
<glyph unicode="&#xe021;" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
<glyph unicode="&#xe022;" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
<glyph unicode="&#xe023;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
<glyph unicode="&#xe024;" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
<glyph unicode="&#xe025;" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
<glyph unicode="&#xe026;" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
<glyph unicode="&#xe027;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
<glyph unicode="&#xe028;" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
<glyph unicode="&#xe029;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
<glyph unicode="&#xe030;" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
<glyph unicode="&#xe031;" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
<glyph unicode="&#xe032;" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
<glyph unicode="&#xe033;" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
<glyph unicode="&#xe034;" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
<glyph unicode="&#xe035;" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
<glyph unicode="&#xe036;" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
<glyph unicode="&#xe037;" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
<glyph unicode="&#xe038;" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
<glyph unicode="&#xe039;" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
<glyph unicode="&#xe040;" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
<glyph unicode="&#xe041;" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
<glyph unicode="&#xe042;" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
<glyph unicode="&#xe043;" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
<glyph unicode="&#xe044;" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
<glyph unicode="&#xe045;" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
<glyph unicode="&#xe046;" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
<glyph unicode="&#xe047;" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
<glyph unicode="&#xe048;" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
<glyph unicode="&#xe049;" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
<glyph unicode="&#xe050;" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
<glyph unicode="&#xe051;" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
<glyph unicode="&#xe052;" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
<glyph unicode="&#xe053;" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
<glyph unicode="&#xe054;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe055;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe056;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe057;" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
<glyph unicode="&#xe058;" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
<glyph unicode="&#xe059;" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
<glyph unicode="&#xe060;" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
<glyph unicode="&#xe062;" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
<glyph unicode="&#xe063;" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
<glyph unicode="&#xe064;" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
<glyph unicode="&#xe065;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
<glyph unicode="&#xe066;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
<glyph unicode="&#xe067;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
<glyph unicode="&#xe068;" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
<glyph unicode="&#xe069;" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe070;" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe071;" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
<glyph unicode="&#xe072;" d="M200 0l900 550l-900 550v-1100z" />
<glyph unicode="&#xe073;" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
<glyph unicode="&#xe074;" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
<glyph unicode="&#xe075;" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
<glyph unicode="&#xe076;" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
<glyph unicode="&#xe077;" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
<glyph unicode="&#xe078;" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
<glyph unicode="&#xe079;" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
<glyph unicode="&#xe080;" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
<glyph unicode="&#xe081;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
<glyph unicode="&#xe082;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
<glyph unicode="&#xe083;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
<glyph unicode="&#xe084;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
<glyph unicode="&#xe085;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
<glyph unicode="&#xe086;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
<glyph unicode="&#xe087;" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
<glyph unicode="&#xe088;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
<glyph unicode="&#xe089;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
<glyph unicode="&#xe090;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
<glyph unicode="&#xe091;" d="M0 547l600 453v-300h600v-300h-600v-301z" />
<glyph unicode="&#xe092;" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
<glyph unicode="&#xe093;" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
<glyph unicode="&#xe094;" d="M104 600h296v600h300v-600h298l-449 -600z" />
<glyph unicode="&#xe095;" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
<glyph unicode="&#xe096;" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
<glyph unicode="&#xe097;" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
<glyph unicode="&#xe101;" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
<glyph unicode="&#xe102;" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
<glyph unicode="&#xe103;" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
<glyph unicode="&#xe104;" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
<glyph unicode="&#xe105;" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
<glyph unicode="&#xe106;" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
<glyph unicode="&#xe107;" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
<glyph unicode="&#xe108;" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
<glyph unicode="&#xe109;" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
<glyph unicode="&#xe110;" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
<glyph unicode="&#xe111;" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
<glyph unicode="&#xe112;" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
<glyph unicode="&#xe113;" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
<glyph unicode="&#xe114;" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
<glyph unicode="&#xe115;" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
<glyph unicode="&#xe116;" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
<glyph unicode="&#xe117;" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
<glyph unicode="&#xe118;" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
<glyph unicode="&#xe119;" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
<glyph unicode="&#xe120;" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
<glyph unicode="&#xe121;" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
<glyph unicode="&#xe122;" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe123;" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
<glyph unicode="&#xe124;" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
<glyph unicode="&#xe125;" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
<glyph unicode="&#xe126;" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
<glyph unicode="&#xe127;" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
<glyph unicode="&#xe128;" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
<glyph unicode="&#xe129;" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
<glyph unicode="&#xe130;" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
<glyph unicode="&#xe131;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
<glyph unicode="&#xe132;" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
<glyph unicode="&#xe133;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
<glyph unicode="&#xe134;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
<glyph unicode="&#xe135;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
<glyph unicode="&#xe136;" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
<glyph unicode="&#xe138;" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
<glyph unicode="&#xe139;" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
<glyph unicode="&#xe140;" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
<glyph unicode="&#xe141;" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
<glyph unicode="&#xe142;" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
<glyph unicode="&#xe143;" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
<glyph unicode="&#xe144;" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
<glyph unicode="&#xe145;" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
<glyph unicode="&#xe146;" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
<glyph unicode="&#xe148;" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
<glyph unicode="&#xe149;" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
<glyph unicode="&#xe150;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
<glyph unicode="&#xe151;" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
<glyph unicode="&#xe152;" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
<glyph unicode="&#xe153;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
<glyph unicode="&#xe154;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
<glyph unicode="&#xe155;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
<glyph unicode="&#xe156;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
<glyph unicode="&#xe157;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
<glyph unicode="&#xe158;" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
<glyph unicode="&#xe159;" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
<glyph unicode="&#xe160;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
<glyph unicode="&#xe161;" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
<glyph unicode="&#xe162;" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
<glyph unicode="&#xe163;" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
<glyph unicode="&#xe164;" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
<glyph unicode="&#xe165;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
<glyph unicode="&#xe166;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe167;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe168;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe169;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe170;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe171;" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
<glyph unicode="&#xe172;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
<glyph unicode="&#xe173;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
<glyph unicode="&#xe174;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
<glyph unicode="&#xe175;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
<glyph unicode="&#xe176;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
<glyph unicode="&#xe177;" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
<glyph unicode="&#xe178;" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
<glyph unicode="&#xe179;" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
<glyph unicode="&#xe180;" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
<glyph unicode="&#xe181;" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
<glyph unicode="&#xe182;" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
<glyph unicode="&#xe183;" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
<glyph unicode="&#xe184;" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
<glyph unicode="&#xe185;" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
<glyph unicode="&#xe186;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
<glyph unicode="&#xe187;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
<glyph unicode="&#xe188;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
<glyph unicode="&#xe189;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
<glyph unicode="&#xe190;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
<glyph unicode="&#xe191;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
<glyph unicode="&#xe192;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
<glyph unicode="&#xe193;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
<glyph unicode="&#xe194;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
<glyph unicode="&#xe195;" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
<glyph unicode="&#xe197;" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
<glyph unicode="&#xe198;" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
<glyph unicode="&#xe199;" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
<glyph unicode="&#xe200;" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
</font>
</defs></svg>

Before

Width:  |  Height:  |  Size: 62 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,297 +0,0 @@
/*
* language_data.js
* ~~~~~~~~~~~~~~~~
*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
/* Non-minified version JS is _stemmer.js if file is provided */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}
var splitChars = (function() {
var result = {};
var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
var i, j, start, end;
for (i = 0; i < singles.length; i++) {
result[singles[i]] = true;
}
var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
[722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
[1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
[1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
[1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
[2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
[2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
[2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
[2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
[2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
[2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
[2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
[3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
[3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
[3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
[3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
[3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
[3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
[4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
[4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
[4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
[4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
[5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
[6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
[6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
[6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
[6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
[7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
[7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
[8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
[8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
[8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
[10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
[11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
[12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
[12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
[12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
[19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
[42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
[42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
[43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
[43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
[43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
[43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
[44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
[57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
[64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
[65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
[65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
for (i = 0; i < ranges.length; i++) {
start = ranges[i][0];
end = ranges[i][1];
for (j = start; j <= end; j++) {
result[j] = true;
}
}
return result;
})();
function splitQuery(query) {
var result = [];
var start = -1;
for (var i = 0; i < query.length; i++) {
if (splitChars[query.charCodeAt(i)]) {
if (start !== -1) {
result.push(query.slice(start, i));
start = -1;
}
} else if (start === -1) {
start = i;
}
}
if (start !== -1) {
result.push(query.slice(start));
}
return result;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 B

View File

@ -1,74 +0,0 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #eeffcc; }
.highlight .c { color: #408090; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #FF0000 } /* Error */
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
.highlight .o { color: #666666 } /* Operator */
.highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #007020 } /* Comment.Preproc */
.highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #FF0000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #333333 } /* Generic.Output */
.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #0044DD } /* Generic.Traceback */
.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #007020 } /* Keyword.Pseudo */
.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #902000 } /* Keyword.Type */
.highlight .m { color: #208050 } /* Literal.Number */
.highlight .s { color: #4070a0 } /* Literal.String */
.highlight .na { color: #4070a0 } /* Name.Attribute */
.highlight .nb { color: #007020 } /* Name.Builtin */
.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
.highlight .no { color: #60add5 } /* Name.Constant */
.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #007020 } /* Name.Exception */
.highlight .nf { color: #06287e } /* Name.Function */
.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #bb60d5 } /* Name.Variable */
.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #208050 } /* Literal.Number.Bin */
.highlight .mf { color: #208050 } /* Literal.Number.Float */
.highlight .mh { color: #208050 } /* Literal.Number.Hex */
.highlight .mi { color: #208050 } /* Literal.Number.Integer */
.highlight .mo { color: #208050 } /* Literal.Number.Oct */
.highlight .sa { color: #4070a0 } /* Literal.String.Affix */
.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
.highlight .sc { color: #4070a0 } /* Literal.String.Char */
.highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */
.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
.highlight .sx { color: #c65d09 } /* Literal.String.Other */
.highlight .sr { color: #235388 } /* Literal.String.Regex */
.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
.highlight .ss { color: #517918 } /* Literal.String.Symbol */
.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #06287e } /* Name.Function.Magic */
.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */
.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */

View File

@ -1,514 +0,0 @@
/*
* searchtools.js
* ~~~~~~~~~~~~~~~~
*
* Sphinx JavaScript utilities for the full-text search.
*
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
if (!Scorer) {
/**
* Simple result scoring code.
*/
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [filename, title, anchor, descr, score]
// and returns the new score.
/*
score: function(result) {
return result[4];
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5}, // used to be unimportantResults
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2
};
}
if (!splitQuery) {
function splitQuery(query) {
return query.split(/\s+/);
}
}
/**
* Search Module
*/
var Search = {
_index : null,
_queued_query : null,
_pulse_status : -1,
htmlToText : function(htmlString) {
var virtualDocument = document.implementation.createHTMLDocument('virtual');
var htmlElement = $(htmlString, virtualDocument);
htmlElement.find('.headerlink').remove();
docContent = htmlElement.find('[role=main]')[0];
if(docContent === undefined) {
console.warn("Content block not found. Sphinx search tries to obtain it " +
"via '[role=main]'. Could you check your theme or template.");
return "";
}
return docContent.textContent || docContent.innerText;
},
init : function() {
var params = $.getQueryParameters();
if (params.q) {
var query = params.q[0];
$('input[name="q"]')[0].value = query;
this.performSearch(query);
}
},
loadIndex : function(url) {
$.ajax({type: "GET", url: url, data: null,
dataType: "script", cache: true,
complete: function(jqxhr, textstatus) {
if (textstatus != "success") {
document.getElementById("searchindexloader").src = url;
}
}});
},
setIndex : function(index) {
var q;
this._index = index;
if ((q = this._queued_query) !== null) {
this._queued_query = null;
Search.query(q);
}
},
hasIndex : function() {
return this._index !== null;
},
deferQuery : function(query) {
this._queued_query = query;
},
stopPulse : function() {
this._pulse_status = 0;
},
startPulse : function() {
if (this._pulse_status >= 0)
return;
function pulse() {
var i;
Search._pulse_status = (Search._pulse_status + 1) % 4;
var dotString = '';
for (i = 0; i < Search._pulse_status; i++)
dotString += '.';
Search.dots.text(dotString);
if (Search._pulse_status > -1)
window.setTimeout(pulse, 500);
}
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch : function(query) {
// create the required interface elements
this.out = $('#search-results');
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
this.dots = $('<span></span>').appendTo(this.title);
this.status = $('<p class="search-summary">&nbsp;</p>').appendTo(this.out);
this.output = $('<ul class="search"/>').appendTo(this.out);
$('#search-progress').text(_('Preparing search...'));
this.startPulse();
// index already loaded, the browser was quick!
if (this.hasIndex())
this.query(query);
else
this.deferQuery(query);
},
/**
* execute search (requires search index to be loaded)
*/
query : function(query) {
var i;
// stem the searchterms and add them to the correct list
var stemmer = new Stemmer();
var searchterms = [];
var excluded = [];
var hlterms = [];
var tmp = splitQuery(query);
var objectterms = [];
for (i = 0; i < tmp.length; i++) {
if (tmp[i] !== "") {
objectterms.push(tmp[i].toLowerCase());
}
if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i] === "") {
// skip this "word"
continue;
}
// stem the word
var word = stemmer.stemWord(tmp[i].toLowerCase());
// prevent stemmer from cutting word smaller than two chars
if(word.length < 3 && tmp[i].length >= 3) {
word = tmp[i];
}
var toAppend;
// select the correct list
if (word[0] == '-') {
toAppend = excluded;
word = word.substr(1);
}
else {
toAppend = searchterms;
hlterms.push(tmp[i].toLowerCase());
}
// only add if not already in the list
if (!$u.contains(toAppend, word))
toAppend.push(word);
}
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
// console.debug('SEARCH: searching for:');
// console.info('required: ', searchterms);
// console.info('excluded: ', excluded);
// prepare search
var terms = this._index.terms;
var titleterms = this._index.titleterms;
// array of [filename, title, anchor, descr, score]
var results = [];
$('#search-progress').empty();
// lookup as object
for (i = 0; i < objectterms.length; i++) {
var others = [].concat(objectterms.slice(0, i),
objectterms.slice(i+1, objectterms.length));
results = results.concat(this.performObjectSearch(objectterms[i], others));
}
// lookup as search terms in fulltext
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) {
for (i = 0; i < results.length; i++)
results[i][4] = Scorer.score(results[i]);
}
// now sort the results by score (in opposite order of appearance, since the
// display function below uses pop() to retrieve items) and then
// alphabetically
results.sort(function(a, b) {
var left = a[4];
var right = b[4];
if (left > right) {
return 1;
} else if (left < right) {
return -1;
} else {
// same score: sort alphabetically
left = a[1].toLowerCase();
right = b[1].toLowerCase();
return (left > right) ? -1 : ((left < right) ? 1 : 0);
}
});
// for debugging
//Search.lastresults = results.slice(); // a copy
//console.info('search results:', Search.lastresults);
// print the results
var resultCount = results.length;
function displayNextItem() {
// results left, load the summary and display it
if (results.length) {
var item = results.pop();
var listItem = $('<li style="display:none"></li>');
var requestUrl = "";
var linkUrl = "";
if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
// dirhtml builder
var dirname = item[0] + '/';
if (dirname.match(/\/index\/$/)) {
dirname = dirname.substring(0, dirname.length-6);
} else if (dirname == 'index/') {
dirname = '';
}
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
}
listItem.append($('<a/>').attr('href',
linkUrl +
highlightstring + item[2]).html(item[1]));
if (item[3]) {
listItem.append($('<span> (' + item[3] + ')</span>'));
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
$.ajax({url: requestUrl,
dataType: "text",
complete: function(jqxhr, textstatus) {
var data = jqxhr.responseText;
if (data !== '' && data !== undefined) {
listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
}
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
}});
} else {
// no source available, just display title
Search.output.append(listItem);
listItem.slideDown(5, function() {
displayNextItem();
});
}
}
// search finished, update title and status message
else {
Search.stopPulse();
Search.title.text(_('Search Results'));
if (!resultCount)
Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
else
Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
Search.status.fadeIn(500);
}
}
displayNextItem();
},
/**
* search for object names
*/
performObjectSearch : function(object, otherterms) {
var filenames = this._index.filenames;
var docnames = this._index.docnames;
var objects = this._index.objects;
var objnames = this._index.objnames;
var titles = this._index.titles;
var i;
var results = [];
for (var prefix in objects) {
for (var name in objects[prefix]) {
var fullname = (prefix ? prefix + '.' : '') + name;
var fullnameLower = fullname.toLowerCase()
if (fullnameLower.indexOf(object) > -1) {
var score = 0;
var parts = fullnameLower.split('.');
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower == object || parts[parts.length - 1] == object) {
score += Scorer.objNameMatch;
// matches in last name
} else if (parts[parts.length - 1].indexOf(object) > -1) {
score += Scorer.objPartialMatch;
}
var match = objects[prefix][name];
var objname = objnames[match[1]][2];
var title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
if (otherterms.length > 0) {
var haystack = (prefix + ' ' + name + ' ' +
objname + ' ' + title).toLowerCase();
var allfound = true;
for (i = 0; i < otherterms.length; i++) {
if (haystack.indexOf(otherterms[i]) == -1) {
allfound = false;
break;
}
}
if (!allfound) {
continue;
}
}
var descr = objname + _(', in ') + title;
var anchor = match[3];
if (anchor === '')
anchor = fullname;
else if (anchor == '-')
anchor = objnames[match[1]][1] + '-' + fullname;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2])) {
score += Scorer.objPrio[match[2]];
} else {
score += Scorer.objPrioDefault;
}
results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
}
}
}
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch : function(searchterms, excluded, terms, titleterms) {
var docnames = this._index.docnames;
var filenames = this._index.filenames;
var titles = this._index.titles;
var i, j, file;
var fileMap = {};
var scoreMap = {};
var results = [];
// perform the search on the required terms
for (i = 0; i < searchterms.length; i++) {
var word = searchterms[i];
var files = [];
var _o = [
{files: terms[word], score: Scorer.term},
{files: titleterms[word], score: Scorer.title}
];
// add support for partial matches
if (word.length > 2) {
for (var w in terms) {
if (w.match(word) && !terms[word]) {
_o.push({files: terms[w], score: Scorer.partialTerm})
}
}
for (var w in titleterms) {
if (w.match(word) && !titleterms[word]) {
_o.push({files: titleterms[w], score: Scorer.partialTitle})
}
}
}
// no match but word was a required one
if ($u.every(_o, function(o){return o.files === undefined;})) {
break;
}
// found search word in contents
$u.each(_o, function(o) {
var _files = o.files;
if (_files === undefined)
return
if (_files.length === undefined)
_files = [_files];
files = files.concat(_files);
// set score for the word in each file to Scorer.term
for (j = 0; j < _files.length; j++) {
file = _files[j];
if (!(file in scoreMap))
scoreMap[file] = {};
scoreMap[file][word] = o.score;
}
});
// create the mapping
for (j = 0; j < files.length; j++) {
file = files[j];
if (file in fileMap && fileMap[file].indexOf(word) === -1)
fileMap[file].push(word);
else
fileMap[file] = [word];
}
}
// now check if the files don't contain excluded terms
for (file in fileMap) {
var valid = true;
// check if all requirements are matched
var filteredTermCount = // as search terms with length < 3 are discarded: ignore
searchterms.filter(function(term){return term.length > 2}).length
if (
fileMap[file].length != searchterms.length &&
fileMap[file].length != filteredTermCount
) continue;
// ensure that none of the excluded terms is in the search result
for (i = 0; i < excluded.length; i++) {
if (terms[excluded[i]] == file ||
titleterms[excluded[i]] == file ||
$u.contains(terms[excluded[i]] || [], file) ||
$u.contains(titleterms[excluded[i]] || [], file)) {
valid = false;
break;
}
}
// if we have still a valid result we can add it to the result list
if (valid) {
// select one (max) score for the file.
// for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
results.push([docnames[file], titles[file], '', null, score, filenames[file]]);
}
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words, hlwords is the list of normal, unstemmed
* words. the first one is used to find the occurrence, the
* latter for highlighting it.
*/
makeSearchSummary : function(htmlText, keywords, hlwords) {
var text = Search.htmlToText(htmlText);
var textLower = text.toLowerCase();
var start = 0;
$.each(keywords, function() {
var i = textLower.indexOf(this.toLowerCase());
if (i > -1)
start = i;
});
start = Math.max(start - 120, 0);
var excerpt = ((start > 0) ? '...' : '') +
$.trim(text.substr(start, 240)) +
((start + 240 - text.length) ? '...' : '');
var rv = $('<div class="context"></div>').text(excerpt);
$.each(hlwords, function() {
rv = rv.highlightText(this, 'highlighted');
});
return rv;
}
};
$(document).ready(function() {
Search.init();
});

View File

@ -1,999 +0,0 @@
// Underscore.js 1.3.1
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var slice = ArrayProto.slice,
unshift = ArrayProto.unshift,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) { return new wrapper(obj); };
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root['_'] = _;
}
// Current version.
_.VERSION = '1.3.1';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (_.has(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results[results.length] = iterator.call(context, value, index, list);
});
if (obj.length === +obj.length) results.length = obj.length;
return results;
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var reversed = _.toArray(obj).reverse();
if (context && !initial) iterator = _.bind(iterator, context);
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, iterator, context) {
var result;
any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
each(obj, function(value, index, list) {
if (!iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if a given value is included in the array or object using `===`.
// Aliased as `contains`.
_.include = _.contains = function(obj, target) {
var found = false;
if (obj == null) return found;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
found = any(obj, function(value) {
return value === target;
});
return found;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
return _.map(obj, function(value) {
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; });
};
// Return the maximum element or (element-based computation).
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed >= result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Shuffle an array.
_.shuffle = function(obj) {
var shuffled = [], rand;
each(obj, function(value, index, list) {
if (index == 0) {
shuffled[0] = value;
} else {
rand = Math.floor(Math.random() * (index + 1));
shuffled[index] = shuffled[rand];
shuffled[rand] = value;
}
});
return shuffled;
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
return _.pluck(_.map(obj, function(value, index, list) {
return {
value : value,
criteria : iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}), 'value');
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = function(obj, val) {
var result = {};
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
each(obj, function(value, index) {
var key = iterator(value, index);
(result[key] || (result[key] = [])).push(value);
});
return result;
};
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator) {
iterator || (iterator = _.identity);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
return low;
};
// Safely convert anything iterable into a real, live array.
_.toArray = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
if (_.isArray(iterable)) return slice.call(iterable);
if (_.isArguments(iterable)) return slice.call(iterable);
return _.values(iterable);
};
// Return the number of elements in an object.
_.size = function(obj) {
return _.toArray(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head`. The **guard** check allows it to work
// with `_.map`.
_.first = _.head = function(array, n, guard) {
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};
// Returns everything but the last entry of the array. Especcialy useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if ((n != null) && !guard) {
return slice.call(array, Math.max(array.length - n, 0));
} else {
return array[array.length - 1];
}
};
// Returns everything but the first entry of the array. Aliased as `tail`.
// Especially useful on the arguments object. Passing an **index** will return
// the rest of the values in the array from that index onward. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = function(array, index, guard) {
return slice.call(array, (index == null) || guard ? 1 : index);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, function(value){ return !!value; });
};
// Return a completely flattened version of an array.
_.flatten = function(array, shallow) {
return _.reduce(array, function(memo, value) {
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
memo[memo.length] = value;
return memo;
}, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator) {
var initial = iterator ? _.map(array, iterator) : array;
var result = [];
_.reduce(initial, function(memo, el, i) {
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
memo[memo.length] = el;
result[result.length] = array[i];
}
return memo;
}, []);
return result;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays. (Aliased as "intersect" for back-compat.)
_.intersection = _.intersect = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = _.flatten(slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.include(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var args = slice.call(arguments);
var length = _.max(_.pluck(args, 'length'));
var results = new Array(length);
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
return results;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i, l;
if (isSorted) {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item) {
if (array == null) return -1;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
var i = array.length;
while (i--) if (i in array && array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var len = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(len);
while(idx < len) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`.
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
// We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function bind(func, context) {
var bound, args;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length == 0) funcs = _.functions(obj);
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(func, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function(func, wait) {
var context, args, timeout, throttling, more;
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
return function() {
context = this; args = arguments;
var later = function() {
timeout = null;
if (more) func.apply(context, args);
whenDone();
};
if (!timeout) timeout = setTimeout(later, wait);
if (throttling) {
more = true;
} else {
func.apply(context, args);
}
whenDone();
throttling = true;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
_.debounce = function(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
return memo = func.apply(this, arguments);
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
var args = [func].concat(slice.call(arguments, 0));
return wrapper.apply(this, args);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
if (times <= 0) return func();
return function() {
if (--times < 1) { return func.apply(this, arguments); }
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
return _.map(obj, _.identity);
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
obj[prop] = source[prop];
}
});
return obj;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
if (obj[prop] == null) obj[prop] = source[prop];
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function.
function eq(a, b, stack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
if (b._chain) b = b._wrapped;
// Invoke a custom `isEqual` method if one is provided.
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = stack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (stack[length] == a) return true;
}
// Add the first object to the stack of traversed objects.
stack.push(a);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
// Ensure commutative equality for sparse arrays.
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
}
}
} else {
// Objects with different constructors are not equivalent.
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
stack.pop();
return result;
}
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType == 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Is a given variable an arguments object?
_.isArguments = function(obj) {
return toString.call(obj) == '[object Arguments]';
};
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Is a given value a function?
_.isFunction = function(obj) {
return toString.call(obj) == '[object Function]';
};
// Is a given value a string?
_.isString = function(obj) {
return toString.call(obj) == '[object String]';
};
// Is a given value a number?
_.isNumber = function(obj) {
return toString.call(obj) == '[object Number]';
};
// Is the given value `NaN`?
_.isNaN = function(obj) {
// `NaN` is the only value for which `===` is not reflexive.
return obj !== obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value a date?
_.isDate = function(obj) {
return toString.call(obj) == '[object Date]';
};
// Is the given value a regular expression?
_.isRegExp = function(obj) {
return toString.call(obj) == '[object RegExp]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Has own property?
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
// Run a function **n** times.
_.times = function (n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
};
// Escape a string for HTML interpolation.
_.escape = function(string) {
return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
};
// Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well.
_.mixin = function(obj) {
each(_.functions(obj), function(name){
addToWrapper(name, _[name] = obj[name]);
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = idCounter++;
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /.^/;
// Within an interpolation, evaluation, or escaping, remove HTML escaping
// that had been previously added.
var unescape = function(code) {
return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(str, data) {
var c = _.templateSettings;
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' +
str.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(c.escape || noMatch, function(match, code) {
return "',_.escape(" + unescape(code) + "),'";
})
.replace(c.interpolate || noMatch, function(match, code) {
return "'," + unescape(code) + ",'";
})
.replace(c.evaluate || noMatch, function(match, code) {
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
})
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
+ "');}return __p.join('');";
var func = new Function('obj', '_', tmpl);
if (data) return func(data, _);
return function(data) {
return func.call(this, data, _);
};
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// The OOP Wrapper
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
var wrapper = function(obj) { this._wrapped = obj; };
// Expose `wrapper.prototype` as `_.prototype`
_.prototype = wrapper.prototype;
// Helper function to continue chaining intermediate results.
var result = function(obj, chain) {
return chain ? _(obj).chain() : obj;
};
// A method to easily add functions to the OOP wrapper.
var addToWrapper = function(name, func) {
wrapper.prototype[name] = function() {
var args = slice.call(arguments);
unshift.call(args, this._wrapped);
return result(func.apply(_, args), this._chain);
};
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
var wrapped = this._wrapped;
method.apply(wrapped, arguments);
var length = wrapped.length;
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
return result(wrapped, this._chain);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
return result(method.apply(this._wrapped, arguments), this._chain);
};
});
// Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function() {
this._chain = true;
return this;
};
// Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function() {
return this._wrapped;
};
}).call(this);

View File

@ -1,31 +0,0 @@
// Underscore.js 1.3.1
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),
function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

File diff suppressed because it is too large Load Diff

View File

@ -1,185 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Contents &mdash; fmt 8.0.1 documentation</title>
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/breathe.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '8.0.1',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<meta name="description" content="Small, safe and fast formatting library">
<meta name="keywords" content="C++, formatting, printf, string, library">
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
</script>
</head>
<body role="document">
<nav class="navbar navbar-inverse">
<div class="tb-container">
<div class="row">
<div class="navbar-content">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">{fmt}</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false">8.0.1
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://fmt.dev/8.0.1">8.0.1</a></li>
<li><a href="https://fmt.dev/8.0.0">8.0.0</a></li>
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
</ul>
</li>
<li class="active"><a href="contents.html">Contents
<span class="sr-only">(current)</span></a></li>
<li><a href="usage.html">Usage</a></li>
<li><a href="api.html">API</a></li>
<li><a href="syntax.html">Syntax</a></li>
</ul>
<form class="navbar-form navbar-right" role="search" action="search.html"
method="get">
<div class="form-group">
<input type="text" name="q" class="form-control"
placeholder="Search" >
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
</div>
</div>
</nav>
<div class="tb-container">
<div class="row">
<div class="content">
<section id="contents">
<h1>Contents<a class="headerlink" href="#contents" title="Permalink to this headline"></a></h1>
<div class="toctree-wrapper compound">
<ul>
<li class="toctree-l1"><a class="reference internal" href="usage.html">Usage</a><ul>
<li class="toctree-l2"><a class="reference internal" href="usage.html#building-the-library">Building the Library</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#installing-the-library">Installing the Library</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#usage-with-cmake">Usage with CMake</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#usage-with-build2">Usage with build2</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#building-the-documentation">Building the Documentation</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#conda">Conda</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#vcpkg">Vcpkg</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#lhelper">LHelper</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#android-ndk">Android NDK</a></li>
<li class="toctree-l2"><a class="reference internal" href="usage.html#homebrew">Homebrew</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="api.html">API Reference</a><ul>
<li class="toctree-l2"><a class="reference internal" href="api.html#core-api">Core API</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#format-api">Format API</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#ranges-and-tuple-formatting">Ranges and Tuple Formatting</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#date-and-time-formatting">Date and Time Formatting</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#format-string-compilation">Format string compilation</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#terminal-color-and-text-style">Terminal color and text style</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#system-apis">System APIs</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#std-ostream-support"><code class="docutils literal notranslate"><span class="pre">std::ostream</span></code> Support</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#printf-formatting"><code class="docutils literal notranslate"><span class="pre">printf</span></code> Formatting</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#wchar-t-support"><code class="docutils literal notranslate"><span class="pre">wchar_t</span></code> Support</a></li>
<li class="toctree-l2"><a class="reference internal" href="api.html#compatibility-with-c-20-std-format">Compatibility with C++20 <code class="docutils literal notranslate"><span class="pre">std::format</span></code></a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="syntax.html">Format String Syntax</a><ul>
<li class="toctree-l2"><a class="reference internal" href="syntax.html#format-specification-mini-language">Format Specification Mini-Language</a></li>
<li class="toctree-l2"><a class="reference internal" href="syntax.html#chrono-format-specifications">Chrono Format Specifications</a></li>
<li class="toctree-l2"><a class="reference internal" href="syntax.html#format-examples">Format Examples</a></li>
</ul>
</li>
</ul>
</div>
</section>
</div>
</div>
</div>
<div class="footer" role="contentinfo">
&copy; Copyright 2012-present, Victor Zverovich.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.3.0.
</div>
<script src="_static/bootstrap.min.js"></script>
</body>
</html>

View File

@ -1,302 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Index &mdash; fmt 8.0.1 documentation</title>
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/breathe.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '8.0.1',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/language_data.js"></script>
<link rel="index" title="Index" href="#" />
<link rel="search" title="Search" href="search.html" />
<meta name="description" content="Small, safe and fast formatting library">
<meta name="keywords" content="C++, formatting, printf, string, library">
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
</script>
</head>
<body role="document">
<nav class="navbar navbar-inverse">
<div class="tb-container">
<div class="row">
<div class="navbar-content">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">{fmt}</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false">8.0.1
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://fmt.dev/8.0.1">8.0.1</a></li>
<li><a href="https://fmt.dev/8.0.0">8.0.0</a></li>
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
</ul>
</li>
<li><a href="contents.html">Contents</a></li>
<li><a href="usage.html">Usage</a></li>
<li><a href="api.html">API</a></li>
<li><a href="syntax.html">Syntax</a></li>
</ul>
<form class="navbar-form navbar-right" role="search" action="search.html"
method="get">
<div class="form-group">
<input type="text" name="q" class="form-control"
placeholder="Search" >
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
</div>
</div>
</nav>
<div class="tb-container">
<div class="row">
<div class="content">
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
<a href="#F"><strong>F</strong></a>
</div>
<h2 id="F">F</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="api.html#_CPPv4I00EN3fmt3argEN6detail9named_argI4Char1TEEPK4CharRK1T">fmt::arg (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt16basic_format_argE">fmt::basic_format_arg (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt17basic_format_argsE">fmt::basic_format_args (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4IDpEN3fmt17basic_format_args17basic_format_argsERK16format_arg_storeI7ContextDp4ArgsE">fmt::basic_format_args::basic_format_args (C++ function)</a>, <a href="api.html#_CPPv4N3fmt17basic_format_args17basic_format_argsEPK10format_argi">[1]</a>, <a href="api.html#_CPPv4N3fmt17basic_format_args17basic_format_argsERK24dynamic_format_arg_storeI7ContextE">[2]</a>
</li>
<li><a href="api.html#_CPPv4NK3fmt17basic_format_args3getEi">fmt::basic_format_args::get (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I00EN3fmt20basic_format_contextE">fmt::basic_format_context (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt20basic_format_context20basic_format_contextE8OutputIt17basic_format_argsI20basic_format_contextEN6detail10locale_refE">fmt::basic_format_context::basic_format_context (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt20basic_format_context9char_typeE">fmt::basic_format_context::char_type (C++ type)</a>
</li>
<li><a href="api.html#_CPPv4I00EN3fmt26basic_format_parse_contextE">fmt::basic_format_parse_context (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt26basic_format_parse_context10advance_toE8iterator">fmt::basic_format_parse_context::advance_to (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4NK3fmt26basic_format_parse_context5beginEv">fmt::basic_format_parse_context::begin (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt26basic_format_parse_context12check_arg_idEi">fmt::basic_format_parse_context::check_arg_id (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4NK3fmt26basic_format_parse_context3endEv">fmt::basic_format_parse_context::end (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt26basic_format_parse_context11next_arg_idEv">fmt::basic_format_parse_context::next_arg_id (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0_6size_t0EN3fmt19basic_memory_bufferE">fmt::basic_memory_buffer (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt19basic_memory_buffer19basic_memory_bufferERR19basic_memory_buffer">fmt::basic_memory_buffer::basic_memory_buffer (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt19basic_memory_buffer4growE6size_t">fmt::basic_memory_buffer::grow (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt19basic_memory_bufferaSERR19basic_memory_buffer">fmt::basic_memory_buffer::operator= (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt19basic_memory_buffer7reserveE6size_t">fmt::basic_memory_buffer::reserve (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt19basic_memory_buffer6resizeE6size_t">fmt::basic_memory_buffer::resize (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt17basic_string_viewE">fmt::basic_string_view (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4I00EN3fmt17basic_string_view17basic_string_viewERKNSt12basic_stringI4Char6Traits5AllocEE">fmt::basic_string_view::basic_string_view (C++ function)</a>, <a href="api.html#_CPPv4N3fmt17basic_string_view17basic_string_viewEPK4Char">[1]</a>, <a href="api.html#_CPPv4N3fmt17basic_string_view17basic_string_viewEPK4Char6size_t">[2]</a>
</li>
<li><a href="api.html#_CPPv4NK3fmt17basic_string_view4dataEv">fmt::basic_string_view::data (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4NK3fmt17basic_string_view4sizeEv">fmt::basic_string_view::size (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt2bgEN6detail10color_typeE">fmt::bg (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt13buffered_file6vprintE11string_view11format_args">fmt::buffered_file::vprint (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt6detail6bufferE">fmt::detail::buffer (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt6detail6buffer6appendEvPK1UPK1U">fmt::detail::buffer::append (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4NK3fmt6detail6buffer8capacityEv">fmt::detail::buffer::capacity (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt6detail6buffer5clearEv">fmt::detail::buffer::clear (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt6detail6buffer4dataEv">fmt::detail::buffer::data (C++ function)</a>, <a href="api.html#_CPPv4NK3fmt6detail6buffer4dataEv">[1]</a>
</li>
<li><a href="api.html#_CPPv4NK3fmt6detail6buffer4sizeEv">fmt::detail::buffer::size (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt24dynamic_format_arg_storeE">fmt::dynamic_format_arg_store (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt2fgEN6detail10color_typeE">fmt::fg (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4IDpEN3fmt6formatENSt6stringE13format_stringIDp1TEDpRR1T">fmt::format (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0DpEN3fmt16format_arg_storeE">fmt::format_arg_store (C++ class)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="api.html#_CPPv4N3fmt11format_argsE">fmt::format_args (C++ type)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt14format_contextE">fmt::format_context (C++ type)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt19format_system_errorERN6detail6bufferIcEEiPKc">fmt::format_system_error (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0DpEN3fmt9format_toE8OutputIt8OutputIt13format_stringIDp1TEDpRR1T">fmt::format_to (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0DpEN3fmt11format_to_nE18format_to_n_resultI8OutputItE8OutputIt6size_t13format_stringIDp1TEDpRK1T">fmt::format_to_n (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt18format_to_n_resultE">fmt::format_to_n_result (C++ struct)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt18format_to_n_result3outE">fmt::format_to_n_result::out (C++ member)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt18format_to_n_result4sizeE">fmt::format_to_n_result::size (C++ member)</a>
</li>
<li><a href="api.html#_CPPv4IDpEN3fmt14formatted_sizeE6size_t13format_stringIDp1TEDpRR1T">fmt::formatted_size (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0Dp0EN3fmt7fprintfEiPNSt4FILEERK1SDpRK1T">fmt::fprintf (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt6gmtimeENSt6time_tE">fmt::gmtime (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt7is_charE">fmt::is_char (C++ struct)</a>
</li>
<li><a href="api.html#_CPPv4I00EN3fmt4joinE9join_viewI2It8SentinelE2It8Sentinel11string_view">fmt::join (C++ function)</a>, <a href="api.html#_CPPv4I0EN3fmt4joinE9join_viewIN6detail10iterator_tI5RangeEEN6detail10sentinel_tI5RangeEEERR5Range11string_view">[1]</a>
</li>
<li><a href="api.html#_CPPv4N3fmt9localtimeENSt6time_tE">fmt::localtime (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4IDp00EN3fmt17make_args_checkedE16format_arg_storeI14buffer_contextI4CharEDp18remove_reference_tI4ArgsEERK1SDpRK18remove_reference_tI4ArgsE">fmt::make_args_checked (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0DpEN3fmt16make_format_argsE16format_arg_storeI7ContextDp4ArgsEDpRK4Args">fmt::make_format_args (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmtli2_aEPKc6size_t">fmt::operator&#34;&#34;_a (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmtli7_formatEPKc6size_t">fmt::operator&#34;&#34;_format (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt7ostreamE">fmt::ostream (C++ class)</a>
</li>
<li><a href="api.html#_CPPv4IDpEN3fmt7ostream11output_fileE7ostream12cstring_viewDp1T">fmt::ostream::output_file (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4IDpEN3fmt7ostream5printEv13format_stringIDp1TEDpRR1T">fmt::ostream::print (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0Dp0EN3fmt5printEvRNSt13basic_ostreamI4CharEERK1SDpRR4Args">fmt::print (C++ function)</a>, <a href="api.html#_CPPv4I0DpEN3fmt5printEvRK10text_styleRK1SDpRK4Args">[1]</a>, <a href="api.html#_CPPv4IDpEN3fmt5printEv13format_stringIDp1TEDpRR1T">[2]</a>, <a href="api.html#_CPPv4IDpEN3fmt5printEvPNSt4FILEE13format_stringIDp1TEDpRR1T">[3]</a>
</li>
<li><a href="api.html#_CPPv4I0DpEN3fmt6printfEiRK1SDpRK1T">fmt::printf (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt3ptrEPKv1T">fmt::ptr (C++ function)</a>, <a href="api.html#_CPPv4I0EN3fmt3ptrEPKvRKNSt10shared_ptrI1TEE">[1]</a>, <a href="api.html#_CPPv4I0EN3fmt3ptrEPKvRKNSt10unique_ptrI1TEE">[2]</a>
</li>
<li><a href="api.html#_CPPv4I0Dp0EN3fmt7sprintfENSt12basic_stringI4CharEERK1SDpRK1T">fmt::sprintf (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt11string_viewE">fmt::string_view (C++ type)</a>
</li>
<li><a href="api.html#_CPPv4IDpEN3fmt12system_errorENSt12system_errorEi13format_stringIDp1TEDpRR1T">fmt::system_error (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt9to_stringENSt6stringERK1T">fmt::to_string (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt14to_string_viewE17basic_string_viewI4CharEPK4Char">fmt::to_string_view (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4I0EN3fmt10to_wstringENSt7wstringERK1T">fmt::to_wstring (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt7vformatE11string_view11format_args">fmt::vformat (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt6vprintEPNSt4FILEE11string_view11format_args">fmt::vprint (C++ function)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt15wformat_contextE">fmt::wformat_context (C++ type)</a>
</li>
<li><a href="api.html#_CPPv4N3fmt12wstring_viewE">fmt::wstring_view (C++ type)</a>
</li>
<li><a href="api.html#c.FMT_COMPILE">FMT_COMPILE (C macro)</a>
</li>
<li><a href="api.html#c.FMT_STRING">FMT_STRING (C macro)</a>
</li>
</ul></td>
</tr></table>
</div>
</div>
</div>
<div class="footer" role="contentinfo">
&copy; Copyright 2012-present, Victor Zverovich.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.3.0.
</div>
<script src="_static/bootstrap.min.js"></script>
</body>
</html>

View File

@ -1,323 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Overview &mdash; fmt 8.0.1 documentation</title>
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/breathe.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '8.0.1',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<meta name="description" content="Small, safe and fast formatting library">
<meta name="keywords" content="C++, formatting, printf, string, library">
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
</script>
</head>
<body role="document">
<nav class="navbar navbar-inverse">
<div class="tb-container">
<div class="row">
<div class="navbar-content">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">{fmt}</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false">8.0.1
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://fmt.dev/8.0.1">8.0.1</a></li>
<li><a href="https://fmt.dev/8.0.0">8.0.0</a></li>
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
</ul>
</li>
<li><a href="contents.html">Contents</a></li>
<li><a href="usage.html">Usage</a></li>
<li><a href="api.html">API</a></li>
<li><a href="syntax.html">Syntax</a></li>
</ul>
<form class="navbar-form navbar-right" role="search" action="search.html"
method="get">
<div class="form-group">
<input type="text" name="q" class="form-control"
placeholder="Search" >
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
</div>
</div>
</nav>
<div class="jumbotron">
<div class="tb-container">
<h1>{fmt}</h1>
<p class="lead">A modern formatting library</p>
<div class="btn-group" role="group">
<a class="btn btn-success"
href="https://github.com/fmtlib/fmt/releases/download/8.0.1/fmt-8.0.1.zip">
<span class="glyphicon glyphicon-download"></span> Download
</a>
<button type="button" class="btn btn-success dropdown-toggle"
data-toggle="dropdown"><span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href="https://github.com/fmtlib/fmt/releases/download/8.0.1/fmt-8.0.1.zip">Version 8.0.1
</a></li>
<li><a href="https://github.com/fmtlib/fmt/releases/download/8.0.0/fmt-8.0.0.zip">Version 8.0.0
</a></li>
<li><a href="https://github.com/fmtlib/fmt/releases/download/7.1.3/fmt-7.1.3.zip">Version 7.1.3
</a></li>
</ul>
</div>
</div>
</div>
<div class="tb-container">
<div class="row">
<div class="content">
<section id="overview">
<h1>Overview<a class="headerlink" href="#overview" title="Permalink to this headline"></a></h1>
<p><strong>{fmt}</strong> is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.</p>
<div class="panel panel-default">
<div class="panel-heading">What users say:</div>
<div class="panel-body">
Thanks for creating this library. Its been a hole in C++ for
a long time. Ive used both <code>boost::format</code> and
<code>loki::SPrintf</code>, and neither felt like the right answer.
This does.
</div>
</div><section id="format-api">
<span id="format-api-intro"></span><h2>Format API<a class="headerlink" href="#format-api" title="Permalink to this headline"></a></h2>
<p>The format API is similar in spirit to the C <code class="docutils literal notranslate"><span class="pre">printf</span></code> family of function but
is safer, simpler and several times <a class="reference external" href="https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html">faster</a>
than common standard library implementations.
The <a class="reference external" href="syntax.html">format string syntax</a> is similar to the one used by
<a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#str.format">str.format</a> in
Python:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;The answer is {}.&quot;</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">fmt::format</span></code> function returns a string “The answer is 42.”. You can use
<code class="docutils literal notranslate"><span class="pre">fmt::memory_buffer</span></code> to avoid constructing <code class="docutils literal notranslate"><span class="pre">std::string</span></code>:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="k">auto</span> <span class="n">out</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">::</span><span class="n">memory_buffer</span><span class="p">();</span>
<span class="n">format_to</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">back_inserter</span><span class="p">(</span><span class="n">out</span><span class="p">),</span>
<span class="s">&quot;For a moment, {} happened.&quot;</span><span class="p">,</span> <span class="s">&quot;nothing&quot;</span><span class="p">);</span>
<span class="k">auto</span> <span class="n">data</span> <span class="o">=</span> <span class="n">out</span><span class="p">.</span><span class="n">data</span><span class="p">();</span> <span class="c1">// pointer to the formatted data</span>
<span class="k">auto</span> <span class="n">size</span> <span class="o">=</span> <span class="n">out</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="c1">// size of the formatted data</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">fmt::print</span></code> function performs formatting and writes the result to a stream:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">&quot;System error code = {}</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">errno</span><span class="p">);</span>
</pre></div>
</div>
<p>If you omit the file argument the function will print to <code class="docutils literal notranslate"><span class="pre">stdout</span></code>:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">&quot;Don&#39;t {}</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="s">&quot;panic&quot;</span><span class="p">);</span>
</pre></div>
</div>
<p>The format API also supports positional arguments useful for localization:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">&quot;I&#39;d rather be {1} than {0}.&quot;</span><span class="p">,</span> <span class="s">&quot;right&quot;</span><span class="p">,</span> <span class="s">&quot;happy&quot;</span><span class="p">);</span>
</pre></div>
</div>
<p>You can pass named arguments with <code class="docutils literal notranslate"><span class="pre">fmt::arg</span></code>:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">&quot;Hello, {name}! The answer is {number}. Goodbye, {name}.&quot;</span><span class="p">,</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">arg</span><span class="p">(</span><span class="s">&quot;name&quot;</span><span class="p">,</span> <span class="s">&quot;World&quot;</span><span class="p">),</span> <span class="n">fmt</span><span class="o">::</span><span class="n">arg</span><span class="p">(</span><span class="s">&quot;number&quot;</span><span class="p">,</span> <span class="mi">42</span><span class="p">));</span>
</pre></div>
</div>
<p>If your compiler supports C++11 user-defined literals, the suffix <code class="docutils literal notranslate"><span class="pre">_a</span></code> offers
an alternative, slightly terser syntax for named arguments:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="k">using</span> <span class="k">namespace</span> <span class="nn">fmt</span><span class="o">::</span><span class="nn">literals</span><span class="p">;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">&quot;Hello, {name}! The answer is {number}. Goodbye, {name}.&quot;</span><span class="p">,</span>
<span class="s">&quot;name&quot;</span><span class="n">_a</span><span class="o">=</span><span class="s">&quot;World&quot;</span><span class="p">,</span> <span class="s">&quot;number&quot;</span><span class="n">_a</span><span class="o">=</span><span class="mi">42</span><span class="p">);</span>
</pre></div>
</div>
</section>
<section id="safety">
<span id="id1"></span><h2>Safety<a class="headerlink" href="#safety" title="Permalink to this headline"></a></h2>
<p>The library is fully type safe, automatic memory management prevents buffer
overflow, errors in format strings are reported using exceptions or at compile
time. For example, the code</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;The answer is {:d}&quot;</span><span class="p">,</span> <span class="s">&quot;forty-two&quot;</span><span class="p">);</span>
</pre></div>
</div>
<p>throws the <code class="docutils literal notranslate"><span class="pre">format_error</span></code> exception because the argument <code class="docutils literal notranslate"><span class="pre">&quot;forty-two&quot;</span></code> is a
string while the format code <code class="docutils literal notranslate"><span class="pre">d</span></code> only applies to integers.</p>
<p>The code</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">format</span><span class="p">(</span><span class="n">FMT_STRING</span><span class="p">(</span><span class="s">&quot;The answer is {:d}&quot;</span><span class="p">),</span> <span class="s">&quot;forty-two&quot;</span><span class="p">);</span>
</pre></div>
</div>
<p>reports a compile-time error on compilers that support relaxed <code class="docutils literal notranslate"><span class="pre">constexpr</span></code>.
See <a class="reference external" href="api.html#c.fmt">here</a> for details.</p>
<p>The following code</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span>fmt::format(&quot;Cyrillic letter {}&quot;, L&#39;\x42e&#39;);
</pre></div>
</div>
<p>produces a compile-time error because wide character <code class="docutils literal notranslate"><span class="pre">L'\x42e'</span></code> cannot be
formatted into a narrow string. For comparison, writing a wide character to
<code class="docutils literal notranslate"><span class="pre">std::ostream</span></code> results in its numeric value being written to the stream
(i.e. 1070 instead of letter ‘ю’ which is represented by <code class="docutils literal notranslate"><span class="pre">L'\x42e'</span></code> if we
use Unicode) which is rarely desirable.</p>
</section>
<section id="compact-binary-code">
<h2>Compact Binary Code<a class="headerlink" href="#compact-binary-code" title="Permalink to this headline"></a></h2>
<p>The library produces compact per-call compiled code. For example
(<a class="reference external" href="https://godbolt.org/g/TZU4KF">godbolt</a>),</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf">&lt;fmt/core.h&gt;</span><span class="cp"></span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">&quot;The answer is {}.&quot;</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
</div>
<p>compiles to just</p>
<div class="highlight-asm notranslate"><div class="highlight"><pre><span></span>main: # @main
sub rsp, 24
mov qword ptr [rsp], 42
mov rcx, rsp
mov edi, offset .L.str
mov esi, 17
mov edx, 1
call fmt::v7::vprint(fmt::v7::basic_string_view&lt;char&gt;, fmt::v7::format_args)
xor eax, eax
add rsp, 24
ret
.L.str:
.asciz &quot;The answer is {}.&quot;
</pre></div>
</div>
</section>
<section id="portability">
<span id="id2"></span><h2>Portability<a class="headerlink" href="#portability" title="Permalink to this headline"></a></h2>
<p>The library is highly portable and relies only on a small set of C++11 features:</p>
<ul class="simple">
<li><p>variadic templates</p></li>
<li><p>type traits</p></li>
<li><p>rvalue references</p></li>
<li><p>decltype</p></li>
<li><p>trailing return types</p></li>
<li><p>deleted functions</p></li>
<li><p>alias templates</p></li>
</ul>
<p>These are available in GCC 4.8, Clang 3.4, MSVC 19.0 (2015) and more recent
compiler version. For older compilers use {fmt} <a class="reference external" href="https://github.com/fmtlib/fmt/releases/tag/4.1.0">version 4.x</a> which is maintained and
only requires C++98.</p>
<p>The output of all formatting functions is consistent across platforms.
For example,</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">&quot;{}&quot;</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">numeric_limits</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;::</span><span class="n">infinity</span><span class="p">());</span>
</pre></div>
</div>
<p>always prints <code class="docutils literal notranslate"><span class="pre">inf</span></code> while the output of <code class="docutils literal notranslate"><span class="pre">printf</span></code> is platform-dependent.</p>
</section>
<section id="ease-of-use">
<span id="id3"></span><h2>Ease of Use<a class="headerlink" href="#ease-of-use" title="Permalink to this headline"></a></h2>
<p>{fmt} has a small self-contained code base with the core library consisting of
just three header files and no external dependencies.
A permissive MIT <a class="reference external" href="https://github.com/fmtlib/fmt#license">license</a> allows
using the library both in open-source and commercial projects.</p>
<p><a class="reference external" href="contents.html">Learn more…</a></p>
<a class="btn btn-success" href="https://github.com/fmtlib/fmt">GitHub Repository</a>
<div class="section footer">
<iframe src="https://ghbtns.com/github-btn.html?user=fmtlib&amp;repo=fmt&amp;type=watch&amp;count=true"
class="github-btn" width="100" height="20"></iframe>
</div></section>
</section>
</div>
</div>
</div>
<div class="footer" role="contentinfo">
&copy; Copyright 2012-present, Victor Zverovich.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.3.0.
</div>
<script src="_static/bootstrap.min.js"></script>
</body>
</html>

Binary file not shown.

View File

@ -1,169 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Search &mdash; fmt 8.0.1 documentation</title>
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/breathe.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '8.0.1',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/language_data.js"></script>
<script type="text/javascript" src="_static/searchtools.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="#" />
<script type="text/javascript">
jQuery(function() { Search.loadIndex("searchindex.js"); });
</script>
<script type="text/javascript" id="searchindexloader"></script>
<meta name="description" content="Small, safe and fast formatting library">
<meta name="keywords" content="C++, formatting, printf, string, library">
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
</script>
</head>
<body role="document">
<nav class="navbar navbar-inverse">
<div class="tb-container">
<div class="row">
<div class="navbar-content">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">{fmt}</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false">8.0.1
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://fmt.dev/8.0.1">8.0.1</a></li>
<li><a href="https://fmt.dev/8.0.0">8.0.0</a></li>
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
</ul>
</li>
<li><a href="contents.html">Contents</a></li>
<li><a href="usage.html">Usage</a></li>
<li><a href="api.html">API</a></li>
<li><a href="syntax.html">Syntax</a></li>
</ul>
</div>
</div>
</div>
</div>
</nav>
<div class="tb-container">
<div class="row">
<div class="content">
<h1 id="search-documentation">Search</h1>
<div id="fallback" class="admonition warning">
<script type="text/javascript">$('#fallback').hide();</script>
<p>
Please activate JavaScript to enable the search
functionality.
</p>
</div>
<p>
From here you can search these documents. Enter your search
words into the box below and click "search". Note that the search
function will automatically search for all of the words. Pages
containing fewer words won't appear in the result list.
</p>
<form class="form-inline" role="search" action="#"
method="get">
<div class="form-group">
<input type="text" name="q" class="form-control"
>
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
<input type="submit" class="btn btn-default" value="search">
</form>
<div id="search-results">
</div>
</div>
</div>
</div>
<div class="footer" role="contentinfo">
&copy; Copyright 2012-present, Victor Zverovich.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.3.0.
</div>
<script src="_static/bootstrap.min.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,630 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Format String Syntax &mdash; fmt 8.0.1 documentation</title>
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/breathe.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '8.0.1',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<meta name="description" content="Small, safe and fast formatting library">
<meta name="keywords" content="C++, formatting, printf, string, library">
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
</script>
</head>
<body role="document">
<nav class="navbar navbar-inverse">
<div class="tb-container">
<div class="row">
<div class="navbar-content">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">{fmt}</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false">8.0.1
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://fmt.dev/8.0.1">8.0.1</a></li>
<li><a href="https://fmt.dev/8.0.0">8.0.0</a></li>
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
</ul>
</li>
<li><a href="contents.html">Contents</a></li>
<li><a href="usage.html">Usage</a></li>
<li><a href="api.html">API</a></li>
<li class="active"><a href="syntax.html">Syntax
<span class="sr-only">(current)</span></a></li>
</ul>
<form class="navbar-form navbar-right" role="search" action="search.html"
method="get">
<div class="form-group">
<input type="text" name="q" class="form-control"
placeholder="Search" >
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
</div>
</div>
</nav>
<div class="tb-container">
<div class="row">
<div class="content">
<section id="format-string-syntax">
<span id="syntax"></span><h1>Format String Syntax<a class="headerlink" href="#format-string-syntax" title="Permalink to this headline"></a></h1>
<p>Formatting functions such as <a class="reference internal" href="api.html#format"><span class="std std-ref">fmt::format()</span></a> and
<a class="reference internal" href="api.html#print"><span class="std std-ref">fmt::print()</span></a> use the same format string syntax described in this
section.</p>
<p>Format strings contain “replacement fields” surrounded by curly braces <code class="docutils literal notranslate"><span class="pre">{}</span></code>.
Anything that is not contained in braces is considered literal text, which is
copied unchanged to the output. If you need to include a brace character in the
literal text, it can be escaped by doubling: <code class="docutils literal notranslate"><span class="pre">{{</span></code> and <code class="docutils literal notranslate"><span class="pre">}}</span></code>.</p>
<p>The grammar for a replacement field is as follows:</p>
<pre>
<strong id="grammar-token-sf-replacement_field"><span id="grammar-token-replacement-field"></span>replacement_field</strong> ::= &quot;{&quot; [<a class="reference internal" href="#grammar-token-sf-arg_id"><code class="xref docutils literal notranslate"><span class="pre">arg_id</span></code></a>] [&quot;:&quot; (<a class="reference internal" href="#grammar-token-sf-format_spec"><code class="xref docutils literal notranslate"><span class="pre">format_spec</span></code></a> | <a class="reference internal" href="#grammar-token-sf-chrono_format_spec"><code class="xref docutils literal notranslate"><span class="pre">chrono_format_spec</span></code></a>)] &quot;}&quot;
<strong id="grammar-token-sf-arg_id"><span id="grammar-token-arg-id"></span>arg_id </strong> ::= <a class="reference internal" href="#grammar-token-sf-integer"><code class="xref docutils literal notranslate"><span class="pre">integer</span></code></a> | <a class="reference internal" href="#grammar-token-sf-identifier"><code class="xref docutils literal notranslate"><span class="pre">identifier</span></code></a>
<strong id="grammar-token-sf-integer"><span id="grammar-token-integer"></span>integer </strong> ::= <a class="reference internal" href="#grammar-token-sf-digit"><code class="xref docutils literal notranslate"><span class="pre">digit</span></code></a>+
<strong id="grammar-token-sf-digit"><span id="grammar-token-digit"></span>digit </strong> ::= &quot;0&quot;...&quot;9&quot;
<strong id="grammar-token-sf-identifier"><span id="grammar-token-identifier"></span>identifier </strong> ::= <a class="reference internal" href="#grammar-token-sf-id_start"><code class="xref docutils literal notranslate"><span class="pre">id_start</span></code></a> <a class="reference internal" href="#grammar-token-sf-id_continue"><code class="xref docutils literal notranslate"><span class="pre">id_continue</span></code></a>*
<strong id="grammar-token-sf-id_start"><span id="grammar-token-id-start"></span>id_start </strong> ::= &quot;a&quot;...&quot;z&quot; | &quot;A&quot;...&quot;Z&quot; | &quot;_&quot;
<strong id="grammar-token-sf-id_continue"><span id="grammar-token-id-continue"></span>id_continue </strong> ::= <a class="reference internal" href="#grammar-token-sf-id_start"><code class="xref docutils literal notranslate"><span class="pre">id_start</span></code></a> | <a class="reference internal" href="#grammar-token-sf-digit"><code class="xref docutils literal notranslate"><span class="pre">digit</span></code></a>
</pre>
<p>In less formal terms, the replacement field can start with an <em>arg_id</em>
that specifies the argument whose value is to be formatted and inserted into
the output instead of the replacement field.
The <em>arg_id</em> is optionally followed by a <em>format_spec</em>, which is preceded by a
colon <code class="docutils literal notranslate"><span class="pre">':'</span></code>. These specify a non-default format for the replacement value.</p>
<p>See also the <a class="reference internal" href="#formatspec"><span class="std std-ref">Format Specification Mini-Language</span></a> section.</p>
<p>If the numerical arg_ids in a format string are 0, 1, 2, … in sequence,
they can all be omitted (not just some) and the numbers 0, 1, 2, … will be
automatically inserted in that order.</p>
<p>Named arguments can be referred to by their names or indices.</p>
<p>Some simple format string examples:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="s">&quot;First, thou shalt count to {0}&quot;</span> <span class="c1">// References the first argument</span>
<span class="s">&quot;Bring me a {}&quot;</span> <span class="c1">// Implicitly references the first argument</span>
<span class="s">&quot;From {} to {}&quot;</span> <span class="c1">// Same as &quot;From {0} to {1}&quot;</span>
</pre></div>
</div>
<p>The <em>format_spec</em> field contains a specification of how the value should be
presented, including such details as field width, alignment, padding, decimal
precision and so on. Each value type can define its own “formatting
mini-language” or interpretation of the <em>format_spec</em>.</p>
<p>Most built-in types support a common formatting mini-language, which is
described in the next section.</p>
<p>A <em>format_spec</em> field can also include nested replacement fields in certain
positions within it. These nested replacement fields can contain only an
argument id; format specifications are not allowed. This allows the formatting
of a value to be dynamically specified.</p>
<p>See the <a class="reference internal" href="#formatexamples"><span class="std std-ref">Format Examples</span></a> section for some examples.</p>
<section id="format-specification-mini-language">
<span id="formatspec"></span><h2>Format Specification Mini-Language<a class="headerlink" href="#format-specification-mini-language" title="Permalink to this headline"></a></h2>
<p>“Format specifications” are used within replacement fields contained within a
format string to define how individual values are presented (see
<a class="reference internal" href="#syntax"><span class="std std-ref">Format String Syntax</span></a>). Each formattable type may define how the format
specification is to be interpreted.</p>
<p>Most built-in types implement the following options for format specifications,
although some of the formatting options are only supported by the numeric types.</p>
<p>The general form of a <em>standard format specifier</em> is:</p>
<pre>
<strong id="grammar-token-sf-format_spec"><span id="grammar-token-format-spec"></span>format_spec</strong> ::= [[<a class="reference internal" href="#grammar-token-sf-fill"><code class="xref docutils literal notranslate"><span class="pre">fill</span></code></a>]<a class="reference internal" href="#grammar-token-sf-align"><code class="xref docutils literal notranslate"><span class="pre">align</span></code></a>][<a class="reference internal" href="#grammar-token-sf-sign"><code class="xref docutils literal notranslate"><span class="pre">sign</span></code></a>][&quot;#&quot;][&quot;0&quot;][<a class="reference internal" href="#grammar-token-sf-width"><code class="xref docutils literal notranslate"><span class="pre">width</span></code></a>][&quot;.&quot; <a class="reference internal" href="#grammar-token-sf-precision"><code class="xref docutils literal notranslate"><span class="pre">precision</span></code></a>][&quot;L&quot;][<a class="reference internal" href="#grammar-token-sf-type"><code class="xref docutils literal notranslate"><span class="pre">type</span></code></a>]
<strong id="grammar-token-sf-fill"><span id="grammar-token-fill"></span>fill </strong> ::= &lt;a character other than '{' or '}'&gt;
<strong id="grammar-token-sf-align"><span id="grammar-token-align"></span>align </strong> ::= &quot;&lt;&quot; | &quot;&gt;&quot; | &quot;^&quot;
<strong id="grammar-token-sf-sign"><span id="grammar-token-sign"></span>sign </strong> ::= &quot;+&quot; | &quot;-&quot; | &quot; &quot;
<strong id="grammar-token-sf-width"><span id="grammar-token-width"></span>width </strong> ::= <a class="reference internal" href="#grammar-token-sf-integer"><code class="xref docutils literal notranslate"><span class="pre">integer</span></code></a> | &quot;{&quot; [<a class="reference internal" href="#grammar-token-sf-arg_id"><code class="xref docutils literal notranslate"><span class="pre">arg_id</span></code></a>] &quot;}&quot;
<strong id="grammar-token-sf-precision"><span id="grammar-token-precision"></span>precision </strong> ::= <a class="reference internal" href="#grammar-token-sf-integer"><code class="xref docutils literal notranslate"><span class="pre">integer</span></code></a> | &quot;{&quot; [<a class="reference internal" href="#grammar-token-sf-arg_id"><code class="xref docutils literal notranslate"><span class="pre">arg_id</span></code></a>] &quot;}&quot;
<strong id="grammar-token-sf-type"><span id="grammar-token-type"></span>type </strong> ::= &quot;a&quot; | &quot;A&quot; | &quot;b&quot; | &quot;B&quot; | &quot;c&quot; | &quot;d&quot; | &quot;e&quot; | &quot;E&quot; | &quot;f&quot; | &quot;F&quot; | &quot;g&quot; | &quot;G&quot; |
&quot;o&quot; | &quot;p&quot; | &quot;s&quot; | &quot;x&quot; | &quot;X&quot;
</pre>
<p>The <em>fill</em> character can be any Unicode code point other than <code class="docutils literal notranslate"><span class="pre">'{'</span></code> or
<code class="docutils literal notranslate"><span class="pre">'}'</span></code>. The presence of a fill character is signaled by the character following
it, which must be one of the alignment options. If the second character of
<em>format_spec</em> is not a valid alignment option, then it is assumed that both the
fill character and the alignment option are absent.</p>
<p>The meaning of the various alignment options is as follows:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 13%" />
<col style="width: 87%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Option</p></th>
<th class="head"><p>Meaning</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'&lt;'</span></code></p></td>
<td><p>Forces the field to be left-aligned within the available
space (this is the default for most objects).</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'&gt;'</span></code></p></td>
<td><p>Forces the field to be right-aligned within the
available space (this is the default for numbers).</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'^'</span></code></p></td>
<td><p>Forces the field to be centered within the available
space.</p></td>
</tr>
</tbody>
</table>
<p>Note that unless a minimum field width is defined, the field width will always
be the same size as the data to fill it, so that the alignment option has no
meaning in this case.</p>
<p>The <em>sign</em> option is only valid for number types, and can be one of the
following:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 13%" />
<col style="width: 87%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Option</p></th>
<th class="head"><p>Meaning</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'+'</span></code></p></td>
<td><p>indicates that a sign should be used for both
positive as well as negative numbers.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'-'</span></code></p></td>
<td><p>indicates that a sign should be used only for negative
numbers (this is the default behavior).</p></td>
</tr>
<tr class="row-even"><td><p>space</p></td>
<td><p>indicates that a leading space should be used on
positive numbers, and a minus sign on negative numbers.</p></td>
</tr>
</tbody>
</table>
<p>The <code class="docutils literal notranslate"><span class="pre">'#'</span></code> option causes the “alternate form” to be used for the
conversion. The alternate form is defined differently for different
types. This option is only valid for integer and floating-point types.
For integers, when binary, octal, or hexadecimal output is used, this
option adds the prefix respective <code class="docutils literal notranslate"><span class="pre">&quot;0b&quot;</span></code> (<code class="docutils literal notranslate"><span class="pre">&quot;0B&quot;</span></code>), <code class="docutils literal notranslate"><span class="pre">&quot;0&quot;</span></code>, or
<code class="docutils literal notranslate"><span class="pre">&quot;0x&quot;</span></code> (<code class="docutils literal notranslate"><span class="pre">&quot;0X&quot;</span></code>) to the output value. Whether the prefix is
lower-case or upper-case is determined by the case of the type
specifier, for example, the prefix <code class="docutils literal notranslate"><span class="pre">&quot;0x&quot;</span></code> is used for the type <code class="docutils literal notranslate"><span class="pre">'x'</span></code>
and <code class="docutils literal notranslate"><span class="pre">&quot;0X&quot;</span></code> is used for <code class="docutils literal notranslate"><span class="pre">'X'</span></code>. For floating-point numbers the
alternate form causes the result of the conversion to always contain a
decimal-point character, even if no digits follow it. Normally, a
decimal-point character appears in the result of these conversions
only if a digit follows it. In addition, for <code class="docutils literal notranslate"><span class="pre">'g'</span></code> and <code class="docutils literal notranslate"><span class="pre">'G'</span></code>
conversions, trailing zeros are not removed from the result.</p>
<p><em>width</em> is a decimal integer defining the minimum field width. If not
specified, then the field width will be determined by the content.</p>
<p>Preceding the <em>width</em> field by a zero (<code class="docutils literal notranslate"><span class="pre">'0'</span></code>) character enables sign-aware
zero-padding for numeric types. It forces the padding to be placed after the
sign or base (if any) but before the digits. This is used for printing fields in
the form +000000120. This option is only valid for numeric types and it has no
effect on formatting of infinity and NaN.</p>
<p>The <em>precision</em> is a decimal number indicating how many digits should be
displayed after the decimal point for a floating-point value formatted with
<code class="docutils literal notranslate"><span class="pre">'f'</span></code> and <code class="docutils literal notranslate"><span class="pre">'F'</span></code>, or before and after the decimal point for a floating-point
value formatted with <code class="docutils literal notranslate"><span class="pre">'g'</span></code> or <code class="docutils literal notranslate"><span class="pre">'G'</span></code>. For non-number types the field
indicates the maximum field size - in other words, how many characters will be
used from the field content. The <em>precision</em> is not allowed for integer,
character, Boolean, and pointer values.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">'L'</span></code> option uses the current locale setting to insert the appropriate
number separator characters. This option is only valid for numeric types.</p>
<p>Finally, the <em>type</em> determines how the data should be presented.</p>
<p>The available string presentation types are:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 13%" />
<col style="width: 87%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Type</p></th>
<th class="head"><p>Meaning</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'s'</span></code></p></td>
<td><p>String format. This is the default type for strings and
may be omitted.</p></td>
</tr>
<tr class="row-odd"><td><p>none</p></td>
<td><p>The same as <code class="docutils literal notranslate"><span class="pre">'s'</span></code>.</p></td>
</tr>
</tbody>
</table>
<p>The available character presentation types are:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 13%" />
<col style="width: 87%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Type</p></th>
<th class="head"><p>Meaning</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'c'</span></code></p></td>
<td><p>Character format. This is the default type for
characters and may be omitted.</p></td>
</tr>
<tr class="row-odd"><td><p>none</p></td>
<td><p>The same as <code class="docutils literal notranslate"><span class="pre">'c'</span></code>.</p></td>
</tr>
</tbody>
</table>
<p>The available integer presentation types are:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 13%" />
<col style="width: 87%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Type</p></th>
<th class="head"><p>Meaning</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'b'</span></code></p></td>
<td><p>Binary format. Outputs the number in base 2. Using the
<code class="docutils literal notranslate"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal notranslate"><span class="pre">&quot;0b&quot;</span></code>
to the output value.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'B'</span></code></p></td>
<td><p>Binary format. Outputs the number in base 2. Using the
<code class="docutils literal notranslate"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal notranslate"><span class="pre">&quot;0B&quot;</span></code>
to the output value.</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'c'</span></code></p></td>
<td><p>Character format. Outputs the number as a character.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'d'</span></code></p></td>
<td><p>Decimal integer. Outputs the number in base 10.</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'o'</span></code></p></td>
<td><p>Octal format. Outputs the number in base 8.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'x'</span></code></p></td>
<td><p>Hex format. Outputs the number in base 16, using
lower-case letters for the digits above 9. Using the
<code class="docutils literal notranslate"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal notranslate"><span class="pre">&quot;0x&quot;</span></code>
to the output value.</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'X'</span></code></p></td>
<td><p>Hex format. Outputs the number in base 16, using
upper-case letters for the digits above 9. Using the
<code class="docutils literal notranslate"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal notranslate"><span class="pre">&quot;0X&quot;</span></code>
to the output value.</p></td>
</tr>
<tr class="row-odd"><td><p>none</p></td>
<td><p>The same as <code class="docutils literal notranslate"><span class="pre">'d'</span></code>.</p></td>
</tr>
</tbody>
</table>
<p>Integer presentation types can also be used with character and Boolean values.
Boolean values are formatted using textual representation, either <code class="docutils literal notranslate"><span class="pre">true</span></code> or
<code class="docutils literal notranslate"><span class="pre">false</span></code>, if the presentation type is not specified.</p>
<p>The available presentation types for floating-point values are:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 13%" />
<col style="width: 87%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Type</p></th>
<th class="head"><p>Meaning</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'a'</span></code></p></td>
<td><p>Hexadecimal floating point format. Prints the number in
base 16 with prefix <code class="docutils literal notranslate"><span class="pre">&quot;0x&quot;</span></code> and lower-case letters for
digits above 9. Uses <code class="docutils literal notranslate"><span class="pre">'p'</span></code> to indicate the exponent.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'A'</span></code></p></td>
<td><p>Same as <code class="docutils literal notranslate"><span class="pre">'a'</span></code> except it uses upper-case letters for
the prefix, digits above 9 and to indicate the exponent.</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'e'</span></code></p></td>
<td><p>Exponent notation. Prints the number in scientific
notation using the letter e to indicate the exponent.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'E'</span></code></p></td>
<td><p>Exponent notation. Same as <code class="docutils literal notranslate"><span class="pre">'e'</span></code> except it uses an
upper-case <code class="docutils literal notranslate"><span class="pre">'E'</span></code> as the separator character.</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'f'</span></code></p></td>
<td><p>Fixed point. Displays the number as a fixed-point
number.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'F'</span></code></p></td>
<td><p>Fixed point. Same as <code class="docutils literal notranslate"><span class="pre">'f'</span></code>, but converts <code class="docutils literal notranslate"><span class="pre">nan</span></code> to
<code class="docutils literal notranslate"><span class="pre">NAN</span></code> and <code class="docutils literal notranslate"><span class="pre">inf</span></code> to <code class="docutils literal notranslate"><span class="pre">INF</span></code>.</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'g'</span></code></p></td>
<td><p>General format. For a given precision <code class="docutils literal notranslate"><span class="pre">p</span> <span class="pre">&gt;=</span> <span class="pre">1</span></code>,
this rounds the number to <code class="docutils literal notranslate"><span class="pre">p</span></code> significant digits and
then formats the result in either fixed-point format
or in scientific notation, depending on its magnitude.</p>
<p>A precision of <code class="docutils literal notranslate"><span class="pre">0</span></code> is treated as equivalent to a
precision of <code class="docutils literal notranslate"><span class="pre">1</span></code>.</p>
</td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'G'</span></code></p></td>
<td><p>General format. Same as <code class="docutils literal notranslate"><span class="pre">'g'</span></code> except switches to
<code class="docutils literal notranslate"><span class="pre">'E'</span></code> if the number gets too large. The
representations of infinity and NaN are uppercased, too.</p></td>
</tr>
<tr class="row-even"><td><p>none</p></td>
<td><p>Similar to <code class="docutils literal notranslate"><span class="pre">'g'</span></code>, except that the default precision is
as high as needed to represent the particular value.</p></td>
</tr>
</tbody>
</table>
<p>The available presentation types for pointers are:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 13%" />
<col style="width: 87%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Type</p></th>
<th class="head"><p>Meaning</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'p'</span></code></p></td>
<td><p>Pointer format. This is the default type for
pointers and may be omitted.</p></td>
</tr>
<tr class="row-odd"><td><p>none</p></td>
<td><p>The same as <code class="docutils literal notranslate"><span class="pre">'p'</span></code>.</p></td>
</tr>
</tbody>
</table>
</section>
<section id="chrono-format-specifications">
<span id="chrono-specs"></span><h2>Chrono Format Specifications<a class="headerlink" href="#chrono-format-specifications" title="Permalink to this headline"></a></h2>
<p>Format specifications for chrono types have the following syntax:</p>
<pre>
<strong id="grammar-token-sf-chrono_format_spec"><span id="grammar-token-chrono-format-spec"></span>chrono_format_spec</strong> ::= [[<a class="reference internal" href="#grammar-token-sf-fill"><code class="xref docutils literal notranslate"><span class="pre">fill</span></code></a>]<a class="reference internal" href="#grammar-token-sf-align"><code class="xref docutils literal notranslate"><span class="pre">align</span></code></a>][<a class="reference internal" href="#grammar-token-sf-width"><code class="xref docutils literal notranslate"><span class="pre">width</span></code></a>][&quot;.&quot; <a class="reference internal" href="#grammar-token-sf-precision"><code class="xref docutils literal notranslate"><span class="pre">precision</span></code></a>][<a class="reference internal" href="#grammar-token-sf-chrono_specs"><code class="xref docutils literal notranslate"><span class="pre">chrono_specs</span></code></a>]
<strong id="grammar-token-sf-chrono_specs"><span id="grammar-token-chrono-specs"></span>chrono_specs </strong> ::= [<a class="reference internal" href="#grammar-token-sf-chrono_specs"><code class="xref docutils literal notranslate"><span class="pre">chrono_specs</span></code></a>] <a class="reference internal" href="#grammar-token-sf-conversion_spec"><code class="xref docutils literal notranslate"><span class="pre">conversion_spec</span></code></a> | <a class="reference internal" href="#grammar-token-sf-chrono_specs"><code class="xref docutils literal notranslate"><span class="pre">chrono_specs</span></code></a> <a class="reference internal" href="#grammar-token-sf-literal_char"><code class="xref docutils literal notranslate"><span class="pre">literal_char</span></code></a>
<strong id="grammar-token-sf-conversion_spec"><span id="grammar-token-conversion-spec"></span>conversion_spec </strong> ::= &quot;%&quot; [<a class="reference internal" href="#grammar-token-sf-modifier"><code class="xref docutils literal notranslate"><span class="pre">modifier</span></code></a>] <a class="reference internal" href="#grammar-token-sf-chrono_type"><code class="xref docutils literal notranslate"><span class="pre">chrono_type</span></code></a>
<strong id="grammar-token-sf-literal_char"><span id="grammar-token-literal-char"></span>literal_char </strong> ::= &lt;a character other than '{', '}' or '%'&gt;
<strong id="grammar-token-sf-modifier"><span id="grammar-token-modifier"></span>modifier </strong> ::= &quot;E&quot; | &quot;O&quot;
<strong id="grammar-token-sf-chrono_type"><span id="grammar-token-chrono-type"></span>chrono_type </strong> ::= &quot;a&quot; | &quot;A&quot; | &quot;b&quot; | &quot;B&quot; | &quot;c&quot; | &quot;C&quot; | &quot;d&quot; | &quot;D&quot; | &quot;e&quot; | &quot;F&quot; |
&quot;g&quot; | &quot;G&quot; | &quot;h&quot; | &quot;H&quot; | &quot;I&quot; | &quot;j&quot; | &quot;m&quot; | &quot;M&quot; | &quot;n&quot; | &quot;p&quot; |
&quot;q&quot; | &quot;Q&quot; | &quot;r&quot; | &quot;R&quot; | &quot;S&quot; | &quot;t&quot; | &quot;T&quot; | &quot;u&quot; | &quot;U&quot; | &quot;V&quot; |
&quot;w&quot; | &quot;W&quot; | &quot;x&quot; | &quot;X&quot; | &quot;y&quot; | &quot;Y&quot; | &quot;z&quot; | &quot;Z&quot; | &quot;%&quot;
</pre>
<p>Literal chars are copied unchanged to the output. Precision is valid only for
<code class="docutils literal notranslate"><span class="pre">std::chrono::duration</span></code> types with a floating-point representation type.</p>
<p>The available presentation types (<em>chrono_type</em>) for chrono durations and time
points are:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 12%" />
<col style="width: 88%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Type</p></th>
<th class="head"><p>Meaning</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'H'</span></code></p></td>
<td><p>The hour (24-hour clock) as a decimal number. If the result is a
single digit, it is prefixed with 0. The modified command <code class="docutils literal notranslate"><span class="pre">%OH</span></code>
produces the locales alternative representation.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">'M'</span></code></p></td>
<td><p>The minute as a decimal number. If the result is a single digit,
it is prefixed with 0. The modified command <code class="docutils literal notranslate"><span class="pre">%OM</span></code> produces the
locales alternative representation.</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">'S'</span></code></p></td>
<td><p>Seconds as a decimal number. If the number of seconds is less than
10, the result is prefixed with 0. If the precision of the input
cannot be exactly represented with seconds, then the format is a
decimal floating-point number with a fixed format and a precision
matching that of the precision of the input (or to a microseconds
precision if the conversion to floating-point decimal seconds
cannot be made within 18 fractional digits). The character for the
decimal point is localized according to the locale. The modified
command <code class="docutils literal notranslate"><span class="pre">%OS</span></code> produces the locales alternative representation.</p></td>
</tr>
</tbody>
</table>
<p>Specifiers that have a calendaric component such as <code class="xref cpp cpp-any docutils literal notranslate"><span class="pre">'d'</span></code> (the day of month)
are valid only for <code class="docutils literal notranslate"><span class="pre">std::tm</span></code> and not durations or time points.</p>
<p><code class="docutils literal notranslate"><span class="pre">std::tm</span></code> uses the systems <a class="reference external" href="https://en.cppreference.com/w/cpp/chrono/c/strftime">strftime</a> so refer to its
documentation for details on supported conversion specifiers.</p>
</section>
<section id="format-examples">
<span id="formatexamples"></span><h2>Format Examples<a class="headerlink" href="#format-examples" title="Permalink to this headline"></a></h2>
<p>This section contains examples of the format syntax and comparison with
the printf formatting.</p>
<p>In most of the cases the syntax is similar to the printf formatting, with the
addition of the <code class="docutils literal notranslate"><span class="pre">{}</span></code> and with <code class="docutils literal notranslate"><span class="pre">:</span></code> used instead of <code class="docutils literal notranslate"><span class="pre">%</span></code>.
For example, <code class="docutils literal notranslate"><span class="pre">&quot;%03.2f&quot;</span></code> can be translated to <code class="docutils literal notranslate"><span class="pre">&quot;{:03.2f}&quot;</span></code>.</p>
<p>The new format syntax also supports new and different options, shown in the
following examples.</p>
<p>Accessing arguments by position:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{0}, {1}, {2}&quot;</span><span class="p">,</span> <span class="sc">&#39;a&#39;</span><span class="p">,</span> <span class="sc">&#39;b&#39;</span><span class="p">,</span> <span class="sc">&#39;c&#39;</span><span class="p">);</span>
<span class="c1">// Result: &quot;a, b, c&quot;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{}, {}, {}&quot;</span><span class="p">,</span> <span class="sc">&#39;a&#39;</span><span class="p">,</span> <span class="sc">&#39;b&#39;</span><span class="p">,</span> <span class="sc">&#39;c&#39;</span><span class="p">);</span>
<span class="c1">// Result: &quot;a, b, c&quot;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{2}, {1}, {0}&quot;</span><span class="p">,</span> <span class="sc">&#39;a&#39;</span><span class="p">,</span> <span class="sc">&#39;b&#39;</span><span class="p">,</span> <span class="sc">&#39;c&#39;</span><span class="p">);</span>
<span class="c1">// Result: &quot;c, b, a&quot;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{0}{1}{0}&quot;</span><span class="p">,</span> <span class="s">&quot;abra&quot;</span><span class="p">,</span> <span class="s">&quot;cad&quot;</span><span class="p">);</span> <span class="c1">// arguments&#39; indices can be repeated</span>
<span class="c1">// Result: &quot;abracadabra&quot;</span>
</pre></div>
</div>
<p>Aligning the text and specifying a width:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:&lt;30}&quot;</span><span class="p">,</span> <span class="s">&quot;left aligned&quot;</span><span class="p">);</span>
<span class="c1">// Result: &quot;left aligned &quot;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:&gt;30}&quot;</span><span class="p">,</span> <span class="s">&quot;right aligned&quot;</span><span class="p">);</span>
<span class="c1">// Result: &quot; right aligned&quot;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:^30}&quot;</span><span class="p">,</span> <span class="s">&quot;centered&quot;</span><span class="p">);</span>
<span class="c1">// Result: &quot; centered &quot;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:*^30}&quot;</span><span class="p">,</span> <span class="s">&quot;centered&quot;</span><span class="p">);</span> <span class="c1">// use &#39;*&#39; as a fill char</span>
<span class="c1">// Result: &quot;***********centered***********&quot;</span>
</pre></div>
</div>
<p>Dynamic width:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:&lt;{}}&quot;</span><span class="p">,</span> <span class="s">&quot;left aligned&quot;</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
<span class="c1">// Result: &quot;left aligned &quot;</span>
</pre></div>
</div>
<p>Dynamic precision:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:.{}f}&quot;</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="c1">// Result: &quot;3.1&quot;</span>
</pre></div>
</div>
<p>Replacing <code class="docutils literal notranslate"><span class="pre">%+f</span></code>, <code class="docutils literal notranslate"><span class="pre">%-f</span></code>, and <code class="docutils literal notranslate"><span class="pre">%</span> <span class="pre">f</span></code> and specifying a sign:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:+f}; {:+f}&quot;</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="mf">-3.14</span><span class="p">);</span> <span class="c1">// show it always</span>
<span class="c1">// Result: &quot;+3.140000; -3.140000&quot;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{: f}; {: f}&quot;</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="mf">-3.14</span><span class="p">);</span> <span class="c1">// show a space for positive numbers</span>
<span class="c1">// Result: &quot; 3.140000; -3.140000&quot;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:-f}; {:-f}&quot;</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="mf">-3.14</span><span class="p">);</span> <span class="c1">// show only the minus -- same as &#39;{:f}; {:f}&#39;</span>
<span class="c1">// Result: &quot;3.140000; -3.140000&quot;</span>
</pre></div>
</div>
<p>Replacing <code class="docutils literal notranslate"><span class="pre">%x</span></code> and <code class="docutils literal notranslate"><span class="pre">%o</span></code> and converting the value to different bases:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}&quot;</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
<span class="c1">// Result: &quot;int: 42; hex: 2a; oct: 52; bin: 101010&quot;</span>
<span class="c1">// with 0x or 0 or 0b as prefix:</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}&quot;</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
<span class="c1">// Result: &quot;int: 42; hex: 0x2a; oct: 052; bin: 0b101010&quot;</span>
</pre></div>
</div>
<p>Padded hex byte with prefix and always prints both hex characters:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">&quot;{:#04x}&quot;</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="c1">// Result: &quot;0x00&quot;</span>
</pre></div>
</div>
<p>Box drawing using Unicode fill:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span>
<span class="s">&quot;┌{0:─^{2}}┐</span><span class="se">\n</span><span class="s">&quot;</span>
<span class="s">&quot;│{1: ^{2}}│</span><span class="se">\n</span><span class="s">&quot;</span>
<span class="s">&quot;└{0:─^{2}}┘</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="s">&quot;&quot;</span><span class="p">,</span> <span class="s">&quot;Hello, world!&quot;</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span>
</pre></div>
</div>
<p>prints:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span>┌────────────────────┐
│ Hello, world! │
└────────────────────┘
</pre></div>
</div>
<p>Using type-specific formatting:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf">&lt;fmt/chrono.h&gt;</span><span class="cp"></span>
<span class="k">auto</span> <span class="n">t</span> <span class="o">=</span> <span class="n">tm</span><span class="p">();</span>
<span class="n">t</span><span class="p">.</span><span class="n">tm_year</span> <span class="o">=</span> <span class="mi">2010</span> <span class="o">-</span> <span class="mi">1900</span><span class="p">;</span>
<span class="n">t</span><span class="p">.</span><span class="n">tm_mon</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
<span class="n">t</span><span class="p">.</span><span class="n">tm_mday</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
<span class="n">t</span><span class="p">.</span><span class="n">tm_hour</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span>
<span class="n">t</span><span class="p">.</span><span class="n">tm_min</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
<span class="n">t</span><span class="p">.</span><span class="n">tm_sec</span> <span class="o">=</span> <span class="mi">58</span><span class="p">;</span>
<span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">&quot;{:%Y-%m-%d %H:%M:%S}&quot;</span><span class="p">,</span> <span class="n">t</span><span class="p">);</span>
<span class="c1">// Prints: 2010-08-04 12:15:58</span>
</pre></div>
</div>
<p>Using the comma as a thousands separator:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf">&lt;fmt/locale.h&gt;</span><span class="cp"></span>
<span class="k">auto</span> <span class="n">s</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">locale</span><span class="p">(</span><span class="s">&quot;en_US.UTF-8&quot;</span><span class="p">),</span> <span class="s">&quot;{:L}&quot;</span><span class="p">,</span> <span class="mi">1234567890</span><span class="p">);</span>
<span class="c1">// s == &quot;1,234,567,890&quot;</span>
</pre></div>
</div>
</section>
</section>
</div>
</div>
</div>
<div class="footer" role="contentinfo">
&copy; Copyright 2012-present, Victor Zverovich.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.3.0.
</div>
<script src="_static/bootstrap.min.js"></script>
</body>
</html>

View File

@ -1,333 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Usage &mdash; fmt 8.0.1 documentation</title>
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/breathe.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '8.0.1',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt'
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<script type="text/javascript" src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<meta name="description" content="Small, safe and fast formatting library">
<meta name="keywords" content="C++, formatting, printf, string, library">
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
</script>
</head>
<body role="document">
<nav class="navbar navbar-inverse">
<div class="tb-container">
<div class="row">
<div class="navbar-content">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">{fmt}</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false">8.0.1
<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="https://fmt.dev/8.0.1">8.0.1</a></li>
<li><a href="https://fmt.dev/8.0.0">8.0.0</a></li>
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
</ul>
</li>
<li><a href="contents.html">Contents</a></li>
<li class="active"><a href="usage.html">Usage
<span class="sr-only">(current)</span></a></li>
<li><a href="api.html">API</a></li>
<li><a href="syntax.html">Syntax</a></li>
</ul>
<form class="navbar-form navbar-right" role="search" action="search.html"
method="get">
<div class="form-group">
<input type="text" name="q" class="form-control"
placeholder="Search" >
</div>
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
</div>
</div>
</nav>
<div class="tb-container">
<div class="row">
<div class="content">
<section id="usage">
<h1>Usage<a class="headerlink" href="#usage" title="Permalink to this headline"></a></h1>
<p>To use the {fmt} library, add <code class="file docutils literal notranslate"><span class="pre">fmt/core.h</span></code>, <code class="file docutils literal notranslate"><span class="pre">fmt/format.h</span></code>,
<code class="file docutils literal notranslate"><span class="pre">fmt/format-inl.h</span></code>, <code class="file docutils literal notranslate"><span class="pre">src/format.cc</span></code> and optionally other headers
from a <a class="reference external" href="https://github.com/fmtlib/fmt/releases/latest">release archive</a> or
the <a class="reference external" href="https://github.com/fmtlib/fmt">Git repository</a> to your project.
Alternatively, you can <a class="reference internal" href="#building"><span class="std std-ref">build the library with CMake</span></a>.</p>
<section id="building-the-library">
<span id="building"></span><h2>Building the Library<a class="headerlink" href="#building-the-library" title="Permalink to this headline"></a></h2>
<p>The included <a class="reference external" href="https://github.com/fmtlib/fmt/blob/master/CMakeLists.txt">CMake build script</a> can be used to build the fmt
library on a wide range of platforms. CMake is freely available for
download from <a class="reference external" href="https://www.cmake.org/download/">https://www.cmake.org/download/</a>.</p>
<p>CMake works by generating native makefiles or project files that can
be used in the compiler environment of your choice. The typical
workflow starts with:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span>mkdir build # Create a directory to hold the build output.
cd build
cmake .. # Generate native build scripts.
</pre></div>
</div>
<p>where <code class="file docutils literal notranslate"><em><span class="pre">&lt;path/to/fmt&gt;</span></em></code> is a path to the <code class="docutils literal notranslate"><span class="pre">fmt</span></code> repository.</p>
<p>If you are on a *nix system, you should now see a Makefile in the
current directory. Now you can build the library by running <strong class="command">make</strong>.</p>
<p>Once the library has been built you can invoke <strong class="command">make test</strong> to run
the tests.</p>
<p>You can control generation of the make <code class="docutils literal notranslate"><span class="pre">test</span></code> target with the <code class="docutils literal notranslate"><span class="pre">FMT_TEST</span></code>
CMake option. This can be useful if you include fmt as a subdirectory in
your project but dont want to add fmts tests to your <code class="docutils literal notranslate"><span class="pre">test</span></code> target.</p>
<p>If you use Windows and have Visual Studio installed, a <code class="file docutils literal notranslate"><span class="pre">FMT.sln</span></code>
file and several <code class="file docutils literal notranslate"><span class="pre">.vcproj</span></code> files will be created. You can then build them
using Visual Studio or msbuild.</p>
<p>On Mac OS X with Xcode installed, an <code class="file docutils literal notranslate"><span class="pre">.xcodeproj</span></code> file will be generated.</p>
<p>To build a <a class="reference external" href="https://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries">shared library</a> set the <code class="docutils literal notranslate"><span class="pre">BUILD_SHARED_LIBS</span></code> CMake variable to
<code class="docutils literal notranslate"><span class="pre">TRUE</span></code>:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">cmake</span> <span class="o">-</span><span class="n">DBUILD_SHARED_LIBS</span><span class="o">=</span><span class="n">TRUE</span> <span class="p">...</span>
</pre></div>
</div>
<p>To build a <code class="xref cpp cpp-any docutils literal notranslate"><span class="pre">static</span> <span class="pre">library</span></code> with position independent code (required if the main
consumer of the fmt library is a shared library i.e. a Python extension) set the
<code class="docutils literal notranslate"><span class="pre">CMAKE_POSITION_INDEPENDENT_CODE</span></code> CMake variable to <code class="docutils literal notranslate"><span class="pre">TRUE</span></code>:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">cmake</span> <span class="o">-</span><span class="n">DCMAKE_POSITION_INDEPENDENT_CODE</span><span class="o">=</span><span class="n">TRUE</span> <span class="p">...</span>
</pre></div>
</div>
</section>
<section id="installing-the-library">
<h2>Installing the Library<a class="headerlink" href="#installing-the-library" title="Permalink to this headline"></a></h2>
<p>After building the library you can install it on a Unix-like system by running
<strong class="command">sudo make install</strong>.</p>
</section>
<section id="usage-with-cmake">
<h2>Usage with CMake<a class="headerlink" href="#usage-with-cmake" title="Permalink to this headline"></a></h2>
<p>You can add the <code class="docutils literal notranslate"><span class="pre">fmt</span></code> library directory into your project and include it in
your <code class="docutils literal notranslate"><span class="pre">CMakeLists.txt</span></code> file:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">add_subdirectory</span><span class="p">(</span><span class="n">fmt</span><span class="p">)</span>
</pre></div>
</div>
<p>or</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">add_subdirectory</span><span class="p">(</span><span class="n">fmt</span> <span class="n">EXCLUDE_FROM_ALL</span><span class="p">)</span>
</pre></div>
</div>
<p>to exclude it from <code class="docutils literal notranslate"><span class="pre">make</span></code>, <code class="docutils literal notranslate"><span class="pre">make</span> <span class="pre">all</span></code>, or <code class="docutils literal notranslate"><span class="pre">cmake</span> <span class="pre">--build</span> <span class="pre">.</span></code>.</p>
<p>You can detect and use an installed version of {fmt} as follows:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">find_package</span><span class="p">(</span><span class="n">fmt</span><span class="p">)</span>
<span class="n">target_link_libraries</span><span class="p">(</span><span class="o">&lt;</span><span class="n">your</span><span class="o">-</span><span class="n">target</span><span class="o">&gt;</span> <span class="n">fmt</span><span class="o">::</span><span class="n">fmt</span><span class="p">)</span>
</pre></div>
</div>
<p>Setting up your target to use a header-only version of <code class="docutils literal notranslate"><span class="pre">fmt</span></code> is equally easy:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">target_link_libraries</span><span class="p">(</span><span class="o">&lt;</span><span class="n">your</span><span class="o">-</span><span class="n">target</span><span class="o">&gt;</span> <span class="n">PRIVATE</span> <span class="n">fmt</span><span class="o">::</span><span class="n">fmt</span><span class="o">-</span><span class="n">header</span><span class="o">-</span><span class="n">only</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="usage-with-build2">
<h2>Usage with build2<a class="headerlink" href="#usage-with-build2" title="Permalink to this headline"></a></h2>
<p>You can use <a class="reference external" href="https://build2.org">build2</a>, a dependency manager and a
build-system combined, to use <code class="docutils literal notranslate"><span class="pre">fmt</span></code>.</p>
<p>Currently this package is available in these package repositories:</p>
<ul class="simple">
<li><p><strong>https://cppget.org/fmt/</strong> for released and published versions.</p></li>
<li><p><a class="reference external" href="https://github.com/build2-packaging/fmt.git">The git repository with the sources of the build2 package of fmt</a>
for unreleased or custom revisions of <code class="docutils literal notranslate"><span class="pre">fmt</span></code>.</p></li>
</ul>
<p><strong>Usage:</strong></p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">build2</span></code> package name: <code class="docutils literal notranslate"><span class="pre">fmt</span></code></p></li>
<li><p>Library target name : <code class="docutils literal notranslate"><span class="pre">lib{fmt}</span></code></p></li>
</ul>
<p>For example, to make your <code class="docutils literal notranslate"><span class="pre">build2</span></code> project depend on <code class="docutils literal notranslate"><span class="pre">fmt</span></code>:</p>
<ul>
<li><p>Add one of the repositories to your configurations, or in your
<code class="docutils literal notranslate"><span class="pre">repositories.manifest</span></code>, if not already there:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="o">:</span>
<span class="nl">role</span><span class="p">:</span> <span class="n">prerequisite</span>
<span class="nl">location</span><span class="p">:</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//pkg.cppget.org/1/stable</span>
</pre></div>
</div>
</li>
<li><p>Add this package as a dependency to your <code class="docutils literal notranslate"><span class="pre">./manifest</span></code> file
(example for <code class="docutils literal notranslate"><span class="pre">v7.0.x</span></code>):</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="nl">depends</span><span class="p">:</span> <span class="n">fmt</span> <span class="o">~</span><span class="mf">7.0.0</span>
</pre></div>
</div>
</li>
<li><p>Import the target and use it as a prerequisite to your own target
using <code class="xref cpp cpp-any docutils literal notranslate"><span class="pre">fmt</span></code> in the appropriate <code class="docutils literal notranslate"><span class="pre">buildfile</span></code>:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="k">import</span> <span class="n">fmt</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">%</span><span class="n">lib</span><span class="p">{</span><span class="n">fmt</span><span class="p">}</span>
<span class="n">lib</span><span class="p">{</span><span class="n">mylib</span><span class="p">}</span> <span class="o">:</span> <span class="n">cxx</span><span class="p">{</span><span class="o">**</span><span class="p">}</span> <span class="p">...</span> <span class="n">$fmt</span>
</pre></div>
</div>
</li>
</ul>
<p>Then build your project as usual with <code class="xref cpp cpp-any docutils literal notranslate"><span class="pre">b</span></code> or <code class="xref cpp cpp-any docutils literal notranslate"><span class="pre">bdep</span> <span class="pre">update</span></code>.</p>
<p>For <code class="docutils literal notranslate"><span class="pre">build2</span></code> newcomers or to get more details and use cases, you can read the
<code class="docutils literal notranslate"><span class="pre">build2</span></code>
<a class="reference external" href="https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml">toolchain introduction</a>.</p>
</section>
<section id="building-the-documentation">
<h2>Building the Documentation<a class="headerlink" href="#building-the-documentation" title="Permalink to this headline"></a></h2>
<p>To build the documentation you need the following software installed on your
system:</p>
<ul>
<li><p><a class="reference external" href="https://www.python.org/">Python</a> with pip and virtualenv</p></li>
<li><p><a class="reference external" href="http://www.stack.nl/~dimitri/doxygen/">Doxygen</a></p></li>
<li><p><a class="reference external" href="http://lesscss.org/">Less</a> with <code class="docutils literal notranslate"><span class="pre">less-plugin-clean-css</span></code>.
Ubuntu doesnt package the <code class="docutils literal notranslate"><span class="pre">clean-css</span></code> plugin so you should use <code class="docutils literal notranslate"><span class="pre">npm</span></code>
instead of <code class="docutils literal notranslate"><span class="pre">apt</span></code> to install both <code class="docutils literal notranslate"><span class="pre">less</span></code> and the plugin:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">sudo</span> <span class="n">npm</span> <span class="n">install</span> <span class="o">-</span><span class="n">g</span> <span class="n">less</span> <span class="n">less</span><span class="o">-</span><span class="n">plugin</span><span class="o">-</span><span class="n">clean</span><span class="o">-</span><span class="n">css</span><span class="p">.</span>
</pre></div>
</div>
</li>
</ul>
<p>First generate makefiles or project files using CMake as described in
the previous section. Then compile the <code class="docutils literal notranslate"><span class="pre">doc</span></code> target/project, for example:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">make</span> <span class="n">doc</span>
</pre></div>
</div>
<p>This will generate the HTML documentation in <code class="docutils literal notranslate"><span class="pre">doc/html</span></code>.</p>
</section>
<section id="conda">
<h2>Conda<a class="headerlink" href="#conda" title="Permalink to this headline"></a></h2>
<p>fmt can be installed on Linux, macOS and Windows with
<a class="reference external" href="https://docs.conda.io/en/latest/">Conda</a>, using its
<a class="reference external" href="https://conda-forge.org">conda-forge</a>
<a class="reference external" href="https://github.com/conda-forge/fmt-feedstock">package</a>, as follows:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">conda</span> <span class="n">install</span> <span class="o">-</span><span class="n">c</span> <span class="n">conda</span><span class="o">-</span><span class="n">forge</span> <span class="n">fmt</span>
</pre></div>
</div>
</section>
<section id="vcpkg">
<h2>Vcpkg<a class="headerlink" href="#vcpkg" title="Permalink to this headline"></a></h2>
<p>You can download and install fmt using the <a class="reference external" href="https://github.com/Microsoft/vcpkg">vcpkg</a> dependency manager:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">git</span> <span class="n">clone</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//github.com/Microsoft/vcpkg.git</span>
<span class="n">cd</span> <span class="n">vcpkg</span>
<span class="p">.</span><span class="o">/</span><span class="n">bootstrap</span><span class="o">-</span><span class="n">vcpkg</span><span class="p">.</span><span class="n">sh</span>
<span class="p">.</span><span class="o">/</span><span class="n">vcpkg</span> <span class="n">integrate</span> <span class="n">install</span>
<span class="p">.</span><span class="o">/</span><span class="n">vcpkg</span> <span class="n">install</span> <span class="n">fmt</span>
</pre></div>
</div>
<p>The fmt port in vcpkg is kept up to date by Microsoft team members and community
contributors. If the version is out of date, please <a class="reference external" href="https://github.com/Microsoft/vcpkg">create an issue or pull
request</a> on the vcpkg repository.</p>
</section>
<section id="lhelper">
<h2>LHelper<a class="headerlink" href="#lhelper" title="Permalink to this headline"></a></h2>
<p>You can download and install fmt using
<a class="reference external" href="https://github.com/franko/lhelper">lhelper</a> dependency manager:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">lhelper</span> <span class="n">activate</span> <span class="o">&lt;</span><span class="n">some</span><span class="o">-</span><span class="n">environment</span><span class="o">&gt;</span>
<span class="n">lhelper</span> <span class="n">install</span> <span class="n">fmt</span>
</pre></div>
</div>
<p>All the recipes for lhelper are kept in the
<a class="reference external" href="https://github.com/franko/lhelper-recipes">lhelpers recipe</a> repository.</p>
</section>
<section id="android-ndk">
<h2>Android NDK<a class="headerlink" href="#android-ndk" title="Permalink to this headline"></a></h2>
<p>fmt provides <a class="reference external" href="https://github.com/fmtlib/fmt/blob/master/support/Android.mk">Android.mk file</a> that can be used to build the library
with <a class="reference external" href="https://developer.android.com/tools/sdk/ndk/index.html">Android NDK</a>.
For an example of using fmt with Android NDK, see the
<a class="reference external" href="https://github.com/fmtlib/android-ndk-example">android-ndk-example</a>
repository.</p>
</section>
<section id="homebrew">
<h2>Homebrew<a class="headerlink" href="#homebrew" title="Permalink to this headline"></a></h2>
<p>fmt can be installed on OS X using <a class="reference external" href="https://brew.sh/">Homebrew</a>:</p>
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">brew</span> <span class="n">install</span> <span class="n">fmt</span>
</pre></div>
</div>
</section>
</section>
</div>
</div>
</div>
<div class="footer" role="contentinfo">
&copy; Copyright 2012-present, Victor Zverovich.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 3.3.0.
</div>
<script src="_static/bootstrap.min.js"></script>
</body>
</html>

View File

@ -143,6 +143,18 @@ class dynamic_format_arg_store
} }
public: public:
constexpr dynamic_format_arg_store() = default;
constexpr dynamic_format_arg_store(
const dynamic_format_arg_store<Context>& store)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(),
#endif
data_(store.data_),
named_info_(store.named_info_) {
}
/** /**
\rst \rst
Adds an argument into the dynamic store for later passing to a formatting Adds an argument into the dynamic store for later passing to a formatting

View File

@ -308,7 +308,17 @@ inline auto do_write(const std::tm& time, const std::locale& loc, char format,
#else #else
using code_unit = char32_t; using code_unit = char32_t;
#endif #endif
auto& f = std::use_facet<std::codecvt<code_unit, char, std::mbstate_t>>(loc);
using codecvt = std::codecvt<code_unit, char, std::mbstate_t>;
#if FMT_CLANG_VERSION
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated"
auto& f = std::use_facet<codecvt>(loc);
# pragma clang diagnostic pop
#else
auto& f = std::use_facet<codecvt>(loc);
#endif
auto mb = std::mbstate_t(); auto mb = std::mbstate_t();
const char* from_next = nullptr; const char* from_next = nullptr;
code_unit* to_next = nullptr; code_unit* to_next = nullptr;

View File

@ -185,9 +185,13 @@ enum class terminal_color : uint8_t {
enum class emphasis : uint8_t { enum class emphasis : uint8_t {
bold = 1, bold = 1,
italic = 1 << 1, faint = 1 << 1,
underline = 1 << 2, italic = 1 << 2,
strikethrough = 1 << 3 underline = 1 << 3,
blink = 1 << 4,
reverse = 1 << 5,
conceal = 1 << 6,
strikethrough = 1 << 7,
}; };
// rgb is a struct for red, green and blue colors. // rgb is a struct for red, green and blue colors.
@ -409,16 +413,18 @@ template <typename Char> struct ansi_color_escape {
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
} }
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {}; uint8_t em_codes[num_emphases] = {};
uint8_t em_bits = static_cast<uint8_t>(em); if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1; if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3; if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4; if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough)) if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
em_codes[3] = 9; if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
size_t index = 0; size_t index = 0;
for (int i = 0; i < 4; ++i) { for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue; if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
@ -435,7 +441,8 @@ template <typename Char> struct ansi_color_escape {
} }
private: private:
Char buffer[7u + 3u * 4u + 1u]; static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT { char delimiter) FMT_NOEXCEPT {
@ -444,6 +451,10 @@ template <typename Char> struct ansi_color_escape {
out[2] = static_cast<Char>('0' + c % 10); out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter); out[3] = static_cast<Char>(delimiter);
} }
static FMT_CONSTEXPR bool has_emphasis(emphasis em,
emphasis mask) FMT_NOEXCEPT {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
}
}; };
template <typename Char> template <typename Char>

View File

@ -289,7 +289,7 @@ template <typename Char> struct runtime_named_field {
constexpr OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
bool found = (try_format_argument(out, name, args) || ...); bool found = (try_format_argument(out, name, args) || ...);
if (!found) { if (!found) {
throw format_error("argument with specified name is not found"); FMT_THROW(format_error("argument with specified name is not found"));
} }
return out; return out;
} }
@ -399,7 +399,7 @@ template <typename Char> struct arg_id_handler {
return 0; return 0;
} }
constexpr void on_error(const char* message) { throw format_error(message); } 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 {
@ -451,7 +451,7 @@ constexpr auto compile_format_string(S format_str) {
constexpr auto str = basic_string_view<char_type>(format_str); constexpr auto str = basic_string_view<char_type>(format_str);
if constexpr (str[POS] == '{') { if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
throw format_error("unmatched '{' in format string"); FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') { if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
@ -500,7 +500,7 @@ constexpr auto compile_format_string(S format_str) {
} }
} else if constexpr (str[POS] == '}') { } else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
throw format_error("unmatched '}' in format string"); FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else { } else {
constexpr auto end = parse_text(str, POS + 1); constexpr auto end = parse_text(str, POS + 1);

View File

@ -333,11 +333,6 @@ struct monostate {
constexpr monostate() {} constexpr monostate() {}
}; };
// Suppress "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
// (void)var does not work on many Intel compilers.
template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
// An enable_if helper to be used in template parameters which results in much // An enable_if helper to be used in template parameters which results in much
// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
// to workaround a bug in MSVC 2019 (see #1140 and #1186). // to workaround a bug in MSVC 2019 (see #1140 and #1186).
@ -349,6 +344,11 @@ template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
// Suppress "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
// (void)var does not work on many Intel compilers.
template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
constexpr FMT_INLINE auto is_constant_evaluated() FMT_NOEXCEPT -> bool { constexpr FMT_INLINE auto is_constant_evaluated() FMT_NOEXCEPT -> bool {
#ifdef __cpp_lib_is_constant_evaluated #ifdef __cpp_lib_is_constant_evaluated
return std::is_constant_evaluated(); return std::is_constant_evaluated();
@ -367,7 +367,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
# ifdef NDEBUG # ifdef NDEBUG
// FMT_ASSERT is not empty to avoid -Werror=empty-body. // FMT_ASSERT is not empty to avoid -Werror=empty-body.
# define FMT_ASSERT(condition, message) \ # define FMT_ASSERT(condition, message) \
::fmt::ignore_unused((condition), (message)) ::fmt::detail::ignore_unused((condition), (message))
# else # else
# define FMT_ASSERT(condition, message) \ # define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
@ -711,6 +711,22 @@ class appender;
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
template <typename Context, typename T>
constexpr auto is_const_formattable_impl(T*)
-> decltype(typename Context::template formatter_type<T>().format(
std::declval<const T&>(), std::declval<Context&>()),
true) {
return true;
}
template <typename Context>
constexpr auto is_const_formattable_impl(...) -> bool {
return false;
}
template <typename T, typename Context>
constexpr auto is_const_formattable() -> bool {
return is_const_formattable_impl<Context>(static_cast<T*>(nullptr));
}
// Extracts a reference to the container from back_insert_iterator. // Extracts a reference to the container from back_insert_iterator.
template <typename Container> template <typename Container>
inline auto get_container(std::back_insert_iterator<Container> it) inline auto get_container(std::back_insert_iterator<Container> it)
@ -1112,8 +1128,8 @@ template <typename Char> struct named_arg_value {
template <typename Context> struct custom_value { template <typename Context> struct custom_value {
using parse_context = typename Context::parse_context_type; using parse_context = typename Context::parse_context_type;
const void* value; void* value;
void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
}; };
// A formatting argument value. // A formatting argument value.
@ -1164,26 +1180,30 @@ template <typename Context> class value {
FMT_INLINE value(const named_arg_info<char_type>* args, size_t size) FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
: named_args{args, size} {} : named_args{args, size} {}
template <typename T> FMT_CONSTEXPR FMT_INLINE value(const T& val) { template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
custom.value = &val; using value_type = remove_cvref_t<T>;
custom.value = const_cast<value_type*>(&val);
// Get the formatter type through the context to allow different contexts // Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and // have different extension points, e.g. `formatter<T>` for `format` and
// `printf_formatter<T>` for `printf`. // `printf_formatter<T>` for `printf`.
custom.format = format_custom_arg< custom.format = format_custom_arg<
T, conditional_t<has_formatter<T, Context>::value, value_type,
typename Context::template formatter_type<T>, conditional_t<has_formatter<value_type, Context>::value,
fallback_formatter<T, char_type>>>; typename Context::template formatter_type<value_type>,
fallback_formatter<value_type, char_type>>>;
} }
private: private:
// Formats an argument of a custom type, such as a user-defined class. // Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter> template <typename T, typename Formatter>
static void format_custom_arg(const void* arg, static void format_custom_arg(void* arg,
typename Context::parse_context_type& parse_ctx, typename Context::parse_context_type& parse_ctx,
Context& ctx) { Context& ctx) {
Formatter f; auto f = Formatter();
parse_ctx.advance_to(f.parse(parse_ctx)); parse_ctx.advance_to(f.parse(parse_ctx));
ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx)); using qualified_type =
conditional_t<is_const_formattable<T, Context>(), const T, T>;
ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
} }
}; };
@ -1323,11 +1343,16 @@ template <typename Context> struct arg_mapper {
static_cast<typename std::underlying_type<T>::type>(val))) { static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val)); return map(static_cast<typename std::underlying_type<T>::type>(val));
} }
template <typename T, template <typename T, typename U = remove_cvref_t<T>,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value && FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
(has_formatter<T, Context>::value || !std::is_array<U>::value &&
has_fallback_formatter<T, char_type>::value))> (has_formatter<U, Context>::value ||
FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> const T& { has_fallback_formatter<U, char_type>::value))>
FMT_CONSTEXPR FMT_INLINE auto map(T&& val) -> T& {
static_assert(is_const_formattable<U, Context>() ||
!std::is_const<remove_reference_t<T>>() ||
has_fallback_formatter<U, char_type>(),
"cannot format a const argument");
return val; return val;
} }
@ -1562,8 +1587,8 @@ FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg<Context> {
// another (not recommended). // another (not recommended).
template <bool IS_PACKED, typename Context, type, typename T, template <bool IS_PACKED, typename Context, type, typename T,
FMT_ENABLE_IF(IS_PACKED)> FMT_ENABLE_IF(IS_PACKED)>
FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value<Context> { FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
const auto& arg = arg_mapper<Context>().map(val); const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
static_assert( static_assert(
!std::is_same<decltype(arg), const unformattable&>::value, !std::is_same<decltype(arg), const unformattable&>::value,
"Cannot format an argument. To make type T formattable provide a " "Cannot format an argument. To make type T formattable provide a "
@ -1684,14 +1709,16 @@ class format_arg_store
: 0); : 0);
public: public:
FMT_CONSTEXPR FMT_INLINE format_arg_store(const Args&... args) template <typename... T>
FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
: :
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this), basic_format_args<Context>(*this),
#endif #endif
data_{detail::make_arg< data_{detail::make_arg<
is_packed, Context, is_packed, Context,
detail::mapped_type_constant<Args, Context>::value>(args)...} { detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
std::forward<T>(args))...} {
detail::init_named_args(data_.named_args(), 0, 0, args...); detail::init_named_args(data_.named_args(), 0, 0, args...);
} }
}; };
@ -1705,9 +1732,9 @@ class format_arg_store
\endrst \endrst
*/ */
template <typename Context = format_context, typename... Args> template <typename Context = format_context, typename... Args>
constexpr auto make_format_args(const Args&... args) constexpr auto make_format_args(Args&&... args)
-> format_arg_store<Context, Args...> { -> format_arg_store<Context, remove_cvref_t<Args>...> {
return {args...}; return {std::forward<Args>(args)...};
} }
/** /**

View File

@ -2525,8 +2525,8 @@ template <> struct formatter<detail::bigint> {
}; };
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
for_each_codepoint(s, [this](uint32_t cp, int error) { for_each_codepoint(s, [this](uint32_t cp, string_view) {
if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8"));
if (cp <= 0xFFFF) { if (cp <= 0xFFFF) {
buffer_.push_back(static_cast<wchar_t>(cp)); buffer_.push_back(static_cast<wchar_t>(cp));
} else { } else {
@ -2534,6 +2534,7 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10))); buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF))); buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
} }
return true;
}); });
buffer_.push_back(0); buffer_.push_back(0);
} }

View File

@ -150,16 +150,19 @@ FMT_END_NAMESPACE
// __builtin_clz is broken in clang with Microsoft CodeGen: // __builtin_clz is broken in clang with Microsoft CodeGen:
// https://github.com/fmtlib/fmt/issues/519 // https://github.com/fmtlib/fmt/issues/519
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz) || FMT_ICC_VERSION) && \
!FMT_MSC_VER
# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) # define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
#endif #endif
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll) || \
FMT_ICC_VERSION) && \
!FMT_MSC_VER
# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) # define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
#endif #endif
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz)) #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz) || FMT_ICC_VERSION)
# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) # define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
#endif #endif
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll)) #if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_ICC_VERSION)
# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) # define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
#endif #endif
@ -480,27 +483,38 @@ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
return next; return next;
} }
enum { invalid_code_point = ~uint32_t() };
// Invokes f(cp, sv) for every code point cp in s with sv being the string view
// corresponding to the code point. cp is invalid_code_point on error.
template <typename F> template <typename F>
FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
auto decode = [f](const char* p) { auto decode = [f](const char* buf_ptr, const char* ptr) {
auto cp = uint32_t(); auto cp = uint32_t();
auto error = 0; auto error = 0;
p = utf8_decode(p, &cp, &error); auto end = utf8_decode(buf_ptr, &cp, &error);
f(cp, error); bool result = f(error ? invalid_code_point : cp,
return p; string_view(ptr, to_unsigned(end - buf_ptr)));
return result ? end : nullptr;
}; };
auto p = s.data(); auto p = s.data();
const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
if (s.size() >= block_size) { if (s.size() >= block_size) {
for (auto end = p + s.size() - block_size + 1; p < end;) p = decode(p); for (auto end = p + s.size() - block_size + 1; p < end;) {
p = decode(p, p);
if (!p) return;
}
} }
if (auto num_chars_left = s.data() + s.size() - p) { if (auto num_chars_left = s.data() + s.size() - p) {
char buf[2 * block_size - 1] = {}; char buf[2 * block_size - 1] = {};
copy_str<char>(p, p + num_chars_left, buf); copy_str<char>(p, p + num_chars_left, buf);
p = buf; const char* buf_ptr = buf;
do { do {
p = decode(p); auto end = decode(buf_ptr, p);
} while (p - buf < num_chars_left); if (!end) return;
p += end - buf_ptr;
buf_ptr = end;
} while (buf_ptr - buf < num_chars_left);
} }
} }
@ -515,10 +529,10 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) {
// It is not a lambda for compatibility with C++14. // It is not a lambda for compatibility with C++14.
struct count_code_points { struct count_code_points {
size_t* count; size_t* count;
FMT_CONSTEXPR void operator()(uint32_t cp, int error) const { FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool {
*count += detail::to_unsigned( *count += detail::to_unsigned(
1 + 1 +
(error == 0 && cp >= 0x1100 && (cp >= 0x1100 &&
(cp <= 0x115f || // Hangul Jamo init. consonants (cp <= 0x115f || // Hangul Jamo init. consonants
cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET
cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET
@ -536,6 +550,7 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) {
(cp >= 0x1f300 && cp <= 0x1f64f) || (cp >= 0x1f300 && cp <= 0x1f64f) ||
// Supplemental Symbols and Pictographs: // Supplemental Symbols and Pictographs:
(cp >= 0x1f900 && cp <= 0x1f9ff)))); (cp >= 0x1f900 && cp <= 0x1f9ff))));
return true;
} }
}; };
for_each_codepoint(s, count_code_points{&num_code_points}); for_each_codepoint(s, count_code_points{&num_code_points});
@ -607,8 +622,8 @@ enum { inline_buffer_size = 500 };
**Example**:: **Example**::
fmt::memory_buffer out; auto out = fmt::memory_buffer();
format_to(out, "The answer is {}.", 42); format_to(std::back_inserter(out), "The answer is {}.", 42);
This will append the following output to the ``out`` object: This will append the following output to the ``out`` object:
@ -916,23 +931,33 @@ FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int {
} }
#endif #endif
#ifdef FMT_BUILTIN_CLZLL
// It is a separate function rather than a part of count_digits to workaround
// the lack of static constexpr in constexpr functions.
inline auto do_count_digits(uint64_t n) -> int {
// This has comparable performance to the version by Kendall Willets
// (https://github.com/fmtlib/format-benchmark/blob/master/digits10)
// but uses smaller tables.
// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
static constexpr uint8_t bsr2log10[] = {
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
static constexpr const uint64_t zero_or_powers_of_10[] = {
0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
10000000000000000000ULL};
return t - (n < zero_or_powers_of_10[t]);
}
#endif
// Returns the number of decimal digits in n. Leading zeros are not counted // Returns the number of decimal digits in n. Leading zeros are not counted
// except for n == 0 in which case count_digits returns 1. // except for n == 0 in which case count_digits returns 1.
FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
#ifdef FMT_BUILTIN_CLZLL #ifdef FMT_BUILTIN_CLZLL
if (!is_constant_evaluated()) { if (!is_constant_evaluated()) {
// https://github.com/fmtlib/format-benchmark/blob/master/digits10 return do_count_digits(n);
// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
constexpr uint16_t bsr2log10[] = {
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
constexpr const uint64_t zero_or_powers_of_10[] = {
0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
10000000000000000000ULL};
return t - (n < zero_or_powers_of_10[t]);
} }
#endif #endif
return count_digits_fallback(n); return count_digits_fallback(n);
@ -954,12 +979,13 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int {
template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; template <> auto count_digits<4>(detail::fallback_uintptr n) -> int;
#ifdef FMT_BUILTIN_CLZ
// It is a separate function rather than a part of count_digits to workaround // It is a separate function rather than a part of count_digits to workaround
// the lack of static constexpr in constexpr functions. // the lack of static constexpr in constexpr functions.
FMT_INLINE uint64_t count_digits_inc(int n) { FMT_INLINE auto do_count_digits(uint32_t n) -> int {
// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. // An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
// This increments the upper 32 bits (log10(T) - 1) when >= T is added. // This increments the upper 32 bits (log10(T) - 1) when >= T is added.
#define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T) # define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T)
static constexpr uint64_t table[] = { static constexpr uint64_t table[] = {
FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8
FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64
@ -973,15 +999,16 @@ FMT_INLINE uint64_t count_digits_inc(int n) {
FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M
FMT_INC(1000000000), FMT_INC(1000000000) // 4B FMT_INC(1000000000), FMT_INC(1000000000) // 4B
}; };
return table[n]; auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31];
return static_cast<int>((n + inc) >> 32);
} }
#endif
// Optional version of count_digits for better performance on 32-bit platforms. // Optional version of count_digits for better performance on 32-bit platforms.
FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
#ifdef FMT_BUILTIN_CLZ #ifdef FMT_BUILTIN_CLZ
if (!is_constant_evaluated()) { if (!is_constant_evaluated()) {
auto inc = count_digits_inc(FMT_BUILTIN_CLZ(n | 1) ^ 31); return do_count_digits(n);
return static_cast<int>((n + inc) >> 32);
} }
#endif #endif
return count_digits_fallback(n); return count_digits_fallback(n);
@ -1393,55 +1420,83 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
}); });
} }
template <typename Char> class digit_grouping {
private:
thousands_sep_result<Char> sep_;
struct next_state {
std::string::const_iterator group;
int pos;
};
next_state initial_state() const { return {sep_.grouping.begin(), 0}; }
// Returns the next digit group separator position.
int next(next_state& state) const {
if (!sep_.thousands_sep) return max_value<int>();
if (state.group == sep_.grouping.end())
return state.pos += sep_.grouping.back();
if (*state.group <= 0 || *state.group == max_value<char>())
return max_value<int>();
state.pos += *state.group++;
return state.pos;
}
public:
explicit digit_grouping(locale_ref loc, bool localized = true) {
if (localized)
sep_ = thousands_sep<Char>(loc);
else
sep_.thousands_sep = Char();
}
Char separator() const { return sep_.thousands_sep; }
int count_separators(int num_digits) const {
int count = 0;
auto state = initial_state();
while (num_digits > next(state)) ++count;
return count;
}
// Applies grouping to digits and write the output to out.
template <typename Out, typename C>
Out apply(Out out, basic_string_view<C> digits) const {
auto num_digits = static_cast<int>(digits.size());
auto separators = basic_memory_buffer<int>();
separators.push_back(0);
auto state = initial_state();
while (int i = next(state)) {
if (i >= num_digits) break;
separators.push_back(i);
}
for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
i < num_digits; ++i) {
if (num_digits - i == separators[sep_index]) {
*out++ = separator();
--sep_index;
}
*out++ = static_cast<Char>(digits[to_unsigned(i)]);
}
return out;
}
};
template <typename OutputIt, typename UInt, typename Char> template <typename OutputIt, typename UInt, typename Char>
auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, auto write_int_localized(OutputIt& out, UInt value, unsigned prefix,
const basic_format_specs<Char>& specs, locale_ref loc) const basic_format_specs<Char>& specs, locale_ref loc)
-> bool { -> bool {
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, ""); static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
const auto sep_size = 1;
auto ts = thousands_sep<Char>(loc);
if (!ts.thousands_sep) return false;
int num_digits = count_digits(value); int num_digits = count_digits(value);
int size = num_digits, n = num_digits;
const std::string& groups = ts.grouping;
std::string::const_iterator group = groups.cbegin();
while (group != groups.cend() && n > *group && *group > 0 &&
*group != max_value<char>()) {
size += sep_size;
n -= *group;
++group;
}
if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back());
char digits[40]; char digits[40];
format_decimal(digits, value, num_digits); format_decimal(digits, value, num_digits);
basic_memory_buffer<Char> buffer;
if (prefix != 0) ++size; auto grouping = digit_grouping<Char>(loc);
const auto usize = to_unsigned(size); unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits +
buffer.resize(usize); grouping.count_separators(num_digits));
basic_string_view<Char> s(&ts.thousands_sep, sep_size);
// Index of a decimal digit with the least significant digit having index 0.
int digit_index = 0;
group = groups.cbegin();
auto p = buffer.data() + size - 1;
for (int i = num_digits - 1; i > 0; --i) {
*p-- = static_cast<Char>(digits[i]);
if (*group <= 0 || ++digit_index % *group != 0 ||
*group == max_value<char>())
continue;
if (group + 1 != groups.cend()) {
digit_index = 0;
++group;
}
std::uninitialized_copy(s.data(), s.data() + s.size(),
make_checked(p, s.size()));
p -= s.size();
}
*p-- = static_cast<Char>(*digits);
if (prefix != 0) *p = static_cast<Char>(prefix);
auto data = buffer.data();
out = write_padded<align::right>( out = write_padded<align::right>(
out, specs, usize, usize, [=](reserve_iterator<OutputIt> it) { out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
return copy_str<Char>(data, data + size, it); if (prefix != 0) *it++ = static_cast<Char>(prefix);
return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
}); });
return true; return true;
} }
@ -1616,7 +1671,7 @@ inline auto get_significand_size(const dragonbox::decimal_fp<T>& fp) -> int {
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
inline auto write_significand(OutputIt out, const char* significand, inline auto write_significand(OutputIt out, const char* significand,
int& significand_size) -> OutputIt { int significand_size) -> OutputIt {
return copy_str<Char>(significand, significand + significand_size, out); return copy_str<Char>(significand, significand + significand_size, out);
} }
template <typename Char, typename OutputIt, typename UInt> template <typename Char, typename OutputIt, typename UInt>
@ -1624,6 +1679,20 @@ inline auto write_significand(OutputIt out, UInt significand,
int significand_size) -> OutputIt { int significand_size) -> OutputIt {
return format_decimal<Char>(out, significand, significand_size).end; return format_decimal<Char>(out, significand, significand_size).end;
} }
template <typename Char, typename OutputIt, typename T>
inline auto write_significand(OutputIt out, T significand, int significand_size,
int exponent,
const digit_grouping<Char>& grouping)
-> OutputIt {
if (!grouping.separator()) {
out = write_significand<Char>(out, significand, significand_size);
return detail::fill_n(out, exponent, static_cast<Char>('0'));
}
auto buffer = memory_buffer();
write_significand<char>(appender(buffer), significand, significand_size);
detail::fill_n(appender(buffer), exponent, '0');
return grouping.apply(out, string_view(buffer.data(), buffer.size()));
}
template <typename Char, typename UInt, template <typename Char, typename UInt,
FMT_ENABLE_IF(std::is_integral<UInt>::value)> FMT_ENABLE_IF(std::is_integral<UInt>::value)>
@ -1666,10 +1735,28 @@ inline auto write_significand(OutputIt out, const char* significand,
significand + significand_size, out); significand + significand_size, out);
} }
template <typename OutputIt, typename Char, typename T>
inline auto write_significand(OutputIt out, T significand, int significand_size,
int integral_size, Char decimal_point,
const digit_grouping<Char>& grouping)
-> OutputIt {
if (!grouping.separator()) {
return write_significand(out, significand, significand_size, integral_size,
decimal_point);
}
auto buffer = basic_memory_buffer<Char>();
write_significand(buffer_appender<Char>(buffer), significand,
significand_size, integral_size, decimal_point);
grouping.apply(
out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size)));
return detail::copy_str_noinline<Char>(buffer.data() + integral_size,
buffer.end(), out);
}
template <typename OutputIt, typename DecimalFP, typename Char> template <typename OutputIt, typename DecimalFP, typename Char>
auto write_float(OutputIt out, const DecimalFP& fp, auto write_float(OutputIt out, const DecimalFP& fp,
const basic_format_specs<Char>& specs, float_specs fspecs, const basic_format_specs<Char>& specs, float_specs fspecs,
Char decimal_point) -> OutputIt { locale_ref loc) -> OutputIt {
auto significand = fp.significand; auto significand = fp.significand;
int significand_size = get_significand_size(fp); int significand_size = get_significand_size(fp);
static const Char zero = static_cast<Char>('0'); static const Char zero = static_cast<Char>('0');
@ -1677,6 +1764,9 @@ auto write_float(OutputIt out, const DecimalFP& fp,
size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
using iterator = reserve_iterator<OutputIt>; using iterator = reserve_iterator<OutputIt>;
Char decimal_point =
fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
int output_exp = fp.exponent + significand_size - 1; int output_exp = fp.exponent + significand_size - 1;
auto use_exp_format = [=]() { auto use_exp_format = [=]() {
if (fspecs.format == float_format::exp) return true; if (fspecs.format == float_format::exp) return true;
@ -1728,10 +1818,12 @@ auto write_float(OutputIt out, const DecimalFP& fp,
if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1;
if (num_zeros > 0) size += to_unsigned(num_zeros) + 1; if (num_zeros > 0) size += to_unsigned(num_zeros) + 1;
} }
auto grouping = digit_grouping<Char>(loc, fspecs.locale);
size += to_unsigned(grouping.count_separators(significand_size));
return write_padded<align::right>(out, specs, size, [&](iterator it) { return write_padded<align::right>(out, specs, size, [&](iterator it) {
if (sign) *it++ = static_cast<Char>(data::signs[sign]); if (sign) *it++ = static_cast<Char>(data::signs[sign]);
it = write_significand<Char>(it, significand, significand_size); it = write_significand<Char>(it, significand, significand_size,
it = detail::fill_n(it, fp.exponent, zero); fp.exponent, grouping);
if (!fspecs.showpoint) return it; if (!fspecs.showpoint) return it;
*it++ = decimal_point; *it++ = decimal_point;
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
@ -1740,10 +1832,12 @@ auto write_float(OutputIt out, const DecimalFP& fp,
// 1234e-2 -> 12.34[0+] // 1234e-2 -> 12.34[0+]
int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
auto grouping = digit_grouping<Char>(loc, fspecs.locale);
size += to_unsigned(grouping.count_separators(significand_size));
return write_padded<align::right>(out, specs, size, [&](iterator it) { return write_padded<align::right>(out, specs, size, [&](iterator it) {
if (sign) *it++ = static_cast<Char>(data::signs[sign]); if (sign) *it++ = static_cast<Char>(data::signs[sign]);
it = write_significand(it, significand, significand_size, exp, it = write_significand(it, significand, significand_size, exp,
decimal_point); decimal_point, grouping);
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
}); });
} }
@ -1808,10 +1902,8 @@ auto write(OutputIt out, T value, basic_format_specs<Char> specs,
fspecs.use_grisu = is_fast_float<T>(); fspecs.use_grisu = is_fast_float<T>();
int exp = format_float(promote_float(value), precision, fspecs, buffer); int exp = format_float(promote_float(value), precision, fspecs, buffer);
fspecs.precision = precision; fspecs.precision = precision;
Char point =
fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
auto fp = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp}; auto fp = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
return write_float(out, fp, specs, fspecs, point); return write_float(out, fp, specs, fspecs, loc);
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
@ -1836,7 +1928,7 @@ auto write(OutputIt out, T value) -> OutputIt {
return write_nonfinite(out, std::isinf(value), specs, fspecs); return write_nonfinite(out, std::isinf(value), specs, fspecs);
auto dec = dragonbox::to_decimal(static_cast<floaty>(value)); auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
return write_float(out, dec, specs, fspecs, static_cast<Char>('.')); return write_float(out, dec, specs, fspecs, {});
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,

View File

@ -394,19 +394,15 @@ struct ostream_params {
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
constexpr detail::buffer_size buffer_size; // Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
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 final : private detail::buffer<char> {
private: private:
file file_; file file_;
void flush() {
if (size() == 0) return;
file_.write(data(), size());
clear();
}
void grow(size_t) override; void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params) ostream(cstring_view path, const detail::ostream_params& params)
@ -426,6 +422,12 @@ class FMT_API ostream final : private detail::buffer<char> {
delete[] data(); delete[] data();
} }
void flush() {
if (size() == 0) return;
file_.write(data(), size());
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);

View File

@ -143,16 +143,16 @@ struct has_mutable_begin_end : std::false_type {};
template <typename T> template <typename T>
struct has_const_begin_end< struct has_const_begin_end<
T, void_t<decltype(detail::range_begin( T,
std::declval<const remove_cvref_t<T>&>())), void_t<
decltype(detail::range_begin( decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
std::declval<const remove_cvref_t<T>&>()))>> decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {}; : std::true_type {};
template <typename T> 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_begin(std::declval<T>())), decltype(detail::range_end(std::declval<T>())),
enable_if_t<std::is_copy_constructible<T>::value>>> enable_if_t<std::is_copy_constructible<T>::value>>>
: std::true_type {}; : std::true_type {};
@ -160,30 +160,6 @@ template <typename T>
struct is_range_<T, void> struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value || : std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {}; has_mutable_begin_end<T>::value)> {};
template <typename T, typename Enable = void> struct range_to_view;
template <typename T>
struct range_to_view<T, enable_if_t<has_const_begin_end<T>::value>> {
struct view_t {
const T* m_range_ptr;
auto begin() const FMT_DECLTYPE_RETURN(detail::range_begin(*m_range_ptr));
auto end() const FMT_DECLTYPE_RETURN(detail::range_end(*m_range_ptr));
};
static auto view(const T& range) -> view_t { return {&range}; }
};
template <typename T>
struct range_to_view<T, enable_if_t<!has_const_begin_end<T>::value &&
has_mutable_begin_end<T>::value>> {
struct view_t {
T m_range_copy;
auto begin() FMT_DECLTYPE_RETURN(detail::range_begin(m_range_copy));
auto end() FMT_DECLTYPE_RETURN(detail::range_end(m_range_copy));
};
static auto view(const T& range) -> view_t { return {range}; }
};
# undef FMT_DECLTYPE_RETURN # undef FMT_DECLTYPE_RETURN
#endif #endif
@ -251,12 +227,272 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
return out; return out;
} }
template < struct singleton {
typename Char, typename OutputIt, typename Arg, unsigned char upper;
FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)> unsigned char lowercount;
OutputIt write_range_entry(OutputIt out, const Arg& v) { };
inline auto check(uint16_t x, const singleton* singletonuppers,
size_t singletonuppers_size,
const unsigned char* singletonlowers,
const unsigned char* normal, size_t normal_size) -> bool {
auto xupper = x >> 8;
auto lowerstart = 0;
for (size_t i = 0; i < singletonuppers_size; ++i) {
auto su = singletonuppers[i];
auto lowerend = lowerstart + su.lowercount;
if (xupper < su.upper) break;
if (xupper == su.upper) {
for (auto j = lowerstart; j < lowerend; ++j) {
if (singletonlowers[j] == x) return false;
}
}
lowerstart = lowerend;
}
auto xsigned = static_cast<int>(x);
auto current = true;
for (size_t i = 0; i < normal_size; ++i) {
auto v = static_cast<int>(normal[i]);
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[i++] : v;
xsigned -= len;
if (xsigned < 0) break;
current = !current;
}
return current;
}
// Returns true iff the code point cp is printable.
// This code is generated by support/printable.py.
inline auto is_printable(uint32_t cp) -> bool {
static constexpr singleton singletons0u[] = {
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
};
static constexpr unsigned char singletons0l[] = {
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
0xfe, 0xff,
};
static constexpr singleton singletons1u[] = {
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
{0xfa, 2}, {0xfb, 1},
};
static constexpr unsigned char singletons1l[] = {
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
};
static constexpr unsigned char normal0[] = {
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
};
static constexpr unsigned char normal1[] = {
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
};
auto lower = static_cast<uint16_t>(cp);
if (cp < 0x10000) {
return check(lower, singletons0u,
sizeof(singletons0u) / sizeof(*singletons0u), singletons0l,
normal0, sizeof(normal0));
}
if (cp < 0x20000) {
return check(lower, singletons1u,
sizeof(singletons1u) / sizeof(*singletons1u), singletons1l,
normal1, sizeof(normal1));
}
if (0x2a6de <= cp && cp < 0x2a700) return false;
if (0x2b735 <= cp && cp < 0x2b740) return false;
if (0x2b81e <= cp && cp < 0x2b820) return false;
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
if (0x2fa1e <= cp && cp < 0x30000) return false;
if (0x3134b <= cp && cp < 0xe0100) return false;
if (0xe01f0 <= cp && cp < 0x110000) return false;
return true;
}
inline auto needs_escape(uint32_t cp) -> bool {
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
!is_printable(cp);
}
template <typename Char> struct find_escape_result {
const Char* begin;
const Char* end;
uint32_t cp;
};
template <typename Char>
auto find_escape(const Char* begin, const Char* end)
-> find_escape_result<Char> {
for (; begin != end; ++begin) {
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
if (needs_escape(cp)) return {begin, begin + 1, cp};
}
return {begin, nullptr, 0};
}
auto find_escape(const char* begin, const char* end)
-> find_escape_result<char> {
if (!is_utf8()) return find_escape<char>(begin, end);
auto result = find_escape_result<char>{end, nullptr, 0};
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
[&](uint32_t cp, string_view sv) {
if (needs_escape(cp)) {
result = {sv.begin(), sv.end(), cp};
return false;
}
return true;
});
return result;
}
template <typename Char, typename OutputIt>
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
*out++ = '"'; *out++ = '"';
out = write<Char>(out, v); auto begin = str.begin(), end = str.end();
do {
auto escape = find_escape(begin, end);
out = copy_str<Char>(begin, escape.begin, out);
begin = escape.end;
if (!begin) break;
auto c = static_cast<Char>(escape.cp);
switch (escape.cp) {
case '\n':
*out++ = '\\';
c = 'n';
break;
case '\r':
*out++ = '\\';
c = 'r';
break;
case '\t':
*out++ = '\\';
c = 't';
break;
case '"':
FMT_FALLTHROUGH;
case '\\':
*out++ = '\\';
break;
default:
if (is_utf8() && escape.cp > 0xffff) {
out = format_to(out, "\\U{:08x}", escape.cp);
continue;
}
for (Char escape_char : basic_string_view<Char>(
escape.begin, to_unsigned(escape.end - escape.begin))) {
out = format_to(
out, "\\x{:02x}",
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
}
continue;
}
*out++ = c;
} while (begin != end);
*out++ = '"'; *out++ = '"';
return out; return out;
} }
@ -347,13 +583,16 @@ struct formatter<
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext> template <
typename FormatContext::iterator format(const T& values, FormatContext& ctx) { typename FormatContext, typename U,
FMT_ENABLE_IF(
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
const T, T>>::value)>
auto format(U& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = detail::copy(formatting.prefix, ctx.out()); auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0; size_t i = 0;
auto view = detail::range_to_view<T>::view(values); auto it = std::begin(values);
auto it = view.begin(); auto end = std::end(values);
auto end = view.end();
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out); if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it); out = detail::write_range_entry<Char>(out, *it);

236
vendor/Fmt/support/printable.py vendored Normal file
View File

@ -0,0 +1,236 @@
#!/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("""\
struct singleton {
unsigned char upper;
unsigned char lowercount;
};
inline auto check(uint16_t x, const singleton* singletonuppers,
size_t singletonuppers_size,
const unsigned char* singletonlowers,
const unsigned char* normal, size_t normal_size) -> bool {
auto xupper = x >> 8;
auto lowerstart = 0;
for (size_t i = 0; i < singletonuppers_size; ++i) {
auto su = singletonuppers[i];
auto lowerend = lowerstart + su.lowercount;
if (xupper < su.upper) break;
if (xupper == su.upper) {
for (auto j = lowerstart; j < lowerend; ++j) {
if (singletonlowers[j] == x) return false;
}
}
lowerstart = lowerend;
}
auto xsigned = static_cast<int>(x);
auto current = true;
for (size_t i = 0; i < normal_size; ++i) {
auto v = static_cast<int>(normal[i]);
auto len = v & 0x80 != 0 ? (v & 0x7f) << 8 | normal[i++] : v;
xsigned -= len;
if (xsigned < 0) break;
current = !current;
}
return current;
}
inline auto is_printable(uint32_t cp) -> bool {\
""")
print_singletons(singletons0u, singletons0l, 'singletons0u', 'singletons0l')
print_singletons(singletons1u, singletons1l, 'singletons1u', 'singletons1l')
print_normal(normal0, 'normal0')
print_normal(normal1, 'normal1')
print("""\
auto lower = static_cast<uint16_t>(cp);
if (cp < 0x10000) {
return check(lower, singletons0u,
sizeof(singletons0u) / sizeof(*singletons0u), singletons0l,
normal0, sizeof(normal0));
}
if (cp < 0x20000) {
return check(lower, singletons1u,
sizeof(singletons1u) / sizeof(*singletons1u), singletons1l,
normal1, sizeof(normal1));
}\
""")
for a, b in extra:
print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b))
print("""\
return true;
}\
""")
if __name__ == '__main__':
main()

View File

@ -26,6 +26,16 @@ function(add_fmt_executable name)
if (MINGW) if (MINGW)
target_link_libraries(${name} -static-libgcc -static-libstdc++) target_link_libraries(${name} -static-libgcc -static-libstdc++)
endif () endif ()
# (Wstringop-overflow) - [meta-bug] bogus/missing -Wstringop-overflow warnings
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88443
# Bogus -Wstringop-overflow warning
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100395
# [10 Regression] spurious -Wstringop-overflow writing to a trailing array plus offset
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95353
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND
NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
target_link_libraries(${name} -Wno-stringop-overflow)
endif ()
endfunction() endfunction()
# Adds a test. # Adds a test.
@ -41,7 +51,7 @@ function(add_fmt_test name)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-weak-vtables) set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-weak-vtables)
endif () endif ()
elseif (ADD_FMT_TEST_MODULE) elseif (ADD_FMT_TEST_MODULE)
set(libs test-main test-module) set(libs gtest test-module)
set_source_files_properties(${name}.cc PROPERTIES OBJECT_DEPENDS test-module) set_source_files_properties(${name}.cc PROPERTIES OBJECT_DEPENDS test-module)
else () else ()
set(libs test-main fmt) set(libs test-main fmt)
@ -97,7 +107,7 @@ if (FMT_CAN_MODULE)
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>) $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
enable_module(test-module) enable_module(test-module)
add_fmt_test(module-test MODULE) add_fmt_test(module-test MODULE test-main.cc)
if (MSVC) if (MSVC)
target_compile_options(test-module PRIVATE /utf-8 /Zc:__cplusplus target_compile_options(test-module PRIVATE /utf-8 /Zc:__cplusplus
/Zc:externConstexpr /Zc:inline) /Zc:externConstexpr /Zc:inline)

View File

@ -171,3 +171,16 @@ TEST(args_test, throw_on_copy) {
} }
EXPECT_EQ(fmt::vformat("{}", store), "foo"); EXPECT_EQ(fmt::vformat("{}", store), "foo");
} }
TEST(args_test, copy_constructor) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.push_back(fmt::arg("test1", "value1"));
store.push_back(fmt::arg("test2", "value2"));
store.push_back(fmt::arg("test3", "value3"));
auto store2 = store;
store2.push_back(fmt::arg("test4", "value4"));
auto result = fmt::vformat("{test1} {test2} {test3} {test4}", store2);
EXPECT_EQ(result, "value1 value2 value3 value4");
}

View File

@ -20,10 +20,16 @@ TEST(color_test, format) {
fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"), fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"),
"\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[1mbold\x1b[0m"); EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[1mbold\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::faint, "faint"), "\x1b[2mfaint\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"), EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"),
"\x1b[3mitalic\x1b[0m"); "\x1b[3mitalic\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"), EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"),
"\x1b[4munderline\x1b[0m"); "\x1b[4munderline\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::blink, "blink"), "\x1b[5mblink\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::reverse, "reverse"),
"\x1b[7mreverse\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::conceal, "conceal"),
"\x1b[8mconceal\x1b[0m");
EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"), EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"),
"\x1b[9mstrikethrough\x1b[0m"); "\x1b[9mstrikethrough\x1b[0m");
EXPECT_EQ( EXPECT_EQ(

View File

@ -151,7 +151,9 @@ TEST(buffer_test, indestructible) {
template <typename T> struct mock_buffer final : buffer<T> { template <typename T> struct mock_buffer final : buffer<T> {
MOCK_METHOD1(do_grow, size_t(size_t capacity)); MOCK_METHOD1(do_grow, size_t(size_t capacity));
void grow(size_t capacity) { this->set(this->data(), do_grow(capacity)); } void grow(size_t capacity) override {
this->set(this->data(), do_grow(capacity));
}
mock_buffer(T* data = nullptr, size_t buf_capacity = 0) { mock_buffer(T* data = nullptr, size_t buf_capacity = 0) {
this->set(data, buf_capacity); this->set(data, buf_capacity);
@ -452,7 +454,7 @@ struct check_custom {
struct test_buffer final : fmt::detail::buffer<char> { struct test_buffer final : fmt::detail::buffer<char> {
char data[10]; char data[10];
test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {} test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
void grow(size_t) {} void grow(size_t) override {}
} buffer; } buffer;
auto parse_ctx = fmt::format_parse_context(""); auto parse_ctx = fmt::format_parse_context("");
auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer), auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer),
@ -845,3 +847,43 @@ TEST(core_test, adl) {
fmt::print("{}", s); fmt::print("{}", s);
fmt::print(stdout, "{}", s); fmt::print(stdout, "{}", s);
} }
struct const_formattable {};
struct nonconst_formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<const_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(const const_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
template <> struct formatter<nonconst_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(nonconst_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
TEST(core_test, is_const_formattable) {
EXPECT_TRUE((fmt::detail::is_const_formattable<const_formattable,
fmt::format_context>()));
EXPECT_FALSE((fmt::detail::is_const_formattable<nonconst_formattable,
fmt::format_context>()));
}
TEST(core_test, format_nonconst) {
EXPECT_EQ(fmt::format("{}", nonconst_formattable()), "test");
}

View File

@ -13,8 +13,8 @@ void invoke_inner(fmt::string_view format_str, Rep rep) {
#if FMT_FUZZ_FORMAT_TO_STRING #if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(format_str, value); std::string message = fmt::format(format_str, value);
#else #else
fmt::memory_buffer buf; auto buf = fmt::memory_buffer();
fmt::format_to(buf, format_str, value); fmt::format_to(std::back_inserter(buf), format_str, value);
#endif #endif
} catch (std::exception&) { } catch (std::exception&) {
} }

View File

@ -32,11 +32,10 @@ output_redirect::~output_redirect() FMT_NOEXCEPT {
} }
void output_redirect::flush() { void output_redirect::flush() {
# if EOF != -1
# error "FMT_RETRY assumes return value of -1 indicating failure"
# endif
int result = 0; int result = 0;
FMT_RETRY(result, fflush(file_)); do {
result = fflush(file_);
} while (result == EOF && errno == EINTR);
if (result != 0) throw fmt::system_error(errno, "cannot flush stream"); if (result != 0) throw fmt::system_error(errno, "cannot flush stream");
} }

View File

@ -12,7 +12,12 @@
#include <string> #include <string>
#ifdef FMT_MODULE_TEST
import fmt;
#else
#include "fmt/os.h" #include "fmt/os.h"
#endif // FMG_MODULE_TEST
#include "gmock/gmock.h" #include "gmock/gmock.h"
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \ #define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
@ -129,6 +134,7 @@ class suppress_assert {
~suppress_assert() { ~suppress_assert() {
_set_invalid_parameter_handler(original_handler_); _set_invalid_parameter_handler(original_handler_);
_CrtSetReportMode(_CRT_ASSERT, original_report_mode_); _CrtSetReportMode(_CRT_ASSERT, original_report_mode_);
(void)original_report_mode_;
} }
}; };

View File

@ -15,6 +15,8 @@
# define FMT_HIDE_MODULE_BUGS # define FMT_HIDE_MODULE_BUGS
#endif #endif
#define FMT_MODULE_TEST
#include <bit> #include <bit>
#include <chrono> #include <chrono>
#include <exception> #include <exception>
@ -35,18 +37,30 @@
# define FMT_USE_FCNTL 0 # define FMT_USE_FCNTL 0
#endif #endif
#define FMT_NOEXCEPT noexcept #define FMT_NOEXCEPT noexcept
#if defined(_WIN32) && !defined(__MINGW32__)
# define FMT_POSIX(call) _##call
#else
# define FMT_POSIX(call) call
#endif
#define FMT_OS_H_ // don't pull in os.h directly or indirectly
import fmt; import fmt;
// check for macros leaking from BMI // check for macros leaking from BMI
static bool macro_leaked = static bool macro_leaked =
#if defined(FMT_CORE_H_) || defined(FMT_FORMAT_H) #if defined(FMT_CORE_H_) || defined(FMT_FORMAT_H_)
true; true;
#else #else
false; false;
#endif #endif
#include "gtest-extra.h" // Include sources to pick up functions and classes from the module rather than
// from the non-modular library which is baked into the 'test-main' library.
// This averts linker problems:
// - strong ownership model: missing linker symbols
// - weak ownership model: duplicate linker symbols
#include "gtest-extra.cc"
#include "util.cc"
// an implicitly exported namespace must be visible [module.interface]/2.2 // an implicitly exported namespace must be visible [module.interface]/2.2
TEST(module_test, namespace) { TEST(module_test, namespace) {

View File

@ -41,9 +41,9 @@ TEST(util_test, utf16_to_utf8_empty_string) {
} }
template <typename Converter, typename Char> template <typename Converter, typename Char>
void check_utf_conversion_error( void check_utf_conversion_error(const char* message,
const char* message, fmt::basic_string_view<Char> str =
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) { fmt::basic_string_view<Char>(nullptr, 1)) {
fmt::memory_buffer out; fmt::memory_buffer out;
fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message); fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
auto error = std::system_error(std::error_code()); auto error = std::system_error(std::error_code());
@ -63,7 +63,7 @@ TEST(util_test, utf16_to_utf8_error) {
TEST(util_test, utf16_to_utf8_convert) { TEST(util_test, utf16_to_utf8_convert) {
fmt::detail::utf16_to_utf8 u; fmt::detail::utf16_to_utf8 u;
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(wstring_view(0, 1))); EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(wstring_view(nullptr, 1)));
EXPECT_EQ(ERROR_INVALID_PARAMETER, EXPECT_EQ(ERROR_INVALID_PARAMETER,
u.convert(wstring_view(L"foo", INT_MAX + 1u))); u.convert(wstring_view(L"foo", INT_MAX + 1u)));
} }
@ -81,12 +81,12 @@ TEST(os_test, format_std_error_code) {
} }
TEST(os_test, format_windows_error) { TEST(os_test, format_windows_error) {
LPWSTR message = 0; LPWSTR message = nullptr;
auto result = FormatMessageW( auto result = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_IGNORE_INSERTS,
0, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), nullptr, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0); reinterpret_cast<LPWSTR>(&message), 0, nullptr);
fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2)); fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
LocalFree(message); LocalFree(message);
fmt::memory_buffer actual_message; fmt::memory_buffer actual_message;
@ -97,17 +97,17 @@ TEST(os_test, format_windows_error) {
} }
TEST(os_test, format_long_windows_error) { TEST(os_test, format_long_windows_error) {
LPWSTR message = 0; LPWSTR message = nullptr;
// this error code is not available on all Windows platforms and // this error code is not available on all Windows platforms and
// Windows SDKs, so do not fail the test if the error string cannot // Windows SDKs, so do not fail the test if the error string cannot
// be retrieved. // be retrieved.
int provisioning_not_allowed = 0x80284013L; // TBS_E_PROVISIONING_NOT_ALLOWED int provisioning_not_allowed = 0x80284013L; // TBS_E_PROVISIONING_NOT_ALLOWED
auto result = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | auto result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_IGNORE_INSERTS,
0, static_cast<DWORD>(provisioning_not_allowed), nullptr, static_cast<DWORD>(provisioning_not_allowed),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0); reinterpret_cast<LPWSTR>(&message), 0, nullptr);
if (result == 0) { if (result == 0) {
LocalFree(message); LocalFree(message);
return; return;
@ -336,6 +336,14 @@ TEST(ostream_test, truncate) {
EXPECT_EQ("foo", read(in, 4)); EXPECT_EQ("foo", read(in, 4));
} }
TEST(ostream_test, flush) {
auto out = fmt::output_file("test-file");
out.print("x");
out.flush();
auto in = fmt::file("test-file", file::RDONLY);
EXPECT_READ(in, "x");
}
TEST(file_test, default_ctor) { TEST(file_test, default_ctor) {
file f; file f;
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());

View File

@ -115,12 +115,12 @@ TEST(ostream_test, write_to_ostream_max_size) {
struct test_buffer final : fmt::detail::buffer<char> { struct test_buffer final : fmt::detail::buffer<char> {
explicit test_buffer(size_t size) explicit test_buffer(size_t size)
: fmt::detail::buffer<char>(nullptr, size, size) {} : fmt::detail::buffer<char>(nullptr, size, size) {}
void grow(size_t) {} void grow(size_t) override {}
} buffer(max_size); } buffer(max_size);
struct mock_streambuf : std::streambuf { struct mock_streambuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n)); MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n));
std::streamsize xsputn(const char* s, std::streamsize n) { std::streamsize xsputn(const char* s, std::streamsize n) override {
const void* v = s; const void* v = s;
return xsputn(v, n); return xsputn(v, n);
} }

View File

@ -262,3 +262,16 @@ TEST(ranges_test, join_range) {
EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0"); EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0");
} }
#endif // FMT_RANGES_TEST_ENABLE_JOIN #endif // FMT_RANGES_TEST_ENABLE_JOIN
TEST(ranges_test, escape_string) {
using vec = std::vector<std::string>;
EXPECT_EQ(fmt::format("{}", vec{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]");
EXPECT_EQ(fmt::format("{}", vec{"\x07"}), "[\"\\x07\"]");
EXPECT_EQ(fmt::format("{}", vec{"\x7f"}), "[\"\\x7f\"]");
// Unassigned Unicode code points.
if (fmt::detail::is_utf8()) {
EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xbf"}), "[\"\\U0010ffff\"]");
}
}

View File

@ -35,6 +35,7 @@ int main(int argc, char** argv) {
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
try { try {
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
testing::FLAGS_gtest_death_test_style = "threadsafe";
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} catch (...) { } catch (...) {
// Catch all exceptions to make Coverity happy. // Catch all exceptions to make Coverity happy.

View File

@ -10,7 +10,11 @@
#include <locale> #include <locale>
#include <string> #include <string>
#ifdef FMT_MODULE_TEST
import fmt;
#else
#include "fmt/os.h" #include "fmt/os.h"
#endif // FMT_MODULE_TEST
#ifdef _MSC_VER #ifdef _MSC_VER
# define FMT_VSNPRINTF vsprintf_s # define FMT_VSNPRINTF vsprintf_s
@ -34,7 +38,7 @@ fmt::buffered_file open_buffered_file(FILE** fp = nullptr);
inline FILE* safe_fopen(const char* filename, const char* mode) { inline FILE* safe_fopen(const char* filename, const char* mode) {
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
// Fix MSVC warning about "unsafe" fopen. // Fix MSVC warning about "unsafe" fopen.
FILE* f = 0; FILE* f = nullptr;
errno = fopen_s(&f, filename, mode); errno = fopen_s(&f, filename, mode);
return f; return f;
#else #else

View File

@ -301,10 +301,12 @@ template <typename Char> struct small_grouping : std::numpunct<Char> {
Char do_thousands_sep() const override { return ','; } Char do_thousands_sep() const override { return ','; }
}; };
TEST(locale_test, double_decimal_point) { TEST(locale_test, localized_double) {
auto loc = std::locale(std::locale(), new numpunct<char>()); auto loc = std::locale(std::locale(), new numpunct<char>());
EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23)); EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23));
EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23)); EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23));
EXPECT_EQ("1~234?5", fmt::format(loc, "{:L}", 1234.5));
EXPECT_EQ("12~000", fmt::format(loc, "{:L}", 12000.0));
} }
TEST(locale_test, format) { TEST(locale_test, format) {

View File

@ -41,6 +41,6 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- run: cmake -DBUILD_TESTING=ON -V . - run: cmake -DBUILD_TESTING=ON .
- run: cmake --build . - run: cmake --build .
- run: ctest -V . - run: ctest -V .

View File

@ -1,3 +1,4 @@
*.a
*.la *.la
*.lo *.lo
*.o *.o
@ -25,13 +26,11 @@
/man /man
/missing /missing
/src/libmaxminddb.pc /src/libmaxminddb.pc
/src/libmaxminddb.a
/src/test-data-pool /src/test-data-pool
/t/*.log /t/*.log
/t/*.trs /t/*.trs
/t/*_t /t/*_t
/t/*-t /t/*-t
/t/libtap.a
/test-driver /test-driver
\#*\# \#*\#
aclocal.m4 aclocal.m4

View File

@ -1,78 +0,0 @@
#
# based on uncrustify config file for the linux kernel
#
code_width = 80
indent_case_brace = 4
indent_columns = 4
indent_label = 2 # pos: absolute col, neg: relative column
indent_with_tabs = 0
#
# inter-symbol newlines
#
nl_brace_else = remove # "} else" vs "} \n else" - cuddle else
nl_brace_while = remove # "} while" vs "} \n while" - cuddle while
nl_do_brace = remove # "do {" vs "do \n {"
nl_else_brace = remove # "else {" vs "else \n {"
nl_enum_brace = remove # "enum {" vs "enum \n {"
nl_fcall_brace = remove # "list_for_each() {" vs "list_for_each()\n{"
nl_fdef_brace = force # "int foo() {" vs "int foo()\n{"
nl_for_brace = remove # "for () {" vs "for () \n {"
nl_func_var_def_blk = 0 # don't add newlines after a block of var declarations
nl_if_brace = remove # "if () {" vs "if () \n {"
nl_multi_line_define = true
nl_struct_brace = remove # "struct {" vs "struct \n {"
nl_switch_brace = remove # "switch () {" vs "switch () \n {"
nl_union_brace = remove # "union {" vs "union \n {"
nl_while_brace = remove # "while () {" vs "while () \n {"
#
# Source code modifications
#
mod_full_brace_do = force # "do a--; while ();" vs "do { a--; } while ();"
mod_full_brace_for = force # "for () a--;" vs "for () { a--; }"
mod_full_brace_if = force # "if (a) a--;" vs "if (a) { a--; }"
mod_full_brace_nl = 3 # don't remove if more than 3 newlines
mod_full_brace_while = force # "while (a) a--;" vs "while (a) { a--; }"
mod_paren_on_return = remove # "return 1;" vs "return (1);"
#
# inter-character spacing options
#
sp_after_cast = remove # "(int) a" vs "(int)a"
sp_after_comma = force
sp_after_sparen = force # "if () {" vs "if (){"
sp_arith = force
sp_assign = force
sp_assign = force
sp_before_comma = remove
sp_before_ptr_star = force # "char *foo" vs "char* foo
sp_before_sparen = force # "if (" vs "if("
sp_between_ptr_star = remove # "char * *foo" vs "char **foo"
sp_bool = force
sp_compare = force
sp_func_call_paren = remove # "foo (" vs "foo("
sp_func_def_paren = remove # "int foo (){" vs "int foo(){"
sp_func_proto_paren = remove # "int foo ();" vs "int foo();"
sp_inside_braces = force # "{ 1 }" vs "{1}"
sp_inside_braces_enum = force # "{ 1 }" vs "{1}"
sp_inside_braces_struct = force # "{ 1 }" vs "{1}"
sp_inside_sparen = remove
sp_paren_brace = force
sp_sizeof_paren = remove # "sizeof (int)" vs "sizeof(int)"
#
# Aligning stuff
#
align_enum_equ_span = 4 # '=' in enum definition
align_nl_cont = true
align_on_tabstop = FALSE # align on tabstops
align_right_cmt_span = 3
align_struct_init_span = 1
align_struct_init_span = 3 # align stuff in a structure init '= { }'
align_var_def_star_style = 2 # void *foo;
align_var_struct_span = 0
align_with_tabs = FALSE # use tabs to align

View File

@ -1,12 +1,18 @@
cmake_minimum_required (VERSION 3.7) cmake_minimum_required (VERSION 3.9)
project(maxminddb project(maxminddb
LANGUAGES C LANGUAGES C
VERSION 1.5.0 VERSION 1.6.0
) )
set(MAXMINDDB_SOVERSION 0.0.7) set(MAXMINDDB_SOVERSION 0.0.7)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_EXTENSIONS OFF)
if (WIN32)
option(MSVC_STATIC_RUNTIME "When ON the library will be built by using MT/MTd run-time libraries" OFF)
endif()
option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF) option(BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" OFF)
option(BUILD_TESTING "Build test programs" OFF) option(BUILD_TESTING "Build test programs" ON)
include(CheckTypeSize) include(CheckTypeSize)
check_type_size("unsigned __int128" UINT128) check_type_size("unsigned __int128" UINT128)
@ -30,7 +36,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
endif() endif()
configure_file(${PROJECT_SOURCE_DIR}/include/maxminddb_config.h.cmake.in configure_file(${PROJECT_SOURCE_DIR}/include/maxminddb_config.h.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/include/maxminddb_config.h) ${PROJECT_SOURCE_DIR}/include/maxminddb_config.h)
add_library(maxminddb add_library(maxminddb
src/maxminddb.c src/maxminddb.c
@ -52,13 +58,27 @@ endif()
if(WIN32) if(WIN32)
target_link_libraries(maxminddb ws2_32) target_link_libraries(maxminddb ws2_32)
if(BUILD_SHARED_LIBS)
set_target_properties(maxminddb PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
if(MSVC_STATIC_RUNTIME)
# On MSVC, when MSVC_STATIC_RUNTIME is ON, MT (Release) and MTd (Debug)
# run-time libraries will be used instead of MD/MDd. The default is OFF so
# MD/MDd are used when nothing related is passed.
#
# Adapted from https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#make-override-files
set(CMAKE_USER_MAKE_RULES_OVERRIDE
${CMAKE_CURRENT_SOURCE_DIR}/c_flag_overrides.cmake)
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX
${CMAKE_CURRENT_SOURCE_DIR}/cxx_flag_overrides.cmake)
endif()
endif() endif()
set(CMAKE_SHARED_LIBRARY_PREFIX lib) target_include_directories(maxminddb PUBLIC
set(CMAKE_STATIC_LIBRARY_PREFIX lib) $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
target_include_directories(maxminddb PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) $<INSTALL_INTERFACE:include>
target_include_directories(maxminddb PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) )
set(MAXMINDB_HEADERS set(MAXMINDB_HEADERS
include/maxminddb.h include/maxminddb.h
@ -66,17 +86,14 @@ set(MAXMINDB_HEADERS
) )
set_target_properties(maxminddb PROPERTIES PUBLIC_HEADER "${MAXMINDB_HEADERS}") set_target_properties(maxminddb PROPERTIES PUBLIC_HEADER "${MAXMINDB_HEADERS}")
#install(TARGETS maxminddb install(TARGETS maxminddb
# EXPORT maxminddb EXPORT maxminddb)
# ARCHIVE DESTINATION lib
# PUBLIC_HEADER DESTINATION include/
#)
# This is required to work with FetchContent # This is required to work with FetchContent
#install(EXPORT maxminddb install(EXPORT maxminddb
# FILE maxminddb-config.cmake FILE maxminddb-config.cmake
# NAMESPACE maxminddb:: NAMESPACE maxminddb::
# DESTINATION lib/cmake/maxminddb) DESTINATION lib/cmake/maxminddb)
# We always want to build mmdblookup # We always want to build mmdblookup
add_subdirectory(bin) add_subdirectory(bin)

View File

@ -1,3 +1,44 @@
## 1.7.0
* `FD_CLOEXEC` is now set on platforms that do not support `O_CLOEXEC`.
Reported by rittneje. GitHub #273.
* When building with Visual Studio, you may now build a static runtime with
CMake by setting `MSVC_STATIC_RUNTIME` to `ON`. Pull request by Rafael
Santiago. GitHub #269.
* The CMake build now works on iOS. Pull request by SpaceIm. GitHub #271.
## 1.6.0 - 2021-04-29
* This release includes several improvements to the CMake build. In
particular:
* C99 support is now properly enabled, fixing builds on older `gcc`
versions. Pull request by Jan Včelák. GitHub #257.
* `CMAKE_SHARED_LIBRARY_PREFIX` and `CMAKE_STATIC_LIBRARY_PREFIX` are
no longer explicitly set and now use the default values for the platform.
Pull request by Jan Včelák. GitHub #258.
* `target_include_directories` now works as expected. Pull request by Jan
Včelák. GitHub #259.
* DLLs are now installed on Windows when `libmaxminddb` is built as a
shared library. Pull request by Jan Včelák. GitHub #261.
* When built as a dynamic library on Windows, all symbols are now exported.
Pull request by Jan Včelák. GitHub #262.
## 1.5.2 - 2021-02-18
* With `libmaxminddb` on Windows and `mmdblookup` generally, there were
instances where the return value of `calloc` was not checked, which could
lead to issues in low memory situations or when resource limits had been
set. Reported by cve-reporting. GitHub #252.
## 1.5.1 - 2021-02-18
* The formatting of the manpages has been improved and the script that
generates them now supports `lowdown` in addition to `pandoc`. Pull request
by Faidon Liambotis. GitHub #248.
## 1.5.0 - 2021-01-05 ## 1.5.0 - 2021-01-05
* A CMake build script has been added for Windows builds. The Visual * A CMake build script has been added for Windows builds. The Visual

View File

@ -1,4 +1,4 @@
Copyright 2013-2014 MaxMind, Inc. Copyright 2013-2021 MaxMind, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.

View File

@ -1,7 +1,6 @@
# Releasing this library # Releasing this library
We release by uploading the tarball to GitHub, uploading Ubuntu PPAs, and by We release by uploading the tarball to GitHub and uploading Ubuntu PPAs.
updating the Homebrew recipe for this library.
## Creating the release tarball ## Creating the release tarball
You may want to refer to the section about prerequisites. You may want to refer to the section about prerequisites.
@ -9,9 +8,12 @@ You may want to refer to the section about prerequisites.
* Check whether there are any open issues to fix while you're doing this. * Check whether there are any open issues to fix while you're doing this.
* Update `Changes.md` to include specify the new version, today's date, and * Update `Changes.md` to include specify the new version, today's date, and
list relevant changes. Commit this. list relevant changes. Commit this.
* Create a new branch off of the latest `main` for the release.
* Run `./dev-bin/release.sh` to update various files in the distro, our * Run `./dev-bin/release.sh` to update various files in the distro, our
GitHub pages, and creates a GitHub release with the tarball. GitHub pages, and creates a GitHub release with the tarball.
* Check the release looks good on both GitHub and launchpad.net. * Check the release looks good on both GitHub and launchpad.net.
* Make a pull request against `main` with the changes from the release
script.
## PPA ## PPA
@ -27,9 +29,16 @@ configurations different than Greg's machine.
Check whether any new Ubuntu versions need to be listed in this script Check whether any new Ubuntu versions need to be listed in this script
before running it. before running it.
You should run it from `master`. You should run it from `main`.
## Homebrew ## Homebrew (optional)
Releasing to Homebrew is no longer required as the formulas are easily
updated by the end-user using a built-in feature in the tool. These
directions remain in case there is a more significant change to the
build process that may require a non-trivial update to the formula or
in the case where we want the Homebrew version updated promptly for
some reason.
* Go to https://github.com/Homebrew/homebrew-core/edit/master/Formula/libmaxminddb.rb * Go to https://github.com/Homebrew/homebrew-core/edit/master/Formula/libmaxminddb.rb
* Edit the file to update the url and sha256. You can get the sha256 for the * Edit the file to update the url and sha256. You can get the sha256 for the

View File

@ -68,7 +68,8 @@ You can clone this repository and build it by running:
$ git clone --recursive https://github.com/maxmind/libmaxminddb $ git clone --recursive https://github.com/maxmind/libmaxminddb
After cloning, run `./bootstrap` from the `libmaxminddb` directory and then After cloning, run `./bootstrap` from the `libmaxminddb` directory and then
follow the instructions for installing from a named release tarball as described above. follow the instructions for installing from a named release tarball as
described above.
## Using CMake ## Using CMake
@ -80,6 +81,11 @@ work.
$ ctest -V . $ ctest -V .
$ cmake --build . --target install $ cmake --build . --target install
When building with Visual Studio, you may build a multithreaded (MT/MTd)
runtime library, using the `MSVC_STATIC_RUNTIME` setting:
$ cmake -DMSVC_STATIC_RUNTIME=ON -DBUILD_SHARED_LIBS=OFF ..
## On Ubuntu via PPA ## On Ubuntu via PPA
MaxMind provides a PPA for recent version of Ubuntu. To add the PPA to your MaxMind provides a PPA for recent version of Ubuntu. To add the PPA to your
@ -120,6 +126,8 @@ Use `make safedist` to check the resulting tarball.
# Copyright and License # Copyright and License
Copyright 2013-2021 MaxMind, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at

View File

@ -8,6 +8,6 @@ if(NOT WIN32)
install( install(
TARGETS mmdblookup TARGETS mmdblookup
RUNTIME DESTINATION bin DESTINATION bin
) )
endif() endif()

View File

@ -23,46 +23,42 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#define LOCAL static static void usage(char *program, int exit_code, const char *error);
static const char **get_options(int argc,
LOCAL void usage(char *program, int exit_code, const char *error); char **argv,
LOCAL const char **get_options( char **mmdb_file,
int argc, char **ip_address,
char **argv, int *verbose,
char **mmdb_file, int *iterations,
char **ip_address, int *lookup_path_length,
int *verbose, int *const thread_count,
int *iterations, char **const ip_file);
int *lookup_path_length, static MMDB_s open_or_die(const char *fname);
int *const thread_count, static void dump_meta(MMDB_s *mmdb);
char **const ip_file); static bool lookup_from_file(MMDB_s *const mmdb,
LOCAL MMDB_s open_or_die(const char *fname); char const *const ip_file,
LOCAL void dump_meta(MMDB_s *mmdb); bool const dump);
LOCAL bool lookup_from_file(MMDB_s *const mmdb, static int lookup_and_print(MMDB_s *mmdb,
char const *const ip_file, const char *ip_address,
bool const dump); const char **lookup_path,
LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address, int lookup_path_length,
const char **lookup_path, bool verbose);
int lookup_path_length, static int benchmark(MMDB_s *mmdb, int iterations);
bool verbose); static MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr);
LOCAL int benchmark(MMDB_s *mmdb, int iterations); static void random_ipv4(char *ip);
LOCAL MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr);
LOCAL void random_ipv4(char *ip);
#ifndef _WIN32 #ifndef _WIN32
// These aren't with the automatically generated prototypes as we'd lose the // These aren't with the automatically generated prototypes as we'd lose the
// enclosing macros. // enclosing macros.
static bool start_threaded_benchmark( static bool start_threaded_benchmark(MMDB_s *const mmdb,
MMDB_s *const mmdb, int const thread_count,
int const thread_count, int const iterations);
int const iterations);
static long double get_time(void); static long double get_time(void);
static void *thread(void *arg); static void *thread(void *arg);
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
int wmain(int argc, wchar_t **wargv) int wmain(int argc, wchar_t **wargv) {
{
// Convert our argument list from UTF-16 to UTF-8. // Convert our argument list from UTF-16 to UTF-8.
char **argv = (char **)calloc(argc, sizeof(char *)); char **argv = (char **)calloc(argc, sizeof(char *));
if (!argv) { if (!argv) {
@ -72,11 +68,11 @@ int wmain(int argc, wchar_t **wargv)
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
int utf8_width; int utf8_width;
char *utf8_string; char *utf8_string;
utf8_width = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, utf8_width =
NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, NULL);
if (utf8_width < 1) { if (utf8_width < 1) {
fprintf(stderr, "WideCharToMultiByte() failed: %d\n", fprintf(
GetLastError()); stderr, "WideCharToMultiByte() failed: %d\n", GetLastError());
exit(1); exit(1);
} }
utf8_string = calloc(utf8_width, sizeof(char)); utf8_string = calloc(utf8_width, sizeof(char));
@ -84,17 +80,17 @@ int wmain(int argc, wchar_t **wargv)
fprintf(stderr, "calloc(): %s\n", strerror(errno)); fprintf(stderr, "calloc(): %s\n", strerror(errno));
exit(1); exit(1);
} }
if (WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, utf8_string, if (WideCharToMultiByte(
utf8_width, NULL, NULL) < 1) { CP_UTF8, 0, wargv[i], -1, utf8_string, utf8_width, NULL, NULL) <
fprintf(stderr, "WideCharToMultiByte() failed: %d\n", 1) {
GetLastError()); fprintf(
stderr, "WideCharToMultiByte() failed: %d\n", GetLastError());
exit(1); exit(1);
} }
argv[i] = utf8_string; argv[i] = utf8_string;
} }
#else // _WIN32 #else // _WIN32
int main(int argc, char **argv) int main(int argc, char **argv) {
{
#endif // _WIN32 #endif // _WIN32
char *mmdb_file = NULL; char *mmdb_file = NULL;
char *ip_address = NULL; char *ip_address = NULL;
@ -104,9 +100,15 @@ int main(int argc, char **argv)
int thread_count = 0; int thread_count = 0;
char *ip_file = NULL; char *ip_file = NULL;
const char **lookup_path = const char **lookup_path = get_options(argc,
get_options(argc, argv, &mmdb_file, &ip_address, &verbose, &iterations, argv,
&lookup_path_length, &thread_count, &ip_file); &mmdb_file,
&ip_address,
&verbose,
&iterations,
&lookup_path_length,
&thread_count,
&ip_file);
MMDB_s mmdb = open_or_die(mmdb_file); MMDB_s mmdb = open_or_die(mmdb_file);
@ -131,13 +133,13 @@ int main(int argc, char **argv)
} }
if (0 == iterations) { if (0 == iterations) {
exit(lookup_and_print(&mmdb, ip_address, lookup_path, exit(lookup_and_print(
lookup_path_length, verbose)); &mmdb, ip_address, lookup_path, lookup_path_length, verbose));
} }
free((void *)lookup_path); free((void *)lookup_path);
srand( (int)time(NULL) ); srand((int)time(NULL));
#ifndef _WIN32 #ifndef _WIN32
if (thread_count > 0) { if (thread_count > 0) {
@ -153,97 +155,96 @@ int main(int argc, char **argv)
exit(benchmark(&mmdb, iterations)); exit(benchmark(&mmdb, iterations));
} }
LOCAL void usage(char *program, int exit_code, const char *error) static void usage(char *program, int exit_code, const char *error) {
{
if (NULL != error) { if (NULL != error) {
fprintf(stderr, "\n *ERROR: %s\n", error); fprintf(stderr, "\n *ERROR: %s\n", error);
} }
char *usage = "\n" char *usage =
" %s --file /path/to/file.mmdb --ip 1.2.3.4 [path to lookup]\n" "\n"
"\n" " %s --file /path/to/file.mmdb --ip 1.2.3.4 [path to lookup]\n"
" This application accepts the following options:\n" "\n"
"\n" " This application accepts the following options:\n"
" --file (-f) The path to the MMDB file. Required.\n" "\n"
"\n" " --file (-f) The path to the MMDB file. Required.\n"
" --ip (-i) The IP address to look up. Required.\n" "\n"
"\n" " --ip (-i) The IP address to look up. Required.\n"
" --verbose (-v) Turns on verbose output. Specifically, this causes this\n" "\n"
" application to output the database metadata.\n" " --verbose (-v) Turns on verbose output. Specifically, this "
"\n" "causes this\n"
" --version Print the program's version number and exit.\n" " application to output the database metadata.\n"
"\n" "\n"
" --help (-h -?) Show usage information.\n" " --version Print the program's version number and exit.\n"
"\n" "\n"
" If an IP's data entry resolves to a map or array, you can provide\n" " --help (-h -?) Show usage information.\n"
" a lookup path to only show part of that data.\n" "\n"
"\n" " If an IP's data entry resolves to a map or array, you can provide\n"
" For example, given a JSON structure like this:\n" " a lookup path to only show part of that data.\n"
"\n" "\n"
" {\n" " For example, given a JSON structure like this:\n"
" \"names\": {\n" "\n"
" \"en\": \"Germany\",\n" " {\n"
" \"de\": \"Deutschland\"\n" " \"names\": {\n"
" },\n" " \"en\": \"Germany\",\n"
" \"cities\": [ \"Berlin\", \"Frankfurt\" ]\n" " \"de\": \"Deutschland\"\n"
" }\n" " },\n"
"\n" " \"cities\": [ \"Berlin\", \"Frankfurt\" ]\n"
" You could look up just the English name by calling mmdblookup with a lookup path of:\n" " }\n"
"\n" "\n"
" mmdblookup --file ... --ip ... names en\n" " You could look up just the English name by calling mmdblookup with "
"\n" "a lookup path of:\n"
" Or you could look up the second city in the list with:\n" "\n"
"\n" " mmdblookup --file ... --ip ... names en\n"
" mmdblookup --file ... --ip ... cities 1\n" "\n"
"\n" " Or you could look up the second city in the list with:\n"
" Array numbering begins with zero (0).\n" "\n"
"\n" " mmdblookup --file ... --ip ... cities 1\n"
" If you do not provide a path to lookup, all of the information for a given IP\n" "\n"
" will be shown.\n" " Array numbering begins with zero (0).\n"
"\n"; "\n"
" If you do not provide a path to lookup, all of the information for "
"a given IP\n"
" will be shown.\n"
"\n";
fprintf(stdout, usage, program); fprintf(stdout, usage, program);
exit(exit_code); exit(exit_code);
} }
LOCAL const char **get_options( static const char **get_options(int argc,
int argc, char **argv,
char **argv, char **mmdb_file,
char **mmdb_file, char **ip_address,
char **ip_address, int *verbose,
int *verbose, int *iterations,
int *iterations, int *lookup_path_length,
int *lookup_path_length, int *const thread_count,
int *const thread_count, char **const ip_file) {
char **const ip_file)
{
static int help = 0; static int help = 0;
static int version = 0; static int version = 0;
while (1) { while (1) {
static struct option options[] = { static struct option options[] = {
{ "file", required_argument, 0, 'f' }, {"file", required_argument, 0, 'f'},
{ "ip", required_argument, 0, 'i' }, {"ip", required_argument, 0, 'i'},
{ "verbose", no_argument, 0, 'v' }, {"verbose", no_argument, 0, 'v'},
{ "version", no_argument, 0, 'n' }, {"version", no_argument, 0, 'n'},
{ "benchmark", required_argument, 0, 'b' }, {"benchmark", required_argument, 0, 'b'},
#ifndef _WIN32 #ifndef _WIN32
{ "threads", required_argument, 0, 't' }, {"threads", required_argument, 0, 't'},
#endif #endif
{ "ip-file", required_argument, 0, 'I' }, {"ip-file", required_argument, 0, 'I'},
{ "help", no_argument, 0, 'h' }, {"help", no_argument, 0, 'h'},
{ "?", no_argument, 0, 1 }, {"?", no_argument, 0, 1},
{ 0, 0, 0, 0 } {0, 0, 0, 0}};
};
int opt_index; int opt_index;
#ifdef _WIN32 #ifdef _WIN32
char const * const optstring = "f:i:b:I:vnh?"; char const *const optstring = "f:i:b:I:vnh?";
#else #else
char const * const optstring = "f:i:b:t:I:vnh?"; char const *const optstring = "f:i:b:t:I:vnh?";
#endif #endif
int opt_char = getopt_long(argc, argv, optstring, options, int opt_char = getopt_long(argc, argv, optstring, options, &opt_index);
&opt_index);
if (-1 == opt_char) { if (-1 == opt_char) {
break; break;
@ -295,6 +296,10 @@ LOCAL const char **get_options(
const char **lookup_path = const char **lookup_path =
calloc((argc - optind) + 1, sizeof(const char *)); calloc((argc - optind) + 1, sizeof(const char *));
if (!lookup_path) {
fprintf(stderr, "calloc(): %s\n", strerror(errno));
exit(1);
}
int i; int i;
for (i = 0; i < argc - optind; i++) { for (i = 0; i < argc - optind; i++) {
lookup_path[i] = argv[i + optind]; lookup_path[i] = argv[i + optind];
@ -305,14 +310,13 @@ LOCAL const char **get_options(
return lookup_path; return lookup_path;
} }
LOCAL MMDB_s open_or_die(const char *fname) static MMDB_s open_or_die(const char *fname) {
{
MMDB_s mmdb; MMDB_s mmdb;
int status = MMDB_open(fname, MMDB_MODE_MMAP, &mmdb); int status = MMDB_open(fname, MMDB_MODE_MMAP, &mmdb);
if (MMDB_SUCCESS != status) { if (MMDB_SUCCESS != status) {
fprintf(stderr, "\n Can't open %s - %s\n", fname, fprintf(
MMDB_strerror(status)); stderr, "\n Can't open %s - %s\n", fname, MMDB_strerror(status));
if (MMDB_IO_ERROR == status) { if (MMDB_IO_ERROR == status) {
fprintf(stderr, " IO error: %s\n", strerror(errno)); fprintf(stderr, " IO error: %s\n", strerror(errno));
@ -326,8 +330,7 @@ LOCAL MMDB_s open_or_die(const char *fname)
return mmdb; return mmdb;
} }
LOCAL void dump_meta(MMDB_s *mmdb) static void dump_meta(MMDB_s *mmdb) {
{
const char *meta_dump = "\n" const char *meta_dump = "\n"
" Database metadata\n" " Database metadata\n"
" Node count: %i\n" " Node count: %i\n"
@ -342,7 +345,8 @@ LOCAL void dump_meta(MMDB_s *mmdb)
const time_t epoch = (const time_t)mmdb->metadata.build_epoch; const time_t epoch = (const time_t)mmdb->metadata.build_epoch;
strftime(date, 40, "%F %T UTC", gmtime(&epoch)); strftime(date, 40, "%F %T UTC", gmtime(&epoch));
fprintf(stdout, meta_dump, fprintf(stdout,
meta_dump,
mmdb->metadata.node_count, mmdb->metadata.node_count,
mmdb->metadata.record_size, mmdb->metadata.record_size,
mmdb->metadata.ip_version, mmdb->metadata.ip_version,
@ -362,7 +366,8 @@ LOCAL void dump_meta(MMDB_s *mmdb)
fprintf(stdout, " Description:\n"); fprintf(stdout, " Description:\n");
for (size_t i = 0; i < mmdb->metadata.description.count; i++) { for (size_t i = 0; i < mmdb->metadata.description.count; i++) {
fprintf(stdout, " %s: %s\n", fprintf(stdout,
" %s: %s\n",
mmdb->metadata.description.descriptions[i]->language, mmdb->metadata.description.descriptions[i]->language,
mmdb->metadata.description.descriptions[i]->description); mmdb->metadata.description.descriptions[i]->description);
} }
@ -381,10 +386,9 @@ LOCAL void dump_meta(MMDB_s *mmdb)
// //
// In addition to being useful for comparisons, this function provides a way to // In addition to being useful for comparisons, this function provides a way to
// have a more deterministic set of lookups for benchmarking. // have a more deterministic set of lookups for benchmarking.
LOCAL bool lookup_from_file(MMDB_s *const mmdb, static bool lookup_from_file(MMDB_s *const mmdb,
char const *const ip_file, char const *const ip_file,
bool const dump) bool const dump) {
{
FILE *const fh = fopen(ip_file, "r"); FILE *const fh = fopen(ip_file, "r");
if (!fh) { if (!fh) {
fprintf(stderr, "fopen(): %s: %s\n", ip_file, strerror(errno)); fprintf(stderr, "fopen(): %s: %s\n", ip_file, strerror(errno));
@ -392,7 +396,7 @@ LOCAL bool lookup_from_file(MMDB_s *const mmdb,
} }
clock_t const clock_start = clock(); clock_t const clock_start = clock();
char buf[1024] = { 0 }; char buf[1024] = {0};
// I'd normally use uint64_t, but support for it is optional in C99. // I'd normally use uint64_t, but support for it is optional in C99.
unsigned long long i = 0; unsigned long long i = 0;
while (1) { while (1) {
@ -429,10 +433,11 @@ LOCAL bool lookup_from_file(MMDB_s *const mmdb,
} }
MMDB_entry_data_list_s *entry_data_list = NULL; MMDB_entry_data_list_s *entry_data_list = NULL;
int const status = MMDB_get_entry_data_list(&result.entry, int const status =
&entry_data_list); MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (status != MMDB_SUCCESS) { if (status != MMDB_SUCCESS) {
fprintf(stderr, "MMDB_get_entry_data_list(): %s\n", fprintf(stderr,
"MMDB_get_entry_data_list(): %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
fclose(fh); fclose(fh);
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
@ -447,10 +452,11 @@ LOCAL bool lookup_from_file(MMDB_s *const mmdb,
if (dump) { if (dump) {
fprintf(stdout, "%s:\n", buf); fprintf(stdout, "%s:\n", buf);
int const status = MMDB_dump_entry_data_list(stderr, int const status =
entry_data_list, 0); MMDB_dump_entry_data_list(stderr, entry_data_list, 0);
if (status != MMDB_SUCCESS) { if (status != MMDB_SUCCESS) {
fprintf(stderr, "MMDB_dump_entry_data_list(): %s\n", fprintf(stderr,
"MMDB_dump_entry_data_list(): %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
fclose(fh); fclose(fh);
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
@ -467,16 +473,18 @@ LOCAL bool lookup_from_file(MMDB_s *const mmdb,
fprintf( fprintf(
stdout, stdout,
"Looked up %llu addresses in %.2f seconds. %.2f lookups per second.\n", "Looked up %llu addresses in %.2f seconds. %.2f lookups per second.\n",
i, seconds, i / seconds); i,
seconds,
i / seconds);
return true; return true;
} }
LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address, static int lookup_and_print(MMDB_s *mmdb,
const char **lookup_path, const char *ip_address,
int lookup_path_length, const char **lookup_path,
bool verbose) int lookup_path_length,
{ bool verbose) {
MMDB_lookup_result_s result = lookup_or_die(mmdb, ip_address); MMDB_lookup_result_s result = lookup_or_die(mmdb, ip_address);
MMDB_entry_data_list_s *entry_data_list = NULL; MMDB_entry_data_list_s *entry_data_list = NULL;
@ -484,38 +492,32 @@ LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address,
int exit_code = 0; int exit_code = 0;
if (verbose) { if (verbose) {
fprintf( fprintf(stdout, "\n Record prefix length: %d\n", result.netmask);
stdout,
"\n Record prefix length: %d\n",
result.netmask
);
} }
if (result.found_entry) { if (result.found_entry) {
int status; int status;
if (lookup_path_length) { if (lookup_path_length) {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
status = MMDB_aget_value(&result.entry, &entry_data, status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
lookup_path);
if (MMDB_SUCCESS == status) { if (MMDB_SUCCESS == status) {
if (entry_data.offset) { if (entry_data.offset) {
MMDB_entry_s entry = MMDB_entry_s entry = {.mmdb = mmdb,
{ .mmdb = mmdb, .offset = entry_data.offset }; .offset = entry_data.offset};
status = MMDB_get_entry_data_list(&entry, status = MMDB_get_entry_data_list(&entry, &entry_data_list);
&entry_data_list);
} else { } else {
fprintf( fprintf(stdout,
stdout, "\n No data was found at the lookup path you "
"\n No data was found at the lookup path you provided\n\n"); "provided\n\n");
} }
} }
} else { } else {
status = MMDB_get_entry_data_list(&result.entry, status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
&entry_data_list);
} }
if (MMDB_SUCCESS != status) { if (MMDB_SUCCESS != status) {
fprintf(stderr, "Got an error looking up the entry data - %s\n", fprintf(stderr,
"Got an error looking up the entry data - %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
exit_code = 5; exit_code = 5;
goto end; goto end;
@ -533,7 +535,7 @@ LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address,
exit_code = 6; exit_code = 6;
} }
end: end:
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
MMDB_close(mmdb); MMDB_close(mmdb);
free((void *)lookup_path); free((void *)lookup_path);
@ -541,8 +543,7 @@ LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address,
return exit_code; return exit_code;
} }
LOCAL int benchmark(MMDB_s *mmdb, int iterations) static int benchmark(MMDB_s *mmdb, int iterations) {
{
char ip_address[16]; char ip_address[16];
int exit_code = 0; int exit_code = 0;
@ -556,11 +557,12 @@ LOCAL int benchmark(MMDB_s *mmdb, int iterations)
if (result.found_entry) { if (result.found_entry) {
int status = MMDB_get_entry_data_list(&result.entry, int status =
&entry_data_list); MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (MMDB_SUCCESS != status) { if (MMDB_SUCCESS != status) {
fprintf(stderr, "Got an error looking up the entry data - %s\n", fprintf(stderr,
"Got an error looking up the entry data - %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
exit_code = 5; exit_code = 5;
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
@ -573,19 +575,20 @@ LOCAL int benchmark(MMDB_s *mmdb, int iterations)
time = clock() - time; time = clock() - time;
double seconds = ((double)time / CLOCKS_PER_SEC); double seconds = ((double)time / CLOCKS_PER_SEC);
fprintf( fprintf(stdout,
stdout, "\n Looked up %i addresses in %.2f seconds. %.2f lookups per "
"\n Looked up %i addresses in %.2f seconds. %.2f lookups per second.\n\n", "second.\n\n",
iterations, seconds, iterations / seconds); iterations,
seconds,
iterations / seconds);
end: end:
MMDB_close(mmdb); MMDB_close(mmdb);
return exit_code; return exit_code;
} }
LOCAL MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr) static MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr) {
{
int gai_error, mmdb_error; int gai_error, mmdb_error;
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
MMDB_lookup_string(mmdb, ipstr, &gai_error, &mmdb_error); MMDB_lookup_string(mmdb, ipstr, &gai_error, &mmdb_error);
@ -599,12 +602,13 @@ LOCAL MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr)
#else #else
gai_strerror(gai_error) gai_strerror(gai_error)
#endif #endif
); );
exit(3); exit(3);
} }
if (MMDB_SUCCESS != mmdb_error) { if (MMDB_SUCCESS != mmdb_error) {
fprintf(stderr, "\n Got an error from the maxminddb library: %s\n\n", fprintf(stderr,
"\n Got an error from the maxminddb library: %s\n\n",
MMDB_strerror(mmdb_error)); MMDB_strerror(mmdb_error));
exit(4); exit(4);
} }
@ -612,15 +616,19 @@ LOCAL MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr)
return result; return result;
} }
LOCAL void random_ipv4(char *ip) static void random_ipv4(char *ip) {
{
// rand() is perfectly fine for this use case // rand() is perfectly fine for this use case
// coverity[dont_call] // coverity[dont_call]
int ip_int = rand(); int ip_int = rand();
uint8_t *bytes = (uint8_t *)&ip_int; uint8_t *bytes = (uint8_t *)&ip_int;
snprintf(ip, 16, "%u.%u.%u.%u", snprintf(ip,
*bytes, *(bytes + 1), *(bytes + 2), *(bytes + 3)); 16,
"%u.%u.%u.%u",
*bytes,
*(bytes + 1),
*(bytes + 2),
*(bytes + 3));
} }
#ifndef _WIN32 #ifndef _WIN32
@ -631,13 +639,11 @@ struct thread_info {
int iterations; int iterations;
}; };
static bool start_threaded_benchmark( static bool start_threaded_benchmark(MMDB_s *const mmdb,
MMDB_s *const mmdb, int const thread_count,
int const thread_count, int const iterations) {
int const iterations) struct thread_info *const tinfo =
{ calloc(thread_count, sizeof(struct thread_info));
struct thread_info *const tinfo = calloc(thread_count,
sizeof(struct thread_info));
if (!tinfo) { if (!tinfo) {
fprintf(stderr, "calloc(): %s\n", strerror(errno)); fprintf(stderr, "calloc(): %s\n", strerror(errno));
return false; return false;
@ -685,20 +691,22 @@ static bool start_threaded_benchmark(
rate = total_ips / elapsed; rate = total_ips / elapsed;
} }
fprintf( fprintf(stdout,
stdout, "Looked up %llu addresses using %d threads in %.2Lf seconds. %.2Lf "
"Looked up %llu addresses using %d threads in %.2Lf seconds. %.2Lf lookups per second.\n", "lookups per second.\n",
total_ips, thread_count, elapsed, rate); total_ips,
thread_count,
elapsed,
rate);
return true; return true;
} }
static long double get_time(void) static long double get_time(void) {
{
// clock_gettime() is not present on OSX until 10.12. // clock_gettime() is not present on OSX until 10.12.
#ifdef HAVE_CLOCK_GETTIME #ifdef HAVE_CLOCK_GETTIME
struct timespec tp = { struct timespec tp = {
.tv_sec = 0, .tv_sec = 0,
.tv_nsec = 0, .tv_nsec = 0,
}; };
clockid_t clk_id = CLOCK_REALTIME; clockid_t clk_id = CLOCK_REALTIME;
@ -720,15 +728,14 @@ static long double get_time(void)
#endif #endif
} }
static void *thread(void *arg) static void *thread(void *arg) {
{
const struct thread_info *const tinfo = arg; const struct thread_info *const tinfo = arg;
if (!tinfo) { if (!tinfo) {
fprintf(stderr, "thread(): %s\n", strerror(EINVAL)); fprintf(stderr, "thread(): %s\n", strerror(EINVAL));
return NULL; return NULL;
} }
char ip_address[16] = { 0 }; char ip_address[16] = {0};
for (int i = 0; i < tinfo->iterations; i++) { for (int i = 0; i < tinfo->iterations; i++) {
memset(ip_address, 0, 16); memset(ip_address, 0, 16);
@ -740,10 +747,11 @@ static void *thread(void *arg)
} }
MMDB_entry_data_list_s *entry_data_list = NULL; MMDB_entry_data_list_s *entry_data_list = NULL;
int const status = MMDB_get_entry_data_list(&result.entry, int const status =
&entry_data_list); MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (status != MMDB_SUCCESS) { if (status != MMDB_SUCCESS) {
fprintf(stderr, "MMDB_get_entry_data_list(): %s\n", fprintf(stderr,
"MMDB_get_entry_data_list(): %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
return NULL; return NULL;

View File

@ -0,0 +1,7 @@
if(MSVC)
set(CMAKE_C_FLAGS_DEBUG_INIT "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1")
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/MT /O1 /Ob1 /D NDEBUG")
set(CMAKE_C_FLAGS_RELEASE_INIT "/MT /O2 /Ob2 /D NDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/MT /Zi /O2 /Ob1 /D NDEBUG")
endif()

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ([2.63]) AC_PREREQ([2.63])
AC_INIT([libmaxminddb], [1.5.0], [support@maxmind.com]) AC_INIT([libmaxminddb], [1.6.0], [support@maxmind.com])
AC_CONFIG_SRCDIR([include/maxminddb.h]) AC_CONFIG_SRCDIR([include/maxminddb.h])
AC_CONFIG_HEADERS([config.h include/maxminddb_config.h]) AC_CONFIG_HEADERS([config.h include/maxminddb_config.h])

View File

@ -0,0 +1,6 @@
if(MSVC)
set(CMAKE_CXX_FLAGS_DEBUG_INIT "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1")
set(CMAKE_CXX_FLAGS_MINSIZEREL_INIT "/MT /O1 /Ob1 /D NDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "/MT /O2 /Ob2 /D NDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "/MT /Zi /O2 /Ob1 /D NDEBUG")
endif()

View File

@ -0,0 +1,15 @@
#!/bin/sh
format="clang-format -i -style=file"
for dir in bin include src t; do
c_files=`find $dir -maxdepth 1 -name '*.c'`
if [ "$c_files" != "" ]; then
$format $dir/*.c;
fi
h_files=`find $dir -maxdepth 1 -name '*.h'`
if [ "$h_files" != "" ]; then
$format $dir/*.h;
fi
done

View File

@ -7,53 +7,75 @@ use autodie qw( :all );
use FindBin qw( $Bin ); use FindBin qw( $Bin );
use File::Path qw( mkpath ); use File::Path qw( mkpath );
use File::Slurp qw( edit_file read_file write_file ); use File::Slurp qw( edit_file read_file );
use File::Temp qw( tempdir );
use File::Which qw( which ); use File::Which qw( which );
sub main { sub main {
my $target = shift || "$Bin/.."; my $target = shift || "$Bin/..";
my $pandoc = which('pandoc') my @translators = qw ( lowdown pandoc );
or die my $translator;
"\n You must install pandoc in order to generate the man pages.\n\n"; foreach my $p (@translators) {
if ( defined which($p) ) {
$translator = $p;
last;
}
}
unless ( defined $translator ) {
die "\n You must install one of "
. join( ', ', @translators )
. " in order to generate the man pages.\n\n";
}
_make_man( $target, 'libmaxminddb', 3 ); _make_man( $translator, $target, 'libmaxminddb', 3 );
_make_lib_man_links($target); _make_lib_man_links($target);
_make_man( $target, 'mmdblookup', 1 ); _make_man( $translator, $target, 'mmdblookup', 1 );
} }
sub _make_man { sub _make_man {
my $target = shift; my $translator = shift;
my $name = shift; my $target = shift;
my $section = shift; my $name = shift;
my $section = shift;
my $input = "$Bin/../doc/$name.md";
my $man_dir = "$target/man/man$section"; my $man_dir = "$target/man/man$section";
mkpath($man_dir); mkpath($man_dir);
my $output = "$man_dir/$name.$section";
my $tempdir = tempdir( CLEANUP => 1 ); if ( $translator eq 'pandoc' ) {
system(
my $markdown = <<"EOF"; 'pandoc',
% $name($section) '-s',
'-f', 'markdown_mmd+backtick_code_blocks',
EOF '-t', 'man',
$markdown .= read_file("$Bin/../doc/$name.md"); '-M', "title:$name",
'-M', "section:$section",
my $tempfile = "$tempdir/$name.$section.md"; $input,
write_file( $tempfile, $markdown ); '-o', $output,
);
my $man_file = "$man_dir/$name.$section"; _pandoc_postprocess($output);
system( qw( pandoc -s -t man ), $tempfile, '-o', $man_file ); }
elsif ( $translator eq 'lowdown' ) {
_fix_indentation($man_file); system(
'lowdown',
'-s',
'--out-no-smarty',
'-Tman',
'-M', "title:$name",
'-M', "section:$section",
$input,
'-o', $output,
);
}
} }
sub _make_lib_man_links { sub _make_lib_man_links {
my $target = shift; my $target = shift;
my $header = read_file("$Bin/../include/maxminddb.h"); my $header = read_file("$Bin/../include/maxminddb.h");
for my $proto ( $header =~ /^ *extern.+?(\w+)\(/gsm ) { for my $proto ( $header =~ /^ *extern.+?(MMDB_\w+)\(/gsm ) {
open my $fh, '>', "$target/man/man3/$proto.3"; open my $fh, '>', "$target/man/man3/$proto.3";
print {$fh} ".so man3/libmaxminddb.3\n"; print {$fh} ".so man3/libmaxminddb.3\n";
close $fh; close $fh;
@ -62,12 +84,13 @@ sub _make_lib_man_links {
# AFAICT there's no way to control the indentation depth for code blocks with # AFAICT there's no way to control the indentation depth for code blocks with
# Pandoc. # Pandoc.
sub _fix_indentation { sub _pandoc_postprocess {
my $file = shift; my $file = shift;
edit_file( edit_file(
sub { sub {
s/^\.IP\n\.nf/.IP "" 4\n.nf/gm; s/^\.IP\n\.nf/.IP "" 4\n.nf/gm;
s/(Automatically generated by Pandoc)(.+)$/$1/m;
}, },
$file $file
); );

View File

@ -4,7 +4,7 @@ set -e
set -x set -x
set -u set -u
DISTS=( artful zesty xenial trusty precise ) DISTS=( groovy focal bionic xenial trusty )
VERSION=$(perl -MFile::Slurp::Tiny=read_file -MDateTime <<EOF VERSION=$(perl -MFile::Slurp::Tiny=read_file -MDateTime <<EOF
use v5.16; use v5.16;

View File

@ -1,19 +0,0 @@
#!/bin/sh
uncrustify="uncrustify -c .uncrustify.cfg --replace --no-backup"
# We indent each thing twice because uncrustify is not idempotent - in some
# cases it will flip-flop between two indentation styles.
for dir in bin include src t; do
c_files=`find $dir -maxdepth 1 -name '*.c'`
if [ "$c_files" != "" ]; then
$uncrustify $dir/*.c;
$uncrustify $dir/*.c;
fi
h_files=`find $dir -maxdepth 1 -name '*.h'`
if [ "$h_files" != "" ]; then
$uncrustify $dir/*.h;
$uncrustify $dir/*.h;
fi
done

View File

@ -28,7 +28,7 @@ extern "C" {
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
/* libmaxminddb package version from configure */ /* libmaxminddb package version from configure */
#define PACKAGE_VERSION "1.5.0" #define PACKAGE_VERSION "1.6.0"
typedef ADDRESS_FAMILY sa_family_t; typedef ADDRESS_FAMILY sa_family_t;
@ -87,7 +87,7 @@ typedef ADDRESS_FAMILY sa_family_t;
#if !(MMDB_UINT128_IS_BYTE_ARRAY) #if !(MMDB_UINT128_IS_BYTE_ARRAY)
#if MMDB_UINT128_USING_MODE #if MMDB_UINT128_USING_MODE
typedef unsigned int mmdb_uint128_t __attribute__ ((__mode__(TI))); typedef unsigned int mmdb_uint128_t __attribute__((__mode__(TI)));
#else #else
typedef unsigned __int128 mmdb_uint128_t; typedef unsigned __int128 mmdb_uint128_t;
#endif #endif
@ -139,7 +139,8 @@ typedef struct MMDB_entry_data_s {
uint32_t type; uint32_t type;
} MMDB_entry_data_s; } MMDB_entry_data_s;
/* This is the return type when someone asks for all the entry data in a map or array */ /* This is the return type when someone asks for all the entry data in a map or
* array */
typedef struct MMDB_entry_data_list_s { typedef struct MMDB_entry_data_list_s {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
struct MMDB_entry_data_list_s *next; struct MMDB_entry_data_list_s *next;
@ -213,16 +214,16 @@ typedef struct MMDB_search_node_s {
MMDB_entry_s right_record_entry; MMDB_entry_s right_record_entry;
} MMDB_search_node_s; } MMDB_search_node_s;
extern int MMDB_open(const char *const filename, uint32_t flags, extern int
MMDB_s *const mmdb); MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb);
extern MMDB_lookup_result_s MMDB_lookup_string(const MMDB_s *const mmdb, extern MMDB_lookup_result_s MMDB_lookup_string(const MMDB_s *const mmdb,
const char *const ipstr, const char *const ipstr,
int *const gai_error, int *const gai_error,
int *const mmdb_error); int *const mmdb_error);
extern MMDB_lookup_result_s MMDB_lookup_sockaddr( extern MMDB_lookup_result_s
const MMDB_s *const mmdb, MMDB_lookup_sockaddr(const MMDB_s *const mmdb,
const struct sockaddr *const sockaddr, const struct sockaddr *const sockaddr,
int *const mmdb_error); int *const mmdb_error);
extern int MMDB_read_node(const MMDB_s *const mmdb, extern int MMDB_read_node(const MMDB_s *const mmdb,
uint32_t node_number, uint32_t node_number,
MMDB_search_node_s *const node); MMDB_search_node_s *const node);
@ -237,18 +238,20 @@ extern int MMDB_aget_value(MMDB_entry_s *const start,
const char *const *const path); const char *const *const path);
extern int MMDB_get_metadata_as_entry_data_list( extern int MMDB_get_metadata_as_entry_data_list(
const MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list); const MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list);
extern int MMDB_get_entry_data_list( extern int
MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list); MMDB_get_entry_data_list(MMDB_entry_s *start,
extern void MMDB_free_entry_data_list( MMDB_entry_data_list_s **const entry_data_list);
MMDB_entry_data_list_s *const entry_data_list); extern void
MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list);
extern void MMDB_close(MMDB_s *const mmdb); extern void MMDB_close(MMDB_s *const mmdb);
extern const char *MMDB_lib_version(void); extern const char *MMDB_lib_version(void);
extern int MMDB_dump_entry_data_list(FILE *const stream, extern int
MMDB_entry_data_list_s *const entry_data_list, MMDB_dump_entry_data_list(FILE *const stream,
int indent); MMDB_entry_data_list_s *const entry_data_list,
int indent);
extern const char *MMDB_strerror(int error_code); extern const char *MMDB_strerror(int error_code);
#endif /* MAXMINDDB_H */ #endif /* MAXMINDDB_H */
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -9,8 +9,7 @@ static bool can_multiply(size_t const, size_t const, size_t const);
// Allocate an MMDB_data_pool_s. It initially has space for size // Allocate an MMDB_data_pool_s. It initially has space for size
// MMDB_entry_data_list_s structs. // MMDB_entry_data_list_s structs.
MMDB_data_pool_s *data_pool_new(size_t const size) MMDB_data_pool_s *data_pool_new(size_t const size) {
{
MMDB_data_pool_s *const pool = calloc(1, sizeof(MMDB_data_pool_s)); MMDB_data_pool_s *const pool = calloc(1, sizeof(MMDB_data_pool_s));
if (!pool) { if (!pool) {
return NULL; return NULL;
@ -40,8 +39,7 @@ MMDB_data_pool_s *data_pool_new(size_t const size)
// the given max. max will typically be SIZE_MAX. // the given max. max will typically be SIZE_MAX.
// //
// We want to know if we'll wrap around. // We want to know if we'll wrap around.
static bool can_multiply(size_t const max, size_t const m, size_t const n) static bool can_multiply(size_t const max, size_t const m, size_t const n) {
{
if (m == 0) { if (m == 0) {
return false; return false;
} }
@ -50,8 +48,7 @@ static bool can_multiply(size_t const max, size_t const m, size_t const n)
} }
// Clean up the data pool. // Clean up the data pool.
void data_pool_destroy(MMDB_data_pool_s *const pool) void data_pool_destroy(MMDB_data_pool_s *const pool) {
{
if (!pool) { if (!pool) {
return; return;
} }
@ -65,8 +62,7 @@ void data_pool_destroy(MMDB_data_pool_s *const pool)
// Claim a new struct from the pool. Doing this may cause the pool's size to // Claim a new struct from the pool. Doing this may cause the pool's size to
// grow. // grow.
MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const pool) MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const pool) {
{
if (!pool) { if (!pool) {
return NULL; return NULL;
} }
@ -115,8 +111,7 @@ MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const pool)
// Turn the structs in the array-like pool into a linked list. // Turn the structs in the array-like pool into a linked list.
// //
// Before calling this function, the list isn't linked up. // Before calling this function, the list isn't linked up.
MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const pool) MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const pool) {
{
if (!pool) { if (!pool) {
return NULL; return NULL;
} }
@ -154,22 +149,16 @@ MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const pool)
static void test_can_multiply(void); static void test_can_multiply(void);
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
test_can_multiply(); test_can_multiply();
done_testing(); done_testing();
} }
static void test_can_multiply(void) static void test_can_multiply(void) {
{ { ok(can_multiply(SIZE_MAX, 1, SIZE_MAX), "1*SIZE_MAX is ok"); }
{
ok(can_multiply(SIZE_MAX, 1, SIZE_MAX), "1*SIZE_MAX is ok");
}
{ { ok(!can_multiply(SIZE_MAX, 2, SIZE_MAX), "2*SIZE_MAX is not ok"); }
ok(!can_multiply(SIZE_MAX, 2, SIZE_MAX), "2*SIZE_MAX is not ok");
}
{ {
ok(can_multiply(SIZE_MAX, 10240, sizeof(MMDB_entry_data_list_s)), ok(can_multiply(SIZE_MAX, 10240, sizeof(MMDB_entry_data_list_s)),

View File

@ -43,32 +43,31 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
static void * static void *
mmdb_memmem(const void *l, size_t l_len, const void *s, size_t s_len) mmdb_memmem(const void *l, size_t l_len, const void *s, size_t s_len) {
{ register char *cur, *last;
register char *cur, *last; const char *cl = (const char *)l;
const char *cl = (const char *)l; const char *cs = (const char *)s;
const char *cs = (const char *)s;
/* we need something to compare */ /* we need something to compare */
if (l_len == 0 || s_len == 0) if (l_len == 0 || s_len == 0)
return NULL; return NULL;
/* "s" must be smaller or equal to "l" */ /* "s" must be smaller or equal to "l" */
if (l_len < s_len) if (l_len < s_len)
return NULL; return NULL;
/* special case where s_len == 1 */ /* special case where s_len == 1 */
if (s_len == 1) if (s_len == 1)
return memchr(l, (int)*cs, l_len); return memchr(l, (int)*cs, l_len);
/* the last position where its possible to find "s" in "l" */ /* the last position where its possible to find "s" in "l" */
last = (char *)cl + l_len - s_len; last = (char *)cl + l_len - s_len;
for (cur = (char *)cl; cur <= last; cur++) for (cur = (char *)cl; cur <= last; cur++)
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
return cur; return cur;
return NULL; return NULL;
} }
/* Applies to strnlen implementation */ /* Applies to strnlen implementation */
@ -97,16 +96,14 @@ mmdb_memmem(const void *l, size_t l_len, const void *s, size_t s_len)
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
static size_t static size_t mmdb_strnlen(const char *s, size_t maxlen) {
mmdb_strnlen(const char *s, size_t maxlen) size_t len;
{
size_t len;
for (len = 0; len < maxlen; len++, s++) { for (len = 0; len < maxlen; len++, s++) {
if (!*s) if (!*s)
break; break;
} }
return (len); return (len);
} }
/* Applies to strdup and strndup implementation */ /* Applies to strdup and strndup implementation */
@ -138,30 +135,26 @@ mmdb_strnlen(const char *s, size_t maxlen)
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
static char * static char *mmdb_strdup(const char *str) {
mmdb_strdup(const char *str) size_t len;
{ char *copy;
size_t len;
char *copy;
len = strlen(str) + 1; len = strlen(str) + 1;
if ((copy = malloc(len)) == NULL) if ((copy = malloc(len)) == NULL)
return (NULL); return (NULL);
memcpy(copy, str, len); memcpy(copy, str, len);
return (copy); return (copy);
} }
static char * static char *mmdb_strndup(const char *str, size_t n) {
mmdb_strndup(const char *str, size_t n) size_t len;
{ char *copy;
size_t len;
char *copy;
len = mmdb_strnlen(str, n); len = mmdb_strnlen(str, n);
if ((copy = malloc(len + 1)) == NULL) if ((copy = malloc(len + 1)) == NULL)
return (NULL); return (NULL);
memcpy(copy, str, len); memcpy(copy, str, len);
copy[len] = '\0'; copy[len] = '\0';
return (copy); return (copy);
} }
/* *INDENT-ON* */ /* *INDENT-ON* */

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,10 @@
#include "maxminddb_test_helper.h" #include "maxminddb_test_helper.h"
int test_read(const char *path, const struct stat *UNUSED( int test_read(const char *path,
sbuf), int flags, struct FTW *UNUSED(ftw)) const struct stat *UNUSED(sbuf),
{ int flags,
struct FTW *UNUSED(ftw)) {
// Check if path is a regular file) // Check if path is a regular file)
if (flags != FTW_F) { if (flags != FTW_F) {
return 0; return 0;
@ -36,16 +37,15 @@ int test_read(const char *path, const struct stat *UNUSED(
BAIL_OUT("could not parse IP address"); BAIL_OUT("could not parse IP address");
} }
cmp_ok(mmdb_error, "!=", MMDB_SUCCESS, "opening %s returned an error", cmp_ok(
path); mmdb_error, "!=", MMDB_SUCCESS, "opening %s returned an error", path);
MMDB_close(mmdb); MMDB_close(mmdb);
free(mmdb); free(mmdb);
return 0; return 0;
} }
int main(void) int main(void) {
{
char *test_db_dir; char *test_db_dir;
#ifdef _WIN32 #ifdef _WIN32
test_db_dir = "../t/maxmind-db/bad-data"; test_db_dir = "../t/maxmind-db/bad-data";

View File

@ -1,7 +1,6 @@
#include "maxminddb_test_helper.h" #include "maxminddb_test_helper.h"
void run_tests(int mode, const char *mode_desc) void run_tests(int mode, const char *mode_desc) {
{
const char *filename = "MaxMind-DB-test-broken-pointers-24.mmdb"; const char *filename = "MaxMind-DB-test-broken-pointers-24.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc); MMDB_s *mmdb = open_ok(path, mode, mode_desc);
@ -15,16 +14,20 @@ void run_tests(int mode, const char *mode_desc)
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
int status = MMDB_get_value(&result.entry, &entry_data, NULL); int status = MMDB_get_value(&result.entry, &entry_data, NULL);
cmp_ok( cmp_ok(status,
status, "==", MMDB_INVALID_DATA_ERROR, "==",
"MMDB_get_value returns MMDB_INVALID_DATA_ERROR for bad pointer in data section"); MMDB_INVALID_DATA_ERROR,
"MMDB_get_value returns MMDB_INVALID_DATA_ERROR for bad pointer "
"in data section");
MMDB_entry_data_list_s *entry_data_list; MMDB_entry_data_list_s *entry_data_list;
status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
cmp_ok( cmp_ok(status,
status, "==", MMDB_INVALID_DATA_ERROR, "==",
"MMDB_get_entry_data_list returns MMDB_INVALID_DATA_ERROR for bad pointer in data section"); MMDB_INVALID_DATA_ERROR,
"MMDB_get_entry_data_list returns MMDB_INVALID_DATA_ERROR for "
"bad pointer in data section");
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
} }
@ -36,17 +39,19 @@ void run_tests(int mode, const char *mode_desc)
MMDB_lookup_result_s UNUSED(result) = MMDB_lookup_result_s UNUSED(result) =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
cmp_ok( cmp_ok(mmdb_error,
mmdb_error, "==", MMDB_CORRUPT_SEARCH_TREE_ERROR, "==",
"MMDB_lookup_string sets mmdb_error to MMDB_CORRUPT_SEARCH_TREE_ERROR when a search tree record points outside the data section"); MMDB_CORRUPT_SEARCH_TREE_ERROR,
"MMDB_lookup_string sets mmdb_error to "
"MMDB_CORRUPT_SEARCH_TREE_ERROR when a search tree record "
"points outside the data section");
} }
MMDB_close(mmdb); MMDB_close(mmdb);
free(mmdb); free(mmdb);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&run_tests); for_all_modes(&run_tests);
done_testing(); done_testing();

View File

@ -7,14 +7,19 @@ static void test_big_lookup(void);
static int Current_Mode; static int Current_Mode;
static const char *Current_Mode_Description; static const char *Current_Mode_Description;
void test_one_result(MMDB_s *mmdb, MMDB_lookup_result_s result, void test_one_result(MMDB_s *mmdb,
const char *ip, const char *expect, MMDB_lookup_result_s result,
const char *function, const char *filename, const char *ip,
const char *mode_desc) const char *expect,
{ const char *function,
const char *filename,
const char *mode_desc) {
int is_ok = ok(result.found_entry, int is_ok = ok(result.found_entry,
"got a result for an IP in the database - %s - %s - %s - %s", "got a result for an IP in the database - %s - %s - %s - %s",
function, ip, filename, mode_desc); function,
ip,
filename,
mode_desc);
if (!is_ok) { if (!is_ok) {
return; return;
@ -33,35 +38,45 @@ void test_one_result(MMDB_s *mmdb, MMDB_lookup_result_s result,
// something like "::1.2.3.4", not just "1.2.3.4". // something like "::1.2.3.4", not just "1.2.3.4".
int maxlen = strlen(expect) + 3; int maxlen = strlen(expect) + 3;
real_expect = malloc(maxlen); real_expect = malloc(maxlen);
if (!real_expect) {
BAIL_OUT("could not allocate memory");
}
snprintf(real_expect, maxlen, "::%s", expect); snprintf(real_expect, maxlen, "::%s", expect);
} }
is(string, real_expect, is(string,
"found expected result for ip key - %s - %s - %s - %s", function, ip, real_expect,
filename, mode_desc); "found expected result for ip key - %s - %s - %s - %s",
function,
ip,
filename,
mode_desc);
free(real_expect); free(real_expect);
free(string); free(string);
} }
void test_one_ip(MMDB_s *mmdb, const char *ip, const char *expect, void test_one_ip(MMDB_s *mmdb,
const char *filename, const char *mode_desc) const char *ip,
{ const char *expect,
const char *filename,
const char *mode_desc) {
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc); lookup_string_ok(mmdb, ip, filename, mode_desc);
test_one_result(mmdb, result, ip, expect, "MMDB_lookup_string", filename, test_one_result(
mode_desc); mmdb, result, ip, expect, "MMDB_lookup_string", filename, mode_desc);
result = lookup_sockaddr_ok(mmdb, ip, filename, mode_desc); result = lookup_sockaddr_ok(mmdb, ip, filename, mode_desc);
test_one_result(mmdb, result, ip, expect, "MMDB_lookup_addrinfo", filename, test_one_result(
mode_desc); mmdb, result, ip, expect, "MMDB_lookup_addrinfo", filename, mode_desc);
} }
void run_ipX_tests(const char *filename, const char **missing_ips, void run_ipX_tests(const char *filename,
int missing_ips_length, const char *pairs[][2], const char **missing_ips,
int pairs_rows) int missing_ips_length,
{ const char *pairs[][2],
int pairs_rows) {
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
int mode = Current_Mode; int mode = Current_Mode;
const char *mode_desc = Current_Mode_Description; const char *mode_desc = Current_Mode_Description;
@ -78,17 +93,21 @@ void run_ipX_tests(const char *filename, const char **missing_ips,
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc); lookup_string_ok(mmdb, ip, filename, mode_desc);
ok( ok(!result.found_entry,
!result.found_entry, "no result entry struct returned for IP address not in the database "
"no result entry struct returned for IP address not in the database (string lookup) - %s - %s - %s", "(string lookup) - %s - %s - %s",
ip, filename, mode_desc); ip,
filename,
mode_desc);
result = lookup_sockaddr_ok(mmdb, ip, filename, mode_desc); result = lookup_sockaddr_ok(mmdb, ip, filename, mode_desc);
ok( ok(!result.found_entry,
!result.found_entry, "no result entry struct returned for IP address not in the database "
"no result entry struct returned for IP address not in the database (ipv4 lookup) - %s - %s - %s", "(ipv4 lookup) - %s - %s - %s",
ip, filename, mode_desc); ip,
filename,
mode_desc);
} }
for (int i = 0; i < pairs_rows; i += 1) { for (int i = 0; i < pairs_rows; i += 1) {
@ -102,52 +121,47 @@ void run_ipX_tests(const char *filename, const char **missing_ips,
free(mmdb); free(mmdb);
} }
void run_ipv4_tests(int UNUSED( void run_ipv4_tests(int UNUSED(record_size),
record_size), const char *filename, const char *UNUSED( const char *filename,
ignored)) const char *UNUSED(ignored)) {
{
const char *pairs[9][2] = { const char *pairs[9][2] = {
{ "1.1.1.1", "1.1.1.1" }, {"1.1.1.1", "1.1.1.1"},
{ "1.1.1.2", "1.1.1.2" }, {"1.1.1.2", "1.1.1.2"},
{ "1.1.1.3", "1.1.1.2" }, {"1.1.1.3", "1.1.1.2"},
{ "1.1.1.7", "1.1.1.4" }, {"1.1.1.7", "1.1.1.4"},
{ "1.1.1.9", "1.1.1.8" }, {"1.1.1.9", "1.1.1.8"},
{ "1.1.1.15", "1.1.1.8" }, {"1.1.1.15", "1.1.1.8"},
{ "1.1.1.17", "1.1.1.16" }, {"1.1.1.17", "1.1.1.16"},
{ "1.1.1.31", "1.1.1.16" }, {"1.1.1.31", "1.1.1.16"},
{ "1.1.1.32", "1.1.1.32" }, {"1.1.1.32", "1.1.1.32"},
}; };
const char *missing[1] = { "2.3.4.5" }; const char *missing[1] = {"2.3.4.5"};
run_ipX_tests(filename, missing, 1, pairs, 9); run_ipX_tests(filename, missing, 1, pairs, 9);
} }
void run_ipv6_tests(int UNUSED( void run_ipv6_tests(int UNUSED(record_size),
record_size), const char *filename, const char *UNUSED( const char *filename,
ignored)) const char *UNUSED(ignored)) {
{
const char *pairs[9][2] = { const char *pairs[9][2] = {
{ "::1:ffff:ffff", "::1:ffff:ffff" }, {"::1:ffff:ffff", "::1:ffff:ffff"},
{ "::2:0:0", "::2:0:0" }, {"::2:0:0", "::2:0:0"},
{ "::2:0:1a", "::2:0:0" }, {"::2:0:1a", "::2:0:0"},
{ "::2:0:40", "::2:0:40" }, {"::2:0:40", "::2:0:40"},
{ "::2:0:4f", "::2:0:40" }, {"::2:0:4f", "::2:0:40"},
{ "::2:0:50", "::2:0:50" }, {"::2:0:50", "::2:0:50"},
{ "::2:0:52", "::2:0:50" }, {"::2:0:52", "::2:0:50"},
{ "::2:0:58", "::2:0:58" }, {"::2:0:58", "::2:0:58"},
{ "::2:0:59", "::2:0:58" }, {"::2:0:59", "::2:0:58"},
}; };
const char *missing[2] = { "2.3.4.5", "::abcd" }; const char *missing[2] = {"2.3.4.5", "::abcd"};
run_ipX_tests(filename, missing, 2, pairs, 9); run_ipX_tests(filename, missing, 2, pairs, 9);
} }
void all_record_sizes(int mode, const char *description) void all_record_sizes(int mode, const char *description) {
{ const char *ipv4_filename_fmts[] = {"MaxMind-DB-test-ipv4-%i.mmdb",
const char *ipv4_filename_fmts[] = { "MaxMind-DB-test-mixed-%i.mmdb"};
"MaxMind-DB-test-ipv4-%i.mmdb",
"MaxMind-DB-test-mixed-%i.mmdb"
};
Current_Mode = mode; Current_Mode = mode;
Current_Mode_Description = description; Current_Mode_Description = description;
@ -156,40 +170,35 @@ void all_record_sizes(int mode, const char *description)
for_all_record_sizes(ipv4_filename_fmts[i], &run_ipv4_tests); for_all_record_sizes(ipv4_filename_fmts[i], &run_ipv4_tests);
} }
const char *ipv6_filename_fmts[] = { const char *ipv6_filename_fmts[] = {"MaxMind-DB-test-ipv6-%i.mmdb",
"MaxMind-DB-test-ipv6-%i.mmdb", "MaxMind-DB-test-mixed-%i.mmdb"};
"MaxMind-DB-test-mixed-%i.mmdb"
};
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
for_all_record_sizes(ipv6_filename_fmts[i], &run_ipv6_tests); for_all_record_sizes(ipv6_filename_fmts[i], &run_ipv6_tests);
} }
} }
static void test_big_lookup(void) static void test_big_lookup(void) {
{
const char *const db_filename = "GeoIP2-Precision-Enterprise-Test.mmdb"; const char *const db_filename = "GeoIP2-Precision-Enterprise-Test.mmdb";
const char *const db_path = test_database_path(db_filename); const char *const db_path = test_database_path(db_filename);
ok(db_path != NULL, "got database path"); ok(db_path != NULL, "got database path");
MMDB_s * const mmdb = open_ok(db_path, MMDB_MODE_MMAP, "mmap mode"); MMDB_s *const mmdb = open_ok(db_path, MMDB_MODE_MMAP, "mmap mode");
ok(mmdb != NULL, "opened MMDB"); ok(mmdb != NULL, "opened MMDB");
free((char *)db_path); free((char *)db_path);
int gai_err = 0, mmdb_err = 0; int gai_err = 0, mmdb_err = 0;
const char *const ip_address = "81.2.69.160"; const char *const ip_address = "81.2.69.160";
MMDB_lookup_result_s result = MMDB_lookup_string(mmdb, ip_address, &gai_err, MMDB_lookup_result_s result =
&mmdb_err); MMDB_lookup_string(mmdb, ip_address, &gai_err, &mmdb_err);
ok(gai_err == 0, "no getaddrinfo error"); ok(gai_err == 0, "no getaddrinfo error");
ok(mmdb_err == MMDB_SUCCESS, "no error from maxminddb library"); ok(mmdb_err == MMDB_SUCCESS, "no error from maxminddb library");
ok(result.found_entry, "found IP"); ok(result.found_entry, "found IP");
MMDB_entry_data_list_s *entry_data_list = NULL; MMDB_entry_data_list_s *entry_data_list = NULL;
ok( ok(MMDB_get_entry_data_list(&result.entry, &entry_data_list) ==
MMDB_get_entry_data_list(&result.entry, MMDB_SUCCESS,
&entry_data_list) == MMDB_SUCCESS, "successfully looked up entry data list");
"successfully looked up entry data list"
);
ok(entry_data_list != NULL, "got an entry_data_list"); ok(entry_data_list != NULL, "got an entry_data_list");
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
@ -198,8 +207,7 @@ static void test_big_lookup(void)
free(mmdb); free(mmdb);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&all_record_sizes); for_all_modes(&all_record_sizes);
test_big_lookup(); test_big_lookup();

View File

@ -1,21 +1,19 @@
#include "libtap/tap.h"
#include "maxminddb_test_helper.h"
#include <assert.h> #include <assert.h>
#include <data-pool.h> #include <data-pool.h>
#include <inttypes.h> #include <inttypes.h>
#include "libtap/tap.h"
#include <math.h> #include <math.h>
#include "maxminddb_test_helper.h"
static void test_data_pool_new(void); static void test_data_pool_new(void);
static void test_data_pool_destroy(void); static void test_data_pool_destroy(void);
static void test_data_pool_alloc(void); static void test_data_pool_alloc(void);
static void test_data_pool_to_list(void); static void test_data_pool_to_list(void);
static bool create_and_check_list(size_t const, static bool create_and_check_list(size_t const, size_t const);
size_t const);
static void check_block_count(MMDB_entry_data_list_s const *const, static void check_block_count(MMDB_entry_data_list_s const *const,
size_t const); size_t const);
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
test_data_pool_new(); test_data_pool_new();
test_data_pool_destroy(); test_data_pool_destroy();
@ -24,8 +22,7 @@ int main(void)
done_testing(); done_testing();
} }
static void test_data_pool_new(void) static void test_data_pool_new(void) {
{
{ {
MMDB_data_pool_s *const pool = data_pool_new(0); MMDB_data_pool_s *const pool = data_pool_new(0);
ok(!pool, "size 0 is not valid"); ok(!pool, "size 0 is not valid");
@ -45,11 +42,8 @@ static void test_data_pool_new(void)
} }
} }
static void test_data_pool_destroy(void) static void test_data_pool_destroy(void) {
{ { data_pool_destroy(NULL); }
{
data_pool_destroy(NULL);
}
{ {
MMDB_data_pool_s *const pool = data_pool_new(512); MMDB_data_pool_s *const pool = data_pool_new(512);
@ -58,8 +52,7 @@ static void test_data_pool_destroy(void)
} }
} }
static void test_data_pool_alloc(void) static void test_data_pool_alloc(void) {
{
{ {
MMDB_data_pool_s *const pool = data_pool_new(1); MMDB_data_pool_s *const pool = data_pool_new(1);
ok(pool != NULL, "created pool"); ok(pool != NULL, "created pool");
@ -109,20 +102,18 @@ static void test_data_pool_alloc(void)
ok(entry != NULL, "got an entry"); ok(entry != NULL, "got an entry");
entry->entry_data.offset = (uint32_t)initial_size; entry->entry_data.offset = (uint32_t)initial_size;
cmp_ok(pool->size, "==", initial_size * 2, cmp_ok(
"size is the initial size*2"); pool->size, "==", initial_size * 2, "size is the initial size*2");
cmp_ok(pool->used, "==", 1, "used size is as expected"); cmp_ok(pool->used, "==", 1, "used size is as expected");
MMDB_entry_data_list_s *const list = data_pool_to_list(pool); MMDB_entry_data_list_s *const list = data_pool_to_list(pool);
MMDB_entry_data_list_s *element = list; MMDB_entry_data_list_s *element = list;
for (size_t i = 0; i < initial_size + 1; i++) { for (size_t i = 0; i < initial_size + 1; i++) {
ok( ok(element->entry_data.offset == (uint32_t)i,
element->entry_data.offset == (uint32_t)i, "found offset %" PRIu32 ", should have %zu",
"found offset %" PRIu32 ", should have %zu", element->entry_data.offset,
element->entry_data.offset, i);
i
);
element = element->next; element = element->next;
} }
@ -133,8 +124,7 @@ static void test_data_pool_alloc(void)
} }
} }
static void test_data_pool_to_list(void) static void test_data_pool_to_list(void) {
{
{ {
size_t const initial_size = 16; size_t const initial_size = 16;
MMDB_data_pool_s *const pool = data_pool_new(initial_size); MMDB_data_pool_s *const pool = data_pool_new(initial_size);
@ -143,8 +133,8 @@ static void test_data_pool_to_list(void)
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool); MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
ok(entry1 != NULL, "got an entry"); ok(entry1 != NULL, "got an entry");
MMDB_entry_data_list_s *const list_one_element MMDB_entry_data_list_s *const list_one_element =
= data_pool_to_list(pool); data_pool_to_list(pool);
ok(list_one_element != NULL, "got a list"); ok(list_one_element != NULL, "got a list");
ok(list_one_element == entry1, ok(list_one_element == entry1,
"list's first element is the first we retrieved"); "list's first element is the first we retrieved");
@ -153,8 +143,8 @@ static void test_data_pool_to_list(void)
MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool); MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
ok(entry2 != NULL, "got another entry"); ok(entry2 != NULL, "got another entry");
MMDB_entry_data_list_s *const list_two_elements MMDB_entry_data_list_s *const list_two_elements =
= data_pool_to_list(pool); data_pool_to_list(pool);
ok(list_two_elements != NULL, "got a list"); ok(list_two_elements != NULL, "got a list");
ok(list_two_elements == entry1, ok(list_two_elements == entry1,
"list's first element is the first we retrieved"); "list's first element is the first we retrieved");
@ -176,8 +166,8 @@ static void test_data_pool_to_list(void)
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool); MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
ok(entry1 != NULL, "got an entry"); ok(entry1 != NULL, "got an entry");
MMDB_entry_data_list_s *const list_one_element MMDB_entry_data_list_s *const list_one_element =
= data_pool_to_list(pool); data_pool_to_list(pool);
ok(list_one_element != NULL, "got a list"); ok(list_one_element != NULL, "got a list");
ok(list_one_element == entry1, ok(list_one_element == entry1,
"list's first element is the first we retrieved"); "list's first element is the first we retrieved");
@ -213,72 +203,54 @@ static void test_data_pool_to_list(void)
{ {
diag("starting test: fill one block save for one spot"); diag("starting test: fill one block save for one spot");
ok( ok(create_and_check_list(3, 2), "fill one block save for one spot");
create_and_check_list(3, 2),
"fill one block save for one spot"
);
} }
{ {
diag("starting test: fill one block"); diag("starting test: fill one block");
ok( ok(create_and_check_list(3, 3), "fill one block");
create_and_check_list(3, 3),
"fill one block"
);
} }
{ {
diag("starting test: fill one block and use one spot in the next block"); diag(
ok( "starting test: fill one block and use one spot in the next block");
create_and_check_list(3, 3 + 1), ok(create_and_check_list(3, 3 + 1),
"fill one block and use one spot in the next block" "fill one block and use one spot in the next block");
);
} }
{ {
diag("starting test: fill two blocks save for one spot"); diag("starting test: fill two blocks save for one spot");
ok( ok(create_and_check_list(3, 3 + 3 * 2 - 1),
create_and_check_list(3, 3 + 3 * 2 - 1), "fill two blocks save for one spot");
"fill two blocks save for one spot"
);
} }
{ {
diag("starting test: fill two blocks"); diag("starting test: fill two blocks");
ok( ok(create_and_check_list(3, 3 + 3 * 2), "fill two blocks");
create_and_check_list(3, 3 + 3 * 2),
"fill two blocks"
);
} }
{ {
diag("starting test: fill two blocks and use one spot in the next"); diag("starting test: fill two blocks and use one spot in the next");
ok( ok(create_and_check_list(3, 3 + 3 * 2 + 1),
create_and_check_list(3, 3 + 3 * 2 + 1), "fill two blocks and use one spot in the next");
"fill two blocks and use one spot in the next"
);
} }
{ {
diag("starting test: fill three blocks save for one spot"); diag("starting test: fill three blocks save for one spot");
ok( ok(create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2 - 1),
create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2 - 1), "fill three blocks save for one spot");
"fill three blocks save for one spot"
);
} }
{ {
diag("starting test: fill three blocks"); diag("starting test: fill three blocks");
ok( ok(create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2),
create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2), "fill three blocks");
"fill three blocks"
);
} }
// It would be nice to have a larger number of these, but it's expensive to // It would be nice to have a larger number of these, but it's expensive to
// run many. We currently hardcode what this will be anyway, so varying // run many. We currently hardcode what this will be anyway, so varying
// this is not very interesting. // this is not very interesting.
size_t const initial_sizes[] = { 1, 2, 32, 64, 128, 256 }; size_t const initial_sizes[] = {1, 2, 32, 64, 128, 256};
size_t const max_element_count = 4096; size_t const max_element_count = 4096;
@ -296,8 +268,7 @@ static void test_data_pool_to_list(void)
// Use assert() rather than libtap as libtap is significantly slower and we run // Use assert() rather than libtap as libtap is significantly slower and we run
// this frequently. // this frequently.
static bool create_and_check_list(size_t const initial_size, static bool create_and_check_list(size_t const initial_size,
size_t const element_count) size_t const element_count) {
{
MMDB_data_pool_s *const pool = data_pool_new(initial_size); MMDB_data_pool_s *const pool = data_pool_new(initial_size);
assert(pool != NULL); assert(pool != NULL);
@ -305,8 +276,8 @@ static bool create_and_check_list(size_t const initial_size,
// Hold on to the pointers as we initially see them so that we can check // Hold on to the pointers as we initially see them so that we can check
// they are still valid after building the list. // they are still valid after building the list.
MMDB_entry_data_list_s **const entry_array MMDB_entry_data_list_s **const entry_array =
= calloc(element_count, sizeof(MMDB_entry_data_list_s *)); calloc(element_count, sizeof(MMDB_entry_data_list_s *));
assert(entry_array != NULL); assert(entry_array != NULL);
for (size_t i = 0; i < element_count; i++) { for (size_t i = 0; i < element_count; i++) {
@ -349,8 +320,7 @@ static bool create_and_check_list(size_t const initial_size,
// Use assert() rather than libtap as libtap is significantly slower and we run // Use assert() rather than libtap as libtap is significantly slower and we run
// this frequently. // this frequently.
static void check_block_count(MMDB_entry_data_list_s const *const list, static void check_block_count(MMDB_entry_data_list_s const *const list,
size_t const initial_size) size_t const initial_size) {
{
size_t got_block_count = 0; size_t got_block_count = 0;
size_t got_element_count = 0; size_t got_element_count = 0;

View File

@ -1,57 +1,68 @@
#include "maxminddb_test_helper.h" #include "maxminddb_test_helper.h"
MMDB_entry_data_list_s *test_array_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_array_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *array = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *array = entry_data_list = entry_data_list->next;
cmp_ok(array->entry_data.type, "==", MMDB_DATA_TYPE_ARRAY, cmp_ok(array->entry_data.type,
"==",
MMDB_DATA_TYPE_ARRAY,
"'array' key's value is an array"); "'array' key's value is an array");
cmp_ok(array->entry_data.data_size, "==", 3, cmp_ok(array->entry_data.data_size,
"==",
3,
"'array' key's value has 3 elements"); "'array' key's value has 3 elements");
MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next;
cmp_ok(idx0->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(idx0->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"first array entry is a UINT32"); "first array entry is a UINT32");
cmp_ok(idx0->entry_data.uint32, "==", 1, "first array entry value is 1"); cmp_ok(idx0->entry_data.uint32, "==", 1, "first array entry value is 1");
MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next;
cmp_ok(idx1->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(idx1->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"second array entry is a UINT32"); "second array entry is a UINT32");
cmp_ok(idx1->entry_data.uint32, "==", 2, "second array entry value is 2"); cmp_ok(idx1->entry_data.uint32, "==", 2, "second array entry value is 2");
MMDB_entry_data_list_s *idx2 = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *idx2 = entry_data_list = entry_data_list->next;
cmp_ok(idx2->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(idx2->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"third array entry is a UINT32"); "third array entry is a UINT32");
cmp_ok(idx2->entry_data.uint32, "==", 3, "third array entry value is 3"); cmp_ok(idx2->entry_data.uint32, "==", 3, "third array entry value is 3");
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_boolean_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_boolean_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_BOOLEAN, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_BOOLEAN,
"'boolean' key's value is a boolean"); "'boolean' key's value is a boolean");
ok(value->entry_data.boolean, "'boolean' key's value is true"); ok(value->entry_data.boolean, "'boolean' key's value is true");
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_bytes_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_bytes_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_BYTES, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_BYTES,
"'bytes' key's value is bytes"); "'bytes' key's value is bytes");
uint8_t *bytes = malloc(value->entry_data.data_size); uint8_t *bytes = malloc(value->entry_data.data_size);
if (NULL == bytes) { if (NULL == bytes) {
BAIL_OUT("malloc failed"); BAIL_OUT("malloc failed");
} }
memcpy(bytes, value->entry_data.bytes, value->entry_data.data_size); memcpy(bytes, value->entry_data.bytes, value->entry_data.data_size);
uint8_t expect[] = { 0x00, 0x00, 0x00, 0x2a }; uint8_t expect[] = {0x00, 0x00, 0x00, 0x2a};
ok(memcmp(bytes, expect, 4) == 0, "got expected value for bytes key"); ok(memcmp(bytes, expect, 4) == 0, "got expected value for bytes key");
@ -60,12 +71,13 @@ MMDB_entry_data_list_s *test_bytes_value(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_double_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_double_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_DOUBLE, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_DOUBLE,
"'double' key's value is a double"); "'double' key's value is a double");
compare_double(value->entry_data.double_value, 42.123456); compare_double(value->entry_data.double_value, 42.123456);
@ -73,12 +85,13 @@ MMDB_entry_data_list_s *test_double_value(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_float_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_float_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_FLOAT, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_FLOAT,
"'float' key's value is a float"); "'float' key's value is a float");
compare_float(value->entry_data.float_value, 1.1F); compare_float(value->entry_data.float_value, 1.1F);
@ -86,64 +99,79 @@ MMDB_entry_data_list_s *test_float_value(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_int32_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_int32_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_INT32, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_INT32,
"'int32' key's value is an int32"); "'int32' key's value is an int32");
int32_t expect = 1 << 28; int32_t expect = 1 << 28;
expect *= -1; expect *= -1;
cmp_ok(value->entry_data.int32, "==", expect, cmp_ok(value->entry_data.int32,
"==",
expect,
"got expected value for int32 key"); "got expected value for int32 key");
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_arrayX_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_arrayX_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *arrayX = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *arrayX = entry_data_list = entry_data_list->next;
cmp_ok(arrayX->entry_data.type, "==", MMDB_DATA_TYPE_ARRAY, cmp_ok(arrayX->entry_data.type,
"==",
MMDB_DATA_TYPE_ARRAY,
"'map{mapX}{arrayX}' key's value is an array"); "'map{mapX}{arrayX}' key's value is an array");
cmp_ok(arrayX->entry_data.data_size, "==", 3, cmp_ok(arrayX->entry_data.data_size,
"==",
3,
"'map{mapX}{arrayX}' key's value has 3 elements"); "'map{mapX}{arrayX}' key's value has 3 elements");
MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next;
cmp_ok(idx0->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(idx0->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"first array entry is a UINT32"); "first array entry is a UINT32");
cmp_ok(idx0->entry_data.uint32, "==", 7, "first array entry value is 7"); cmp_ok(idx0->entry_data.uint32, "==", 7, "first array entry value is 7");
MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next;
cmp_ok(idx1->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(idx1->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"second array entry is a UINT32"); "second array entry is a UINT32");
cmp_ok(idx1->entry_data.uint32, "==", 8, "second array entry value is 8"); cmp_ok(idx1->entry_data.uint32, "==", 8, "second array entry value is 8");
MMDB_entry_data_list_s *idx2 = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *idx2 = entry_data_list = entry_data_list->next;
cmp_ok(idx2->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(idx2->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"third array entry is a UINT32"); "third array entry is a UINT32");
cmp_ok(idx2->entry_data.uint32, "==", 9, "third array entry value is 9"); cmp_ok(idx2->entry_data.uint32, "==", 9, "third array entry value is 9");
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_mapX_key_value_pair(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_mapX_key_value_pair(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *mapX_key = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *mapX_key = entry_data_list = entry_data_list->next;
cmp_ok(mapX_key->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, cmp_ok(mapX_key->entry_data.type,
"==",
MMDB_DATA_TYPE_UTF8_STRING,
"found a map key in 'map{mapX}'"); "found a map key in 'map{mapX}'");
const char *mapX_key_name = dup_entry_string_or_bail(mapX_key->entry_data); const char *mapX_key_name = dup_entry_string_or_bail(mapX_key->entry_data);
if (strcmp(mapX_key_name, "utf8_stringX") == 0) { if (strcmp(mapX_key_name, "utf8_stringX") == 0) {
MMDB_entry_data_list_s *mapX_value = MMDB_entry_data_list_s *mapX_value = entry_data_list =
entry_data_list = entry_data_list->next; entry_data_list->next;
cmp_ok(mapX_value->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, cmp_ok(mapX_value->entry_data.type,
"==",
MMDB_DATA_TYPE_UTF8_STRING,
"'map{mapX}{utf8_stringX}' type is utf8_string"); "'map{mapX}{utf8_stringX}' type is utf8_string");
const char *utf8_stringX_value = dup_entry_string_or_bail( const char *utf8_stringX_value =
mapX_value->entry_data); dup_entry_string_or_bail(mapX_value->entry_data);
ok(strcmp(utf8_stringX_value, "hello") == 0, ok(strcmp(utf8_stringX_value, "hello") == 0,
"map{mapX}{utf8_stringX} value is 'hello'"); "map{mapX}{utf8_stringX} value is 'hello'");
free((void *)utf8_stringX_value); free((void *)utf8_stringX_value);
@ -158,16 +186,22 @@ MMDB_entry_data_list_s *test_mapX_key_value_pair(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_map_value(MMDB_entry_data_list_s *entry_data_list) MMDB_entry_data_list_s *
{ test_map_value(MMDB_entry_data_list_s *entry_data_list) {
MMDB_entry_data_list_s *map = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *map = entry_data_list = entry_data_list->next;
cmp_ok(map->entry_data.type, "==", MMDB_DATA_TYPE_MAP, cmp_ok(map->entry_data.type,
"==",
MMDB_DATA_TYPE_MAP,
"'map' key's value is a map"); "'map' key's value is a map");
cmp_ok(map->entry_data.data_size, "==", 1, cmp_ok(map->entry_data.data_size,
"==",
1,
"'map' key's value has 1 key/value pair"); "'map' key's value has 1 key/value pair");
MMDB_entry_data_list_s *map_key_1 = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *map_key_1 = entry_data_list = entry_data_list->next;
cmp_ok(map_key_1->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, cmp_ok(map_key_1->entry_data.type,
"==",
MMDB_DATA_TYPE_UTF8_STRING,
"found a map key in 'map'"); "found a map key in 'map'");
const char *map_key_1_name = const char *map_key_1_name =
dup_entry_string_or_bail(map_key_1->entry_data); dup_entry_string_or_bail(map_key_1->entry_data);
@ -175,9 +209,13 @@ MMDB_entry_data_list_s *test_map_value(MMDB_entry_data_list_s *entry_data_list)
free((void *)map_key_1_name); free((void *)map_key_1_name);
MMDB_entry_data_list_s *mapX = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *mapX = entry_data_list = entry_data_list->next;
cmp_ok(mapX->entry_data.type, "==", MMDB_DATA_TYPE_MAP, cmp_ok(mapX->entry_data.type,
"==",
MMDB_DATA_TYPE_MAP,
"'map{mapX}' key's value is a map"); "'map{mapX}' key's value is a map");
cmp_ok(mapX->entry_data.data_size, "==", 2, cmp_ok(mapX->entry_data.data_size,
"==",
2,
"'map' key's value has 2 key/value pairs"); "'map' key's value has 2 key/value pairs");
entry_data_list = test_mapX_key_value_pair(entry_data_list); entry_data_list = test_mapX_key_value_pair(entry_data_list);
@ -186,17 +224,32 @@ MMDB_entry_data_list_s *test_map_value(MMDB_entry_data_list_s *entry_data_list)
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_uint128_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_uint128_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT128, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT128,
"'uint128' key's value is an uint128"); "'uint128' key's value is an uint128");
#if MMDB_UINT128_IS_BYTE_ARRAY #if MMDB_UINT128_IS_BYTE_ARRAY
uint8_t expect[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, uint8_t expect[16] = {0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00};
ok(memcmp(value->entry_data.uint128, expect, 16) == 0, ok(memcmp(value->entry_data.uint128, expect, 16) == 0,
"uint128 field is 2**120"); "uint128 field is 2**120");
#else #else
@ -208,12 +261,13 @@ MMDB_entry_data_list_s *test_uint128_value(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_uint16_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_uint16_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT16, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT16,
"'uint16' key's value is an uint16"); "'uint16' key's value is an uint16");
uint16_t expect = 100; uint16_t expect = 100;
ok(value->entry_data.uint16 == expect, "uint16 field is 100"); ok(value->entry_data.uint16 == expect, "uint16 field is 100");
@ -221,12 +275,13 @@ MMDB_entry_data_list_s *test_uint16_value(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_uint32_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_uint32_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"'uint32' key's value is an uint32"); "'uint32' key's value is an uint32");
uint32_t expect = 1 << 28; uint32_t expect = 1 << 28;
cmp_ok(value->entry_data.uint32, "==", expect, "uint32 field is 100"); cmp_ok(value->entry_data.uint32, "==", expect, "uint32 field is 100");
@ -234,12 +289,13 @@ MMDB_entry_data_list_s *test_uint32_value(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_uint64_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_uint64_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT64, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_UINT64,
"'uint64' key's value is an uint64"); "'uint64' key's value is an uint64");
uint64_t expect = 1; uint64_t expect = 1;
expect <<= 60; expect <<= 60;
@ -248,18 +304,35 @@ MMDB_entry_data_list_s *test_uint64_value(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
MMDB_entry_data_list_s *test_utf8_string_value(MMDB_entry_data_list_s MMDB_entry_data_list_s *
*entry_data_list) test_utf8_string_value(MMDB_entry_data_list_s *entry_data_list) {
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next; MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, cmp_ok(value->entry_data.type,
"==",
MMDB_DATA_TYPE_UTF8_STRING,
"'utf8_string' key's value is a string"); "'utf8_string' key's value is a string");
const char *utf8_string = dup_entry_string_or_bail(value->entry_data); const char *utf8_string = dup_entry_string_or_bail(value->entry_data);
// This is hex for "unicode! ☯ - ♫" as bytes // This is hex for "unicode! ☯ - ♫" as bytes
char expect[19] = char expect[19] = {0x75,
{ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x21, 0x20, 0xe2, 0x98, 0x6e,
0xaf, 0x20, 0x2d, 0x20, 0xe2, 0x99, 0xab, 0x00 }; 0x69,
0x63,
0x6f,
0x64,
0x65,
0x21,
0x20,
0xe2,
0x98,
0xaf,
0x20,
0x2d,
0x20,
0xe2,
0x99,
0xab,
0x00};
is(utf8_string, expect, "got expected value for utf8_string key"); is(utf8_string, expect, "got expected value for utf8_string key");
@ -268,8 +341,7 @@ MMDB_entry_data_list_s *test_utf8_string_value(MMDB_entry_data_list_s
return entry_data_list; return entry_data_list;
} }
void run_tests(int mode, const char *description) void run_tests(int mode, const char *description) {
{
const char *filename = "MaxMind-DB-test-decoder.mmdb"; const char *filename = "MaxMind-DB-test-decoder.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, description); MMDB_s *mmdb = open_ok(path, mode, description);
@ -286,15 +358,19 @@ void run_tests(int mode, const char *description)
BAIL_OUT("MMDB_get_entry_data_list failed with %s", BAIL_OUT("MMDB_get_entry_data_list failed with %s",
MMDB_strerror(status)); MMDB_strerror(status));
} else { } else {
cmp_ok(status, "==", MMDB_SUCCESS, cmp_ok(
"MMDB_get_entry_data_list succeeded"); status, "==", MMDB_SUCCESS, "MMDB_get_entry_data_list succeeded");
} }
first = entry_data_list; first = entry_data_list;
cmp_ok(entry_data_list->entry_data.type, "==", MMDB_DATA_TYPE_MAP, cmp_ok(entry_data_list->entry_data.type,
"==",
MMDB_DATA_TYPE_MAP,
"first entry in entry data list is a map"); "first entry in entry data list is a map");
cmp_ok(entry_data_list->entry_data.data_size, "==", 12, cmp_ok(entry_data_list->entry_data.data_size,
"==",
12,
"first map in entry data list has 12 k/v pairs"); "first map in entry data list has 12 k/v pairs");
while (1) { while (1) {
@ -304,7 +380,9 @@ void run_tests(int mode, const char *description)
break; break;
} }
cmp_ok(key->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING, cmp_ok(key->entry_data.type,
"==",
MMDB_DATA_TYPE_UTF8_STRING,
"found a map key"); "found a map key");
const char *key_name = dup_entry_string_or_bail(key->entry_data); const char *key_name = dup_entry_string_or_bail(key->entry_data);
@ -345,8 +423,7 @@ void run_tests(int mode, const char *description)
free(mmdb); free(mmdb);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&run_tests); for_all_modes(&run_tests);
done_testing(); done_testing();

View File

@ -1,21 +1,40 @@
#include "maxminddb_test_helper.h" #include "maxminddb_test_helper.h"
void test_all_data_types(MMDB_lookup_result_s *result, const char *ip, void test_all_data_types(MMDB_lookup_result_s *result,
const char *UNUSED(filename), const char *mode_desc) const char *ip,
{ const char *UNUSED(filename),
const char *mode_desc) {
{ {
char description[500]; char description[500];
snprintf(description, 500, "utf8_string field for %s - %s", ip, snprintf(
mode_desc); description, 500, "utf8_string field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data = MMDB_entry_data_s data = data_ok(result,
data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description, MMDB_DATA_TYPE_UTF8_STRING,
"utf8_string", NULL); description,
"utf8_string",
NULL);
const char *string = mmdb_strndup(data.utf8_string, data.data_size); const char *string = mmdb_strndup(data.utf8_string, data.data_size);
// This is hex for "unicode! ☯ - ♫" as bytes // This is hex for "unicode! ☯ - ♫" as bytes
char expect[19] = char expect[19] = {0x75,
{ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x21, 0x20, 0xe2, 0x98, 0x6e,
0xaf, 0x20, 0x2d, 0x20, 0xe2, 0x99, 0xab, 0x00 }; 0x69,
0x63,
0x6f,
0x64,
0x65,
0x21,
0x20,
0xe2,
0x98,
0xaf,
0x20,
0x2d,
0x20,
0xe2,
0x99,
0xab,
0x00};
is(string, expect, "got expected utf8_string value"); is(string, expect, "got expected utf8_string value");
free((char *)string); free((char *)string);
@ -47,7 +66,7 @@ void test_all_data_types(MMDB_lookup_result_s *result, const char *ip,
MMDB_entry_data_s data = MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_BYTES, description, "bytes", NULL); data_ok(result, MMDB_DATA_TYPE_BYTES, description, "bytes", NULL);
uint8_t expect[] = { 0x00, 0x00, 0x00, 0x2a }; uint8_t expect[] = {0x00, 0x00, 0x00, 0x2a};
ok(memcmp((uint8_t *)data.bytes, expect, 4) == 0, ok(memcmp((uint8_t *)data.bytes, expect, 4) == 0,
"bytes field has expected value"); "bytes field has expected value");
} }
@ -98,12 +117,25 @@ void test_all_data_types(MMDB_lookup_result_s *result, const char *ip,
char description[500]; char description[500];
snprintf(description, 500, "uint128 field for %s - %s", ip, mode_desc); snprintf(description, 500, "uint128 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data = MMDB_entry_data_s data = data_ok(
data_ok(result, MMDB_DATA_TYPE_UINT128, description, "uint128", result, MMDB_DATA_TYPE_UINT128, description, "uint128", NULL);
NULL);
#if MMDB_UINT128_IS_BYTE_ARRAY #if MMDB_UINT128_IS_BYTE_ARRAY
uint8_t expect[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, uint8_t expect[16] = {0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00};
ok(memcmp(data.uint128, expect, 16) == 0, "uint128 field is 2**120"); ok(memcmp(data.uint128, expect, 16) == 0, "uint128 field is 2**120");
#else #else
mmdb_uint128_t expect = 1; mmdb_uint128_t expect = 1;
@ -116,9 +148,8 @@ void test_all_data_types(MMDB_lookup_result_s *result, const char *ip,
char description[500]; char description[500];
snprintf(description, 500, "boolean field for %s - %s", ip, mode_desc); snprintf(description, 500, "boolean field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data = MMDB_entry_data_s data = data_ok(
data_ok(result, MMDB_DATA_TYPE_BOOLEAN, description, "boolean", result, MMDB_DATA_TYPE_BOOLEAN, description, "boolean", NULL);
NULL);
cmp_ok(data.boolean, "==", true, "boolean field is true"); cmp_ok(data.boolean, "==", true, "boolean field is true");
} }
@ -131,21 +162,18 @@ void test_all_data_types(MMDB_lookup_result_s *result, const char *ip,
ok(data.data_size == 3, "array field has 3 elements"); ok(data.data_size == 3, "array field has 3 elements");
snprintf(description, 500, "array[0] for %s - %s", ip, mode_desc); snprintf(description, 500, "array[0] for %s - %s", ip, mode_desc);
data = data = data_ok(
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "0", result, MMDB_DATA_TYPE_UINT32, description, "array", "0", NULL);
NULL);
ok(data.uint32 == 1, "array[0] is 1"); ok(data.uint32 == 1, "array[0] is 1");
snprintf(description, 500, "array[1] for %s - %s", ip, mode_desc); snprintf(description, 500, "array[1] for %s - %s", ip, mode_desc);
data = data = data_ok(
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "1", result, MMDB_DATA_TYPE_UINT32, description, "array", "1", NULL);
NULL);
ok(data.uint32 == 2, "array[1] is 1"); ok(data.uint32 == 2, "array[1] is 1");
snprintf(description, 500, "array[2] for %s - %s", ip, mode_desc); snprintf(description, 500, "array[2] for %s - %s", ip, mode_desc);
data = data = data_ok(
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "2", result, MMDB_DATA_TYPE_UINT32, description, "array", "2", NULL);
NULL);
ok(data.uint32 == 3, "array[2] is 1"); ok(data.uint32 == 3, "array[2] is 1");
} }
@ -159,64 +187,99 @@ void test_all_data_types(MMDB_lookup_result_s *result, const char *ip,
snprintf(description, 500, "map{mapX} for %s - %s", ip, mode_desc); snprintf(description, 500, "map{mapX} for %s - %s", ip, mode_desc);
data = data = data_ok(
data_ok(result, MMDB_DATA_TYPE_MAP, description, "map", "mapX", result, MMDB_DATA_TYPE_MAP, description, "map", "mapX", NULL);
NULL);
ok(data.data_size == 2, "map{mapX} field has 2 elements"); ok(data.data_size == 2, "map{mapX} field has 2 elements");
snprintf(description, 500, "map{mapX}{utf8_stringX} for %s - %s", ip, snprintf(description,
500,
"map{mapX}{utf8_stringX} for %s - %s",
ip,
mode_desc); mode_desc);
data = data = data_ok(result,
data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description, "map", MMDB_DATA_TYPE_UTF8_STRING,
"mapX", "utf8_stringX", NULL); description,
"map",
"mapX",
"utf8_stringX",
NULL);
const char *string = mmdb_strndup(data.utf8_string, data.data_size); const char *string = mmdb_strndup(data.utf8_string, data.data_size);
is(string, "hello", "map{mapX}{utf8_stringX} is 'hello'"); is(string, "hello", "map{mapX}{utf8_stringX} is 'hello'");
free((char *)string); free((char *)string);
snprintf(description, 500, "map{mapX}{arrayX} for %s - %s", ip, snprintf(
mode_desc); description, 500, "map{mapX}{arrayX} for %s - %s", ip, mode_desc);
data = data = data_ok(result,
data_ok(result, MMDB_DATA_TYPE_ARRAY, description, "map", "mapX", MMDB_DATA_TYPE_ARRAY,
"arrayX", NULL); description,
"map",
"mapX",
"arrayX",
NULL);
ok(data.data_size == 3, "map{mapX}{arrayX} field has 3 elements"); ok(data.data_size == 3, "map{mapX}{arrayX} field has 3 elements");
snprintf(description, 500, "map{mapX}{arrayX}[0] for %s - %s", ip, snprintf(description,
500,
"map{mapX}{arrayX}[0] for %s - %s",
ip,
mode_desc); mode_desc);
data = data = data_ok(result,
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX", MMDB_DATA_TYPE_UINT32,
"arrayX", "0", NULL); description,
"map",
"mapX",
"arrayX",
"0",
NULL);
ok(data.uint32 == 7, "map{mapX}{arrayX}[0] is 7"); ok(data.uint32 == 7, "map{mapX}{arrayX}[0] is 7");
snprintf(description, 500, "map{mapX}{arrayX}[1] for %s - %s", ip, snprintf(description,
500,
"map{mapX}{arrayX}[1] for %s - %s",
ip,
mode_desc); mode_desc);
data = data = data_ok(result,
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX", MMDB_DATA_TYPE_UINT32,
"arrayX", "1", NULL); description,
"map",
"mapX",
"arrayX",
"1",
NULL);
ok(data.uint32 == 8, "map{mapX}{arrayX}[1] is 8"); ok(data.uint32 == 8, "map{mapX}{arrayX}[1] is 8");
snprintf(description, 500, "map{mapX}{arrayX}[2] for %s - %s", ip, snprintf(description,
500,
"map{mapX}{arrayX}[2] for %s - %s",
ip,
mode_desc); mode_desc);
data = data = data_ok(result,
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX", MMDB_DATA_TYPE_UINT32,
"arrayX", "2", NULL); description,
"map",
"mapX",
"arrayX",
"2",
NULL);
ok(data.uint32 == 9, "map{mapX}{arrayX}[2] is 9"); ok(data.uint32 == 9, "map{mapX}{arrayX}[2] is 9");
} }
} }
void test_all_data_types_as_zero(MMDB_lookup_result_s *result, const char *ip, void test_all_data_types_as_zero(MMDB_lookup_result_s *result,
const char *UNUSED( const char *ip,
filename), const char *mode_desc) const char *UNUSED(filename),
{ const char *mode_desc) {
{ {
char description[500]; char description[500];
snprintf(description, 500, "utf8_string field for %s - %s", ip, snprintf(
mode_desc); description, 500, "utf8_string field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data = MMDB_entry_data_s data = data_ok(result,
data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description, MMDB_DATA_TYPE_UTF8_STRING,
"utf8_string", NULL); description,
"utf8_string",
NULL);
is(data.utf8_string, "", "got expected utf8_string value (NULL)"); is(data.utf8_string, "", "got expected utf8_string value (NULL)");
} }
@ -249,7 +312,8 @@ void test_all_data_types_as_zero(MMDB_lookup_result_s *result, const char *ip,
ok(data.data_size == 0, "bytes field data_size is 0"); ok(data.data_size == 0, "bytes field data_size is 0");
/* In C does it makes sense to write something like this? /* In C does it makes sense to write something like this?
uint8_t expect[0] = {}; uint8_t expect[0] = {};
ok(memcmp(data.bytes, expect, 0) == 0, "got expected bytes value (NULL)"); */ ok(memcmp(data.bytes, expect, 0) == 0, "got expected bytes value
(NULL)"); */
} }
{ {
@ -297,12 +361,25 @@ void test_all_data_types_as_zero(MMDB_lookup_result_s *result, const char *ip,
char description[500]; char description[500];
snprintf(description, 500, "uint128 field for %s - %s", ip, mode_desc); snprintf(description, 500, "uint128 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data = MMDB_entry_data_s data = data_ok(
data_ok(result, MMDB_DATA_TYPE_UINT128, description, "uint128", result, MMDB_DATA_TYPE_UINT128, description, "uint128", NULL);
NULL);
#if MMDB_UINT128_IS_BYTE_ARRAY #if MMDB_UINT128_IS_BYTE_ARRAY
uint8_t expect[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, uint8_t expect[16] = {0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00};
ok(memcmp(data.uint128, expect, 16) == 0, "uint128 field is 0"); ok(memcmp(data.uint128, expect, 16) == 0, "uint128 field is 0");
#else #else
mmdb_uint128_t expect = 0; mmdb_uint128_t expect = 0;
@ -314,9 +391,8 @@ void test_all_data_types_as_zero(MMDB_lookup_result_s *result, const char *ip,
char description[500]; char description[500];
snprintf(description, 500, "boolean field for %s - %s", ip, mode_desc); snprintf(description, 500, "boolean field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data = MMDB_entry_data_s data = data_ok(
data_ok(result, MMDB_DATA_TYPE_BOOLEAN, description, "boolean", result, MMDB_DATA_TYPE_BOOLEAN, description, "boolean", NULL);
NULL);
cmp_ok(data.boolean, "==", false, "boolean field is false"); cmp_ok(data.boolean, "==", false, "boolean field is false");
} }
@ -339,8 +415,7 @@ void test_all_data_types_as_zero(MMDB_lookup_result_s *result, const char *ip,
} }
} }
void run_tests(int mode, const char *mode_desc) void run_tests(int mode, const char *mode_desc) {
{
const char *filename = "MaxMind-DB-test-decoder.mmdb"; const char *filename = "MaxMind-DB-test-decoder.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc); MMDB_s *mmdb = open_ok(path, mode, mode_desc);
@ -360,11 +435,15 @@ void run_tests(int mode, const char *mode_desc)
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
cmp_ok(gai_error, "==", EAI_NONAME, cmp_ok(gai_error,
"MMDB_lookup populates getaddrinfo error properly - %s", ip); "==",
EAI_NONAME,
"MMDB_lookup populates getaddrinfo error properly - %s",
ip);
ok(!result.found_entry, ok(!result.found_entry,
"no result entry struct returned for invalid IP address '%s'", ip); "no result entry struct returned for invalid IP address '%s'",
ip);
} }
{ {
@ -372,10 +451,12 @@ void run_tests(int mode, const char *mode_desc)
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc); lookup_string_ok(mmdb, ip, filename, mode_desc);
ok( ok(!result.found_entry,
!result.found_entry, "no result entry struct returned for IP address not in the database "
"no result entry struct returned for IP address not in the database - %s - %s - %s", "- %s - %s - %s",
ip, filename, mode_desc); ip,
filename,
mode_desc);
} }
{ {
@ -383,15 +464,21 @@ void run_tests(int mode, const char *mode_desc)
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc); lookup_string_ok(mmdb, ip, filename, mode_desc);
ok( ok(result.found_entry,
result.found_entry, "got a result entry struct for IP address in the database - %s - %s "
"got a result entry struct for IP address in the database - %s - %s - %s", "- %s",
ip, filename, mode_desc); ip,
filename,
mode_desc);
cmp_ok( cmp_ok(result.entry.offset,
result.entry.offset, ">", 0, ">",
"result.entry.offset > 0 for address in the database - %s - %s - %s", 0,
ip, filename, mode_desc); "result.entry.offset > 0 for address in the database - %s - %s "
"- %s",
ip,
filename,
mode_desc);
test_all_data_types(&result, ip, filename, mode_desc); test_all_data_types(&result, ip, filename, mode_desc);
} }
@ -401,15 +488,21 @@ void run_tests(int mode, const char *mode_desc)
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc); lookup_string_ok(mmdb, ip, filename, mode_desc);
ok( ok(result.found_entry,
result.found_entry, "got a result entry struct for IP address in the database - %s - %s "
"got a result entry struct for IP address in the database - %s - %s - %s", "- %s",
ip, filename, mode_desc); ip,
filename,
mode_desc);
cmp_ok( cmp_ok(result.entry.offset,
result.entry.offset, ">", 0, ">",
"result.entry.offset > 0 for address in the database - %s - %s - %s", 0,
ip, filename, mode_desc); "result.entry.offset > 0 for address in the database - %s - %s "
"- %s",
ip,
filename,
mode_desc);
test_all_data_types(&result, ip, filename, mode_desc); test_all_data_types(&result, ip, filename, mode_desc);
} }
@ -419,10 +512,12 @@ void run_tests(int mode, const char *mode_desc)
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc); lookup_string_ok(mmdb, ip, filename, mode_desc);
ok( ok(result.found_entry,
result.found_entry, "got a result entry struct for IP address in the database - %s - %s "
"got a result entry struct for IP address in the database - %s - %s - %s", "- %s",
ip, filename, mode_desc); ip,
filename,
mode_desc);
test_all_data_types_as_zero(&result, ip, filename, mode_desc); test_all_data_types_as_zero(&result, ip, filename, mode_desc);
} }
@ -431,8 +526,7 @@ void run_tests(int mode, const char *mode_desc)
free(mmdb); free(mmdb);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&run_tests); for_all_modes(&run_tests);
done_testing(); done_testing();

View File

@ -2,8 +2,7 @@
#include "maxminddb_test_helper.h" #include "maxminddb_test_helper.h"
#ifdef HAVE_OPEN_MEMSTREAM #ifdef HAVE_OPEN_MEMSTREAM
void run_tests(int mode, const char *mode_desc) void run_tests(int mode, const char *mode_desc) {
{
const char *filename = "MaxMind-DB-test-decoder.mmdb"; const char *filename = "MaxMind-DB-test-decoder.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc); MMDB_s *mmdb = open_ok(path, mode, mode_desc);
@ -16,8 +15,7 @@ void run_tests(int mode, const char *mode_desc)
MMDB_entry_data_list_s *entry_data_list; MMDB_entry_data_list_s *entry_data_list;
int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
ok(MMDB_SUCCESS == status, ok(MMDB_SUCCESS == status, "MMDB_get_entry_data_list is successful");
"MMDB_get_entry_data_list is successful");
char *dump_output; char *dump_output;
size_t dump_size; size_t dump_size;
@ -32,54 +30,53 @@ void run_tests(int mode, const char *mode_desc)
cmp_ok(dump_size, ">", 0, "MMDB_dump produced output - %s", mode_desc); cmp_ok(dump_size, ">", 0, "MMDB_dump produced output - %s", mode_desc);
char *expect[] = { char *expect[] = {"{",
"{", " \"array\": ",
" \"array\": ", " [",
" [", " 1 <uint32>",
" 1 <uint32>", " 2 <uint32>",
" 2 <uint32>", " 3 <uint32>",
" 3 <uint32>", " ]",
" ]", " \"boolean\": ",
" \"boolean\": ", " true <boolean>",
" true <boolean>", " \"bytes\": ",
" \"bytes\": ", " 0000002A <bytes>",
" 0000002A <bytes>", " \"double\": ",
" \"double\": ", " 42.123456 <double>",
" 42.123456 <double>", " \"float\": ",
" \"float\": ", " 1.100000 <float>",
" 1.100000 <float>", " \"int32\": ",
" \"int32\": ", " -268435456 <int32>",
" -268435456 <int32>", " \"map\": ",
" \"map\": ", " {",
" {", " \"mapX\": ",
" \"mapX\": ", " {",
" {", " \"arrayX\": ",
" \"arrayX\": ", " [",
" [", " 7 <uint32>",
" 7 <uint32>", " 8 <uint32>",
" 8 <uint32>", " 9 <uint32>",
" 9 <uint32>", " ]",
" ]", " \"utf8_stringX\": ",
" \"utf8_stringX\": ", " \"hello\" <utf8_string>",
" \"hello\" <utf8_string>", " }",
" }", " }",
" }", " \"uint128\": ",
" \"uint128\": ", " 0x01000000000000000000000000000000 <uint128>",
" 0x01000000000000000000000000000000 <uint128>", " \"uint16\": ",
" \"uint16\": ", " 100 <uint16>",
" 100 <uint16>", " \"uint32\": ",
" \"uint32\": ", " 268435456 <uint32>",
" 268435456 <uint32>", " \"uint64\": ",
" \"uint64\": ", " 1152921504606846976 <uint64>",
" 1152921504606846976 <uint64>", " \"utf8_string\": ",
" \"utf8_string\": ", " \"unicode! ☯ - ♫\" <utf8_string>",
" \"unicode! ☯ - ♫\" <utf8_string>", "}"};
"}"
};
for (int i = 0; i < 42; i++) { for (int i = 0; i < 42; i++) {
ok((strstr(dump_output, expect[i]) != NULL), ok((strstr(dump_output, expect[i]) != NULL),
"dump output contains expected line (%s) - %s", expect[i], "dump output contains expected line (%s) - %s",
expect[i],
mode_desc); mode_desc);
} }
@ -89,15 +86,13 @@ void run_tests(int mode, const char *mode_desc)
free(mmdb); free(mmdb);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&run_tests); for_all_modes(&run_tests);
done_testing(); done_testing();
} }
#else #else
int main(void) int main(void) {
{
plan(SKIP_ALL, "This test requires the open_memstream() function"); plan(SKIP_ALL, "This test requires the open_memstream() function");
} }
#endif #endif

View File

@ -14,15 +14,20 @@
* exists before checking to see if the lookups are correct. * exists before checking to see if the lookups are correct.
*/ */
void test_one_ip(MMDB_s *mmdb, const char *filename, const char *mode_desc, void test_one_ip(MMDB_s *mmdb,
char *ip, char *country_code) const char *filename,
{ const char *mode_desc,
char *ip,
char *country_code) {
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc); lookup_string_ok(mmdb, ip, filename, mode_desc);
MMDB_entry_data_s entry_data = MMDB_entry_data_s entry_data = data_ok(&result,
data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "country{iso_code}", MMDB_DATA_TYPE_UTF8_STRING,
"country", "iso_code", NULL); "country{iso_code}",
"country",
"iso_code",
NULL);
if (ok(entry_data.has_data, "found data for country{iso_code}")) { if (ok(entry_data.has_data, "found data for country{iso_code}")) {
char *string = char *string =
@ -31,16 +36,16 @@ void test_one_ip(MMDB_s *mmdb, const char *filename, const char *mode_desc,
ok(0, "mmdb_strndup() call failed"); ok(0, "mmdb_strndup() call failed");
exit(1); exit(1);
} }
if (!ok(strcmp(string, if (!ok(strcmp(string, country_code) == 0,
country_code) == 0, "iso_code is %s", country_code)) { "iso_code is %s",
country_code)) {
diag(" value is %s", string); diag(" value is %s", string);
} }
free(string); free(string);
} }
} }
void run_tests(int mode, const char *mode_desc) void run_tests(int mode, const char *mode_desc) {
{
const char *filename = "GeoIP2-City-Test.mmdb"; const char *filename = "GeoIP2-City-Test.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc); MMDB_s *mmdb = open_ok(path, mode, mode_desc);
@ -50,17 +55,16 @@ void run_tests(int mode, const char *mode_desc)
* another part of the data section. */ * another part of the data section. */
test_one_ip(mmdb, filename, mode_desc, "2001:218::", "JP"); test_one_ip(mmdb, filename, mode_desc, "2001:218::", "JP");
/* This exercises a bug where one subnet's data shares part of the data /* This exercises a bug where one subnet's data shares part of the data
* with another subnet - in this case it is the "country" key (and others) * with another subnet - in this case it is the "country" key (and others)
* in the top level map. We are testing that the "country" key's value is * in the top level map. We are testing that the "country" key's value is
* handled correctly. The value _should_ be a pointer to another map. */ * handled correctly. The value _should_ be a pointer to another map. */
test_one_ip(mmdb, filename, mode_desc, "81.2.69.160", "GB"); test_one_ip(mmdb, filename, mode_desc, "81.2.69.160", "GB");
MMDB_close(mmdb); MMDB_close(mmdb);
free(mmdb); free(mmdb);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&run_tests); for_all_modes(&run_tests);
done_testing(); done_testing();

View File

@ -1,52 +1,70 @@
#include "maxminddb_test_helper.h" #include "maxminddb_test_helper.h"
void test_array_0_result(int status, MMDB_entry_data_s entry_data, void test_array_0_result(int status,
char *function) MMDB_entry_data_s entry_data,
{ char *function) {
cmp_ok(status, "==", MMDB_SUCCESS, cmp_ok(status,
"status for %s() is MMDB_SUCCESS - array[0]", function); "==",
MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - array[0]",
function);
ok(entry_data.has_data, "found a value for array[0]"); ok(entry_data.has_data, "found a value for array[0]");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - array[0]"); "returned entry type is uint32 - array[0]");
cmp_ok(entry_data.uint32, "==", 1, "entry value is 1 - array[0]"); cmp_ok(entry_data.uint32, "==", 1, "entry value is 1 - array[0]");
} }
void test_array_2_result(int status, MMDB_entry_data_s entry_data, void test_array_2_result(int status,
char *function) MMDB_entry_data_s entry_data,
{ char *function) {
cmp_ok(status, "==", MMDB_SUCCESS, cmp_ok(status,
"status for %s() is MMDB_SUCCESS - array[2]", function); "==",
MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - array[2]",
function);
ok(entry_data.has_data, "found a value for array[2]"); ok(entry_data.has_data, "found a value for array[2]");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - array[2]"); "returned entry type is uint32 - array[2]");
cmp_ok(entry_data.uint32, "==", 3, "entry value is 3 - array[2]"); cmp_ok(entry_data.uint32, "==", 3, "entry value is 3 - array[2]");
} }
void test_array_minus_3_result(int status, MMDB_entry_data_s entry_data, void test_array_minus_3_result(int status,
char *function) MMDB_entry_data_s entry_data,
{ char *function) {
cmp_ok(status, "==", MMDB_SUCCESS, cmp_ok(status,
"status for %s() is MMDB_SUCCESS - array[-3]", function); "==",
MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - array[-3]",
function);
ok(entry_data.has_data, "found a value for array[-3]"); ok(entry_data.has_data, "found a value for array[-3]");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - array[-3]"); "returned entry type is uint32 - array[-3]");
cmp_ok(entry_data.uint32, "==", 1, "entry value is 1 - array[-3]"); cmp_ok(entry_data.uint32, "==", 1, "entry value is 1 - array[-3]");
} }
void test_array_minus_1_result(int status, MMDB_entry_data_s entry_data, void test_array_minus_1_result(int status,
char *function) MMDB_entry_data_s entry_data,
{ char *function) {
cmp_ok(status, "==", MMDB_SUCCESS, cmp_ok(status,
"status for %s() is MMDB_SUCCESS - array[-1]", function); "==",
MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - array[-1]",
function);
ok(entry_data.has_data, "found a value for array[-1]"); ok(entry_data.has_data, "found a value for array[-1]");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - array[-1]"); "returned entry type is uint32 - array[-1]");
cmp_ok(entry_data.uint32, "==", 3, "entry value is 3 - array[-1]"); cmp_ok(entry_data.uint32, "==", 3, "entry value is 3 - array[-1]");
} }
int call_vget_value(MMDB_entry_s *entry, MMDB_entry_data_s *entry_data, ...) {
int call_vget_value(MMDB_entry_s *entry, MMDB_entry_data_s *entry_data, ...)
{
va_list keys; va_list keys;
va_start(keys, entry_data); va_start(keys, entry_data);
@ -57,8 +75,7 @@ int call_vget_value(MMDB_entry_s *entry, MMDB_entry_data_s *entry_data, ...)
return status; return status;
} }
void test_simple_structure(int mode, const char *mode_desc) void test_simple_structure(int mode, const char *mode_desc) {
{
const char *filename = "MaxMind-DB-test-decoder.mmdb"; const char *filename = "MaxMind-DB-test-decoder.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc); MMDB_s *mmdb = open_ok(path, mode, mode_desc);
@ -70,7 +87,7 @@ void test_simple_structure(int mode, const char *mode_desc)
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
const char *lookup_path[] = { "array", "0", NULL }; const char *lookup_path[] = {"array", "0", NULL};
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_array_0_result(status, entry_data, "MMDB_aget_value"); test_array_0_result(status, entry_data, "MMDB_aget_value");
@ -84,7 +101,7 @@ void test_simple_structure(int mode, const char *mode_desc)
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
const char *lookup_path[] = { "array", "2", NULL }; const char *lookup_path[] = {"array", "2", NULL};
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_array_2_result(status, entry_data, "MMDB_aget_value"); test_array_2_result(status, entry_data, "MMDB_aget_value");
@ -96,18 +113,19 @@ void test_simple_structure(int mode, const char *mode_desc)
test_array_2_result(status, entry_data, "MMDB_vget_value"); test_array_2_result(status, entry_data, "MMDB_vget_value");
} }
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
int status = MMDB_get_value(&result.entry, &entry_data, "array", "zero", int status =
NULL); MMDB_get_value(&result.entry, &entry_data, "array", "zero", NULL);
cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR, cmp_ok(status,
"==",
MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR,
"MMDB_get_value() returns error on non-integer array index"); "MMDB_get_value() returns error on non-integer array index");
} }
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
const char *lookup_path[] = { "array", "-1", NULL }; const char *lookup_path[] = {"array", "-1", NULL};
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_array_minus_1_result(status, entry_data, "MMDB_aget_value"); test_array_minus_1_result(status, entry_data, "MMDB_aget_value");
@ -122,7 +140,7 @@ void test_simple_structure(int mode, const char *mode_desc)
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
const char *lookup_path[] = { "array", "-3", NULL }; const char *lookup_path[] = {"array", "-3", NULL};
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_array_minus_3_result(status, entry_data, "MMDB_aget_value"); test_array_minus_3_result(status, entry_data, "MMDB_aget_value");
@ -137,77 +155,92 @@ void test_simple_structure(int mode, const char *mode_desc)
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
int status = MMDB_get_value(&result.entry, &entry_data, "array", "-4", int status =
NULL); MMDB_get_value(&result.entry, &entry_data, "array", "-4", NULL);
cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR, cmp_ok(status,
"==",
MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR,
"MMDB_get_value() returns error on too large negative integer"); "MMDB_get_value() returns error on too large negative integer");
} }
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
int status = int status = MMDB_get_value(
MMDB_get_value(&result.entry, &entry_data, "array", &result.entry, &entry_data, "array", "-18446744073709551616", NULL);
"-18446744073709551616", cmp_ok(
NULL); status,
cmp_ok(status, "==", MMDB_INVALID_LOOKUP_PATH_ERROR, "==",
"MMDB_get_value() returns error on integer smaller than LONG_MIN"); MMDB_INVALID_LOOKUP_PATH_ERROR,
"MMDB_get_value() returns error on integer smaller than LONG_MIN");
} }
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
int status = int status = MMDB_get_value(
MMDB_get_value(&result.entry, &entry_data, "array", &result.entry, &entry_data, "array", "18446744073709551616", NULL);
"18446744073709551616", cmp_ok(
NULL); status,
cmp_ok(status, "==", MMDB_INVALID_LOOKUP_PATH_ERROR, "==",
"MMDB_get_value() returns error on integer larger than LONG_MAX"); MMDB_INVALID_LOOKUP_PATH_ERROR,
"MMDB_get_value() returns error on integer larger than LONG_MAX");
} }
MMDB_close(mmdb); MMDB_close(mmdb);
free(mmdb); free(mmdb);
} }
void test_complex_map_a_result(int status, MMDB_entry_data_s entry_data, void test_complex_map_a_result(int status,
char *function) MMDB_entry_data_s entry_data,
{ char *function) {
cmp_ok(status, "==", MMDB_SUCCESS, cmp_ok(status,
"==",
MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - map1{map2}{array}[0]{map3}{a}", "status for %s() is MMDB_SUCCESS - map1{map2}{array}[0]{map3}{a}",
function); function);
ok(entry_data.has_data, ok(entry_data.has_data, "found a value for map1{map2}{array}[0]{map3}{a}");
"found a value for map1{map2}{array}[0]{map3}{a}"); cmp_ok(entry_data.type,
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, "==",
MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - map1{map2}{array}[0]{map3}{a}"); "returned entry type is uint32 - map1{map2}{array}[0]{map3}{a}");
cmp_ok(entry_data.uint32, "==", 1, cmp_ok(entry_data.uint32,
"==",
1,
"entry value is 1 - map1{map2}{array}[0]{map3}{a}"); "entry value is 1 - map1{map2}{array}[0]{map3}{a}");
} }
void test_complex_map_c_result(int status, MMDB_entry_data_s entry_data, void test_complex_map_c_result(int status,
char *function) MMDB_entry_data_s entry_data,
{ char *function) {
cmp_ok( cmp_ok(status,
status, "==", MMDB_SUCCESS, "==",
"status for %s() is MMDB_SUCCESS - map1{map2}{array}[0]{map3}{c}", MMDB_SUCCESS,
function); "status for %s() is MMDB_SUCCESS - map1{map2}{array}[0]{map3}{c}",
ok(entry_data.has_data, function);
"found a value for map1{map2}{array}[0]{map3}{c}"); ok(entry_data.has_data, "found a value for map1{map2}{array}[0]{map3}{c}");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32, cmp_ok(entry_data.type,
"==",
MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - map1{map2}{array}[0]{map3}{c}"); "returned entry type is uint32 - map1{map2}{array}[0]{map3}{c}");
cmp_ok(entry_data.uint32, "==", 3, cmp_ok(entry_data.uint32,
"==",
3,
"entry value is 3 - map1{map2}{array}[0]{map3}{c}"); "entry value is 3 - map1{map2}{array}[0]{map3}{c}");
} }
void test_no_result(int status, MMDB_entry_data_s entry_data, char *function, void test_no_result(int status,
char *path_description) MMDB_entry_data_s entry_data,
{ char *function,
cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR, char *path_description) {
cmp_ok(status,
"==",
MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR,
"status for %s() is MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR - %s", "status for %s() is MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR - %s",
function, path_description); function,
path_description);
ok(!entry_data.has_data, "did not find a value for %s", path_description); ok(!entry_data.has_data, "did not find a value for %s", path_description);
} }
void test_nested_structure(int mode, const char *mode_desc) void test_nested_structure(int mode, const char *mode_desc) {
{
const char *filename = "MaxMind-DB-test-nested.mmdb"; const char *filename = "MaxMind-DB-test-nested.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc); MMDB_s *mmdb = open_ok(path, mode, mode_desc);
@ -219,79 +252,139 @@ void test_nested_structure(int mode, const char *mode_desc)
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
const char *lookup_path[] = const char *lookup_path[] = {
{ "map1", "map2", "array", "0", "map3", "a", NULL }; "map1", "map2", "array", "0", "map3", "a", NULL};
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_complex_map_a_result(status, entry_data, "MMDB_aget_value"); test_complex_map_a_result(status, entry_data, "MMDB_aget_value");
status = MMDB_get_value(&result.entry, &entry_data, status = MMDB_get_value(&result.entry,
"map1", "map2", "array", "0", "map3", "a", &entry_data,
"map1",
"map2",
"array",
"0",
"map3",
"a",
NULL); NULL);
test_complex_map_a_result(status, entry_data, "MMDB_get_value"); test_complex_map_a_result(status, entry_data, "MMDB_get_value");
status = call_vget_value(&result.entry, &entry_data, status = call_vget_value(&result.entry,
"map1", "map2", "array", "0", "map3", "a", &entry_data,
"map1",
"map2",
"array",
"0",
"map3",
"a",
NULL); NULL);
test_complex_map_a_result(status, entry_data, "MMDB_vget_value"); test_complex_map_a_result(status, entry_data, "MMDB_vget_value");
} }
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
const char *lookup_path[] = const char *lookup_path[] = {
{ "map1", "map2", "array", "0", "map3", "c", NULL }; "map1", "map2", "array", "0", "map3", "c", NULL};
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_complex_map_c_result(status, entry_data, "MMDB_aget_value"); test_complex_map_c_result(status, entry_data, "MMDB_aget_value");
status = MMDB_get_value(&result.entry, &entry_data, status = MMDB_get_value(&result.entry,
"map1", "map2", "array", "0", "map3", "c", &entry_data,
"map1",
"map2",
"array",
"0",
"map3",
"c",
NULL); NULL);
test_complex_map_c_result(status, entry_data, "MMDB_get_value"); test_complex_map_c_result(status, entry_data, "MMDB_get_value");
status = call_vget_value(&result.entry, &entry_data, status = call_vget_value(&result.entry,
"map1", "map2", "array", "0", "map3", "c", &entry_data,
"map1",
"map2",
"array",
"0",
"map3",
"c",
NULL); NULL);
test_complex_map_c_result(status, entry_data, "MMDB_vget_value"); test_complex_map_c_result(status, entry_data, "MMDB_vget_value");
} }
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
const char *lookup_path[] = const char *lookup_path[] = {
{ "map1", "map42", "array", "0", "map3", "c", NULL }; "map1", "map42", "array", "0", "map3", "c", NULL};
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_no_result(status, entry_data, "MMDB_aget_value", test_no_result(status,
entry_data,
"MMDB_aget_value",
"map1{map42}{array}[0]{map3}{c}"); "map1{map42}{array}[0]{map3}{c}");
status = MMDB_get_value(&result.entry, &entry_data, status = MMDB_get_value(&result.entry,
"map1", "map42", "array", "0", "map3", "c", &entry_data,
"map1",
"map42",
"array",
"0",
"map3",
"c",
NULL); NULL);
test_no_result(status, entry_data, "MMDB_get_value", test_no_result(status,
entry_data,
"MMDB_get_value",
"map1{map42}{array}[0]{map3}{c}"); "map1{map42}{array}[0]{map3}{c}");
status = call_vget_value(&result.entry, &entry_data, status = call_vget_value(&result.entry,
"map1", "map42", "array", "0", "map3", "c", &entry_data,
"map1",
"map42",
"array",
"0",
"map3",
"c",
NULL); NULL);
test_no_result(status, entry_data, "MMDB_vget_value", test_no_result(status,
entry_data,
"MMDB_vget_value",
"map1{map42}{array}[0]{map3}{c}"); "map1{map42}{array}[0]{map3}{c}");
} }
{ {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
const char *lookup_path[] = const char *lookup_path[] = {
{ "map1", "map2", "array", "9", "map3", "c", NULL }; "map1", "map2", "array", "9", "map3", "c", NULL};
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path); int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_no_result(status, entry_data, "MMDB_aget_value", test_no_result(status,
entry_data,
"MMDB_aget_value",
"map1{map42}{array}[9]{map3}{c}"); "map1{map42}{array}[9]{map3}{c}");
status = MMDB_get_value(&result.entry, &entry_data, status = MMDB_get_value(&result.entry,
"map1", "map2", "array", "9", "map3", "c", &entry_data,
"map1",
"map2",
"array",
"9",
"map3",
"c",
NULL); NULL);
test_no_result(status, entry_data, "MMDB_get_value", test_no_result(status,
entry_data,
"MMDB_get_value",
"map1{map42}{array}[9]{map3}{c}"); "map1{map42}{array}[9]{map3}{c}");
status = call_vget_value(&result.entry, &entry_data, status = call_vget_value(&result.entry,
"map1", "map2", "array", "9", "map3", "c", &entry_data,
"map1",
"map2",
"array",
"9",
"map3",
"c",
NULL); NULL);
test_no_result(status, entry_data, "MMDB_vget_value", test_no_result(status,
entry_data,
"MMDB_vget_value",
"map1{map42}{array}[9]{map3}{c}"); "map1{map42}{array}[9]{map3}{c}");
} }
@ -299,14 +392,12 @@ void test_nested_structure(int mode, const char *mode_desc)
free(mmdb); free(mmdb);
} }
void run_tests(int mode, const char *mode_desc) void run_tests(int mode, const char *mode_desc) {
{
test_simple_structure(mode, mode_desc); test_simple_structure(mode, mode_desc);
test_nested_structure(mode, mode_desc); test_nested_structure(mode, mode_desc);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&run_tests); for_all_modes(&run_tests);
done_testing(); done_testing();

View File

@ -1,21 +1,22 @@
#include "maxminddb_test_helper.h" #include "maxminddb_test_helper.h"
void test_one_ip(MMDB_s *mmdb, const char *ip, const char *filename, void test_one_ip(MMDB_s *mmdb,
const char *mode_desc) const char *ip,
{ const char *filename,
const char *mode_desc) {
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc); lookup_string_ok(mmdb, ip, filename, mode_desc);
ok( ok(result.found_entry,
result.found_entry, "got a result for an IPv4 address included in a larger-than-IPv4 subnet "
"got a result for an IPv4 address included in a larger-than-IPv4 subnet - %s - %s", "- %s - %s",
ip, mode_desc); ip,
mode_desc);
data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "string value for IP", NULL); data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "string value for IP", NULL);
} }
void run_tests(int mode, const char *mode_desc) void run_tests(int mode, const char *mode_desc) {
{
const char *filename = "MaxMind-DB-no-ipv4-search-tree.mmdb"; const char *filename = "MaxMind-DB-no-ipv4-search-tree.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc); MMDB_s *mmdb = open_ok(path, mode, mode_desc);
@ -28,8 +29,7 @@ void run_tests(int mode, const char *mode_desc)
free(mmdb); free(mmdb);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&run_tests); for_all_modes(&run_tests);
done_testing(); done_testing();

View File

@ -1,7 +1,6 @@
#include "maxminddb_test_helper.h" #include "maxminddb_test_helper.h"
void run_tests(int mode, const char *mode_desc) void run_tests(int mode, const char *mode_desc) {
{
const char *filename = "MaxMind-DB-test-ipv4-28.mmdb"; const char *filename = "MaxMind-DB-test-ipv4-28.mmdb";
const char *path = test_database_path(filename); const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc); MMDB_s *mmdb = open_ok(path, mode, mode_desc);
@ -12,18 +11,18 @@ void run_tests(int mode, const char *mode_desc)
MMDB_lookup_result_s UNUSED(result) = MMDB_lookup_result_s UNUSED(result) =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
cmp_ok( cmp_ok(mmdb_error,
mmdb_error, "==", MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR, "==",
"MMDB_lookup_string sets mmdb_error to MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR when we try to look up an IPv6 address in an IPv4-only database"); MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR,
"MMDB_lookup_string sets mmdb_error to "
"MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR when we try to look up an "
"IPv6 address in an IPv4-only database");
struct addrinfo hints = { struct addrinfo hints = {.ai_family = AF_INET6, .ai_flags = AI_NUMERICHOST};
.ai_family = AF_INET6,
.ai_flags = AI_NUMERICHOST
};
struct addrinfo *addresses; struct addrinfo *addresses;
gai_error = getaddrinfo("2001:db8:85a3:0:0:8a2e:370:7334", NULL, gai_error = getaddrinfo(
&hints, &addresses); "2001:db8:85a3:0:0:8a2e:370:7334", NULL, &hints, &addresses);
if (gai_error) { if (gai_error) {
BAIL_OUT("getaddrinfo failed: %s", gai_strerror(gai_error)); BAIL_OUT("getaddrinfo failed: %s", gai_strerror(gai_error));
} }
@ -31,17 +30,19 @@ void run_tests(int mode, const char *mode_desc)
mmdb_error = 0; mmdb_error = 0;
MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error); MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error);
cmp_ok( cmp_ok(mmdb_error,
mmdb_error, "==", MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR, "==",
"MMDB_lookup_sockaddr sets mmdb_error to MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR when we try to look up an IPv6 address in an IPv4-only database"); MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR,
"MMDB_lookup_sockaddr sets mmdb_error to "
"MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR when we try to look up an "
"IPv6 address in an IPv4-only database");
freeaddrinfo(addresses); freeaddrinfo(addresses);
MMDB_close(mmdb); MMDB_close(mmdb);
free(mmdb); free(mmdb);
} }
int main(void) int main(void) {
{
plan(NO_PLAN); plan(NO_PLAN);
for_all_modes(&run_tests); for_all_modes(&run_tests);
done_testing(); done_testing();

View File

@ -18,10 +18,10 @@
#endif #endif
void for_all_record_sizes(const char *filename_fmt, void for_all_record_sizes(const char *filename_fmt,
void (*tests)(int record_size, const char *filename, void (*tests)(int record_size,
const char *description)) const char *filename,
{ const char *description)) {
int sizes[] = { 24, 28, 32 }; int sizes[] = {24, 28, 32};
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
int size = sizes[i]; int size = sizes[i];
@ -35,13 +35,11 @@ void for_all_record_sizes(const char *filename_fmt,
} }
} }
void for_all_modes(void (*tests)(int mode, const char *description)) void for_all_modes(void (*tests)(int mode, const char *description)) {
{
tests(MMDB_MODE_MMAP, "mmap mode"); tests(MMDB_MODE_MMAP, "mmap mode");
} }
const char *test_database_path(const char *filename) const char *test_database_path(const char *filename) {
{
char *test_db_dir; char *test_db_dir;
#ifdef _WIN32 #ifdef _WIN32
test_db_dir = "../t/maxmind-db/test-data"; test_db_dir = "../t/maxmind-db/test-data";
@ -64,10 +62,9 @@ const char *test_database_path(const char *filename)
return (const char *)path; return (const char *)path;
} }
const char *dup_entry_string_or_bail(MMDB_entry_data_s entry_data) const char *dup_entry_string_or_bail(MMDB_entry_data_s entry_data) {
{ const char *string =
const char *string = mmdb_strndup(entry_data.utf8_string, mmdb_strndup(entry_data.utf8_string, entry_data.data_size);
entry_data.data_size);
if (NULL == string) { if (NULL == string) {
BAIL_OUT("mmdb_strndup failed"); BAIL_OUT("mmdb_strndup failed");
} }
@ -75,12 +72,11 @@ const char *dup_entry_string_or_bail(MMDB_entry_data_s entry_data)
return string; return string;
} }
MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc) MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc) {
{
if (0 != access(db_file, R_OK)) { if (0 != access(db_file, R_OK)) {
BAIL_OUT( BAIL_OUT("could not read the specified file - %s\nIf you are in a git "
"could not read the specified file - %s\nIf you are in a git checkout try running 'git submodule update --init'", "checkout try running 'git submodule update --init'",
db_file); db_file);
} }
MMDB_s *mmdb = (MMDB_s *)calloc(1, sizeof(MMDB_s)); MMDB_s *mmdb = (MMDB_s *)calloc(1, sizeof(MMDB_s));
@ -91,8 +87,10 @@ MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc)
int status = MMDB_open(db_file, mode, mmdb); int status = MMDB_open(db_file, mode, mmdb);
int is_ok = ok(MMDB_SUCCESS == status, "open %s status is success - %s", int is_ok = ok(MMDB_SUCCESS == status,
db_file, mode_desc); "open %s status is success - %s",
db_file,
mode_desc);
if (!is_ok) { if (!is_ok) {
diag("open status code = %d (%s)", status, MMDB_strerror(status)); diag("open status code = %d (%s)", status, MMDB_strerror(status));
@ -102,7 +100,8 @@ MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc)
is_ok = ok(mmdb->file_size > 0, is_ok = ok(mmdb->file_size > 0,
"mmdb struct has been set for %s - %s", "mmdb struct has been set for %s - %s",
db_file, mode_desc); db_file,
mode_desc);
if (!is_ok) { if (!is_ok) {
free(mmdb); free(mmdb);
@ -112,26 +111,26 @@ MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc)
return mmdb; return mmdb;
} }
MMDB_lookup_result_s lookup_string_ok(MMDB_s *mmdb, const char *ip, MMDB_lookup_result_s lookup_string_ok(MMDB_s *mmdb,
const char *file, const char *mode_desc) const char *ip,
{ const char *file,
const char *mode_desc) {
int gai_error, mmdb_error; int gai_error, mmdb_error;
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error); MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
test_lookup_errors(gai_error, mmdb_error, "MMDB_lookup_string", ip, file, test_lookup_errors(
mode_desc); gai_error, mmdb_error, "MMDB_lookup_string", ip, file, mode_desc);
return result; return result;
} }
MMDB_lookup_result_s lookup_sockaddr_ok(MMDB_s *mmdb, const char *ip, MMDB_lookup_result_s lookup_sockaddr_ok(MMDB_s *mmdb,
const char *file, const char *mode_desc) const char *ip,
{ const char *file,
const char *mode_desc) {
int ai_flags = AI_NUMERICHOST; int ai_flags = AI_NUMERICHOST;
struct addrinfo hints = { struct addrinfo hints = {.ai_socktype = SOCK_STREAM};
.ai_socktype = SOCK_STREAM
};
struct addrinfo *addresses = NULL; struct addrinfo *addresses = NULL;
if (ip[0] == ':') { if (ip[0] == ':') {
@ -148,7 +147,7 @@ MMDB_lookup_result_s lookup_sockaddr_ok(MMDB_s *mmdb, const char *ip,
int gai_error = getaddrinfo(ip, NULL, &hints, &addresses); int gai_error = getaddrinfo(ip, NULL, &hints, &addresses);
int mmdb_error = 0; int mmdb_error = 0;
MMDB_lookup_result_s result = { .found_entry = false }; MMDB_lookup_result_s result = {.found_entry = false};
if (gai_error == 0) { if (gai_error == 0) {
result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error); result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error);
} }
@ -156,38 +155,48 @@ MMDB_lookup_result_s lookup_sockaddr_ok(MMDB_s *mmdb, const char *ip,
freeaddrinfo(addresses); freeaddrinfo(addresses);
} }
test_lookup_errors(gai_error, mmdb_error, "MMDB_lookup_sockaddr", ip, file, test_lookup_errors(
mode_desc); gai_error, mmdb_error, "MMDB_lookup_sockaddr", ip, file, mode_desc);
return result; return result;
} }
void test_lookup_errors(int gai_error, int mmdb_error, void test_lookup_errors(int gai_error,
const char *function, const char *ip, int mmdb_error,
const char *file, const char *mode_desc) const char *function,
{ const char *ip,
const char *file,
const char *mode_desc) {
int is_ok = ok(0 == gai_error, int is_ok = ok(0 == gai_error,
"no getaddrinfo error in call to %s for %s - %s - %s", "no getaddrinfo error in call to %s for %s - %s - %s",
function, ip, file, mode_desc); function,
ip,
file,
mode_desc);
if (!is_ok) { if (!is_ok) {
diag("error from call to getaddrinfo for %s - %s", diag("error from call to getaddrinfo for %s - %s",
ip, gai_strerror(gai_error)); ip,
gai_strerror(gai_error));
} }
is_ok = ok(0 == mmdb_error, is_ok = ok(0 == mmdb_error,
"no MMDB error in call to %s for %s - %s - %s", "no MMDB error in call to %s for %s - %s - %s",
function, ip, file, mode_desc); function,
ip,
file,
mode_desc);
if (!is_ok) { if (!is_ok) {
diag("MMDB error - %s", MMDB_strerror(mmdb_error)); diag("MMDB error - %s", MMDB_strerror(mmdb_error));
} }
} }
MMDB_entry_data_s data_ok(MMDB_lookup_result_s *result, uint32_t expect_type, MMDB_entry_data_s data_ok(MMDB_lookup_result_s *result,
const char *description, ...) uint32_t expect_type,
{ const char *description,
...) {
va_list keys; va_list keys;
va_start(keys, description); va_start(keys, description);
@ -196,13 +205,20 @@ MMDB_entry_data_s data_ok(MMDB_lookup_result_s *result, uint32_t expect_type,
va_end(keys); va_end(keys);
if (cmp_ok(status, "==", MMDB_SUCCESS, if (cmp_ok(status,
"no error from call to MMDB_vget_value - %s", description)) { "==",
MMDB_SUCCESS,
"no error from call to MMDB_vget_value - %s",
description)) {
if (!cmp_ok(data.type, "==", expect_type, if (!cmp_ok(data.type,
"got the expected data type - %s", description)) { "==",
expect_type,
"got the expected data type - %s",
description)) {
diag(" data type value is %i but expected %i", data.type, diag(" data type value is %i but expected %i",
data.type,
expect_type); expect_type);
} }
} else { } else {
@ -212,22 +228,20 @@ MMDB_entry_data_s data_ok(MMDB_lookup_result_s *result, uint32_t expect_type,
return data; return data;
} }
void compare_double(double got, double expect) void compare_double(double got, double expect) {
{
double diff = fabs(got - expect); double diff = fabs(got - expect);
int is_ok = ok(diff < 0.01, "double value was approximately %2.6f", expect); int is_ok = ok(diff < 0.01, "double value was approximately %2.6f", expect);
if (!is_ok) { if (!is_ok) {
diag(" got %2.6f but expected %2.6f (diff = %2.6f)", diag(
got, expect, diff); " got %2.6f but expected %2.6f (diff = %2.6f)", got, expect, diff);
} }
} }
void compare_float(float got, float expect) void compare_float(float got, float expect) {
{
float diff = fabsf(got - expect); float diff = fabsf(got - expect);
int is_ok = ok(diff < 0.01, "float value was approximately %2.1f", expect); int is_ok = ok(diff < 0.01, "float value was approximately %2.1f", expect);
if (!is_ok) { if (!is_ok) {
diag(" got %2.4f but expected %2.1f (diff = %2.1f)", diag(
got, expect, diff); " got %2.4f but expected %2.1f (diff = %2.1f)", got, expect, diff);
} }
} }

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