mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
Decreased warning threshold [#MOBMET-3248]
This commit is contained in:
commit
1c86e582c1
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
|
||||||
|
# Matches multiple files with brace expansion notation
|
||||||
|
# Set default charset
|
||||||
|
[*.{c,cpp,cxx,h,hpp,hxx,py}]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
contrib/* linguist-vendored
|
||||||
|
*.h linguist-language=C++
|
197
.gitignore
vendored
Normal file
197
.gitignore
vendored
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# emacs files
|
||||||
|
*~
|
||||||
|
*\#
|
||||||
|
|
||||||
|
# auto generated files
|
||||||
|
*.logrt
|
||||||
|
|
||||||
|
build
|
||||||
|
|
||||||
|
# callgrind files
|
||||||
|
callgrind.out.*
|
||||||
|
|
||||||
|
# ignore kdevelop files
|
||||||
|
*.kdev4
|
||||||
|
*.kdev_include_paths
|
||||||
|
|
||||||
|
# ignore perf output
|
||||||
|
*/perf.data
|
||||||
|
|
||||||
|
# ignore build files
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
Makefile
|
||||||
|
cmake_install.cmake
|
||||||
|
CTestTestfile.cmake
|
||||||
|
*.a
|
||||||
|
*.o
|
||||||
|
|
||||||
|
# ignore generated files
|
||||||
|
*-metrika-yandex
|
||||||
|
|
||||||
|
test.cpp
|
||||||
|
utils/compressor/compressor
|
||||||
|
utils/corrector_utf8/corrector_utf8
|
||||||
|
utils/iotest/iotest
|
||||||
|
utils/iotest/iotest_aio
|
||||||
|
utils/iotest/iotest_nonblock
|
||||||
|
utils/config-processor/config-processor
|
||||||
|
CPackConfig.cmake
|
||||||
|
CPackSourceConfig.cmake
|
||||||
|
contrib/libpoco/Poco/
|
||||||
|
contrib/libpoco/bin/
|
||||||
|
contrib/libpoco/cmake_uninstall.cmake
|
||||||
|
contrib/libre2/re2_st/
|
||||||
|
dbms/src/Client/clickhouse-benchmark
|
||||||
|
dbms/src/Client/clickhouse-client
|
||||||
|
dbms/src/Client/tests/test-connect
|
||||||
|
dbms/src/Common/tests/arena_with_free_lists
|
||||||
|
dbms/src/Common/tests/auto_array
|
||||||
|
dbms/src/Common/tests/compact_array
|
||||||
|
dbms/src/Common/tests/hash_table
|
||||||
|
dbms/src/Common/tests/hashes_test
|
||||||
|
dbms/src/Common/tests/int_hashes_perf
|
||||||
|
dbms/src/Common/tests/lru_cache
|
||||||
|
dbms/src/Common/tests/parallel_aggregation
|
||||||
|
dbms/src/Common/tests/parallel_aggregation2
|
||||||
|
dbms/src/Common/tests/radix_sort
|
||||||
|
dbms/src/Common/tests/shell_command_test
|
||||||
|
dbms/src/Common/tests/simple_cache
|
||||||
|
dbms/src/Common/tests/sip_hash
|
||||||
|
dbms/src/Common/tests/sip_hash_perf
|
||||||
|
dbms/src/Common/tests/small_table
|
||||||
|
dbms/src/Core/tests/exception
|
||||||
|
dbms/src/Core/tests/field
|
||||||
|
dbms/src/Core/tests/rvo_test
|
||||||
|
dbms/src/Core/tests/string_pool
|
||||||
|
dbms/src/DataStreams/tests/aggregating_stream
|
||||||
|
dbms/src/DataStreams/tests/block_row_transforms
|
||||||
|
dbms/src/DataStreams/tests/block_tab_separated_streams
|
||||||
|
dbms/src/DataStreams/tests/collapsing_sorted_stream
|
||||||
|
dbms/src/DataStreams/tests/expression_stream
|
||||||
|
dbms/src/DataStreams/tests/filter_stream
|
||||||
|
dbms/src/DataStreams/tests/filter_stream_hitlog
|
||||||
|
dbms/src/DataStreams/tests/fork_streams
|
||||||
|
dbms/src/DataStreams/tests/glue_streams
|
||||||
|
dbms/src/DataStreams/tests/json_streams
|
||||||
|
dbms/src/DataStreams/tests/native_streams
|
||||||
|
dbms/src/DataStreams/tests/sorting_stream
|
||||||
|
dbms/src/DataStreams/tests/tab_separated_streams
|
||||||
|
dbms/src/DataStreams/tests/union_stream
|
||||||
|
dbms/src/DataStreams/tests/union_stream2
|
||||||
|
dbms/src/DataTypes/tests/data_type_string
|
||||||
|
dbms/src/DataTypes/tests/data_types_number_fixed
|
||||||
|
dbms/src/Functions/tests/functions_arithmetic
|
||||||
|
dbms/src/Functions/tests/logical_functions_performance
|
||||||
|
dbms/src/Functions/tests/number_traits
|
||||||
|
dbms/src/IO/tests/async_write
|
||||||
|
dbms/src/IO/tests/cached_compressed_read_buffer
|
||||||
|
dbms/src/IO/tests/compressed_buffer
|
||||||
|
dbms/src/IO/tests/hashing_read_buffer
|
||||||
|
dbms/src/IO/tests/hashing_write_buffer
|
||||||
|
dbms/src/IO/tests/io_and_exceptions
|
||||||
|
dbms/src/IO/tests/io_operators
|
||||||
|
dbms/src/IO/tests/mempbrk
|
||||||
|
dbms/src/IO/tests/o_direct_and_dirty_pages
|
||||||
|
dbms/src/IO/tests/parse_int_perf
|
||||||
|
dbms/src/IO/tests/parse_int_perf2
|
||||||
|
dbms/src/IO/tests/read_buffer
|
||||||
|
dbms/src/IO/tests/read_buffer_aio
|
||||||
|
dbms/src/IO/tests/read_buffer_perf
|
||||||
|
dbms/src/IO/tests/read_escaped_string
|
||||||
|
dbms/src/IO/tests/read_float_perf
|
||||||
|
dbms/src/IO/tests/read_write_int
|
||||||
|
dbms/src/IO/tests/valid_utf8
|
||||||
|
dbms/src/IO/tests/valid_utf8_perf
|
||||||
|
dbms/src/IO/tests/var_uint
|
||||||
|
dbms/src/IO/tests/write_buffer
|
||||||
|
dbms/src/IO/tests/write_buffer_aio
|
||||||
|
dbms/src/IO/tests/write_buffer_perf
|
||||||
|
dbms/src/Interpreters/tests/address_patterns
|
||||||
|
dbms/src/Interpreters/tests/aggregate
|
||||||
|
dbms/src/Interpreters/tests/compiler_test
|
||||||
|
dbms/src/Interpreters/tests/create_query
|
||||||
|
dbms/src/Interpreters/tests/expression
|
||||||
|
dbms/src/Interpreters/tests/expression_analyzer
|
||||||
|
dbms/src/Interpreters/tests/hash_map
|
||||||
|
dbms/src/Interpreters/tests/hash_map2
|
||||||
|
dbms/src/Interpreters/tests/hash_map3
|
||||||
|
dbms/src/Interpreters/tests/hash_map_string
|
||||||
|
dbms/src/Interpreters/tests/hash_map_string_2
|
||||||
|
dbms/src/Interpreters/tests/hash_map_string_3
|
||||||
|
dbms/src/Interpreters/tests/hash_map_string_small
|
||||||
|
dbms/src/Interpreters/tests/in_join_subqueries_preprocessor
|
||||||
|
dbms/src/Interpreters/tests/logical_expressions_optimizer
|
||||||
|
dbms/src/Interpreters/tests/select_query
|
||||||
|
dbms/src/Interpreters/tests/two_level_hash_map
|
||||||
|
dbms/src/Interpreters/tests/users
|
||||||
|
dbms/src/Parsers/tests/create_parser
|
||||||
|
dbms/src/Parsers/tests/select_parser
|
||||||
|
dbms/src/Server/clickhouse-server
|
||||||
|
dbms/src/Server/clickhouse-server.init
|
||||||
|
dbms/src/Storages/tests/hit_log
|
||||||
|
dbms/src/Storages/tests/merge_tree
|
||||||
|
dbms/src/Storages/tests/part_checker
|
||||||
|
dbms/src/Storages/tests/part_name
|
||||||
|
dbms/src/Storages/tests/pk_condition
|
||||||
|
dbms/src/Storages/tests/seek_speed_test
|
||||||
|
dbms/src/Storages/tests/storage_log
|
||||||
|
dbms/src/Storages/tests/system_numbers
|
||||||
|
libs/libcommon/src/revision.h
|
||||||
|
libs/libcommon/src/tests/date_lut2
|
||||||
|
libs/libcommon/src/tests/date_lut3
|
||||||
|
libs/libcommon/src/tests/date_lut4
|
||||||
|
libs/libcommon/src/tests/date_lut_init
|
||||||
|
libs/libcommon/src/tests/multi_version
|
||||||
|
libs/libmysqlxx/src/tests/failover
|
||||||
|
libs/libmysqlxx/src/tests/mysqlxx_test
|
||||||
|
libs/libzkutil/src/tests/zkutil_expiration_test
|
||||||
|
libs/libzkutil/src/tests/zkutil_test
|
||||||
|
libs/libzkutil/src/tests/zkutil_test_async
|
||||||
|
libs/libzkutil/src/tests/zkutil_test_commands
|
||||||
|
libs/libzkutil/src/tests/zkutil_test_lock
|
||||||
|
libs/libzkutil/src/tests/zkutil_zookeeper_holder
|
||||||
|
utils/zookeeper-create-entry-to-download-part/zookeeper-create-entry-to-download-part
|
||||||
|
utils/zookeeper-dump-tree/zookeeper-dump-tree
|
||||||
|
utils/zookeeper-remove-by-list/zookeeper-remove-by-list
|
||||||
|
|
||||||
|
dbms/src/Server/data/*
|
||||||
|
dbms/src/Server/metadata/*
|
||||||
|
dbms/src/Server/status
|
||||||
|
config-9001.xml
|
||||||
|
|
||||||
|
*-preprocessed.xml
|
||||||
|
|
||||||
|
core
|
||||||
|
vgcore*
|
||||||
|
|
||||||
|
*.install
|
||||||
|
*.deb
|
||||||
|
*.build
|
||||||
|
*.upload
|
||||||
|
*.changes
|
||||||
|
build-stamp
|
||||||
|
configure-stamp
|
||||||
|
debian/changelog
|
||||||
|
debian/*.debhelper.log
|
||||||
|
debian/*.cron.d
|
||||||
|
debian/*.init
|
||||||
|
debian/*.debhelper
|
||||||
|
debian/*.substvars
|
||||||
|
|
||||||
|
*.bin
|
||||||
|
*.mrk
|
||||||
|
|
||||||
|
.dupload.conf
|
||||||
|
|
||||||
|
# Netbeans project files
|
||||||
|
nbproject/*
|
||||||
|
|
||||||
|
# JetBrains project files
|
||||||
|
.idea
|
||||||
|
|
||||||
|
config-preprocessed.xml
|
||||||
|
|
||||||
|
# Protobuf
|
||||||
|
*.pb.cpp
|
||||||
|
*.pb.h
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "private"]
|
||||||
|
path = private
|
||||||
|
url = git@github.yandex-team.ru:Metrika/ClickHouse_private.git
|
22
AUTHORS
Normal file
22
AUTHORS
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The following authors have created the source code of "ClickHouse"
|
||||||
|
published and distributed by YANDEX LLC as the owner:
|
||||||
|
|
||||||
|
Alexey Milovidov <milovidov@yandex-team.ru>
|
||||||
|
Alexey Arno <af-arno@yandex-team.ru>
|
||||||
|
Andrey Mironov <hertz@yandex-team.ru>
|
||||||
|
Michael Kolupaev <mkolupaev@yandex-team.ru>
|
||||||
|
Pavel Kartavyy <kartavyy@yandex-team.ru>
|
||||||
|
Evgeniy Gatov <egatov@yandex-team.ru>
|
||||||
|
Sergey Fedorov <fets@yandex-team.ru>
|
||||||
|
Vyacheslav Alipov <alipov@yandex-team.ru>
|
||||||
|
Vladimir Chebotarev <chebotarev@yandex-team.ru>
|
||||||
|
Dmitry Galuza <galuza@yandex-team.ru>
|
||||||
|
Sergey Magidovich <mgsergio@yandex-team.ru>
|
||||||
|
Anton Tikhonov <rokerjoker@yandex-team.ru>
|
||||||
|
Alexey Vasiliev <loudhorr@yandex-team.ru>
|
||||||
|
Andrey Urusov <drobus@yandex-team.ru>
|
||||||
|
Vsevolod Orlov <vorloff@yandex-team.ru>
|
||||||
|
Roman Peshkurov <peshkurov@yandex-team.ru>
|
||||||
|
Michael Razuvaev <razuvaev@yandex-team.ru>
|
||||||
|
Ilya Korolev <breeze@yandex-team.ru>
|
||||||
|
Maxim Nikulin <mnikulin@yandex-team.ru>
|
129
CMakeLists.txt
Normal file
129
CMakeLists.txt
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
SET(CMAKE_C_COMPILER "gcc-5")
|
||||||
|
SET(CMAKE_CXX_COMPILER "g++-5")
|
||||||
|
|
||||||
|
project (METRICA)
|
||||||
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
|
# отключаем варнинг о том, что в каждой директории должен быть CMakeLists.txt
|
||||||
|
CMAKE_POLICY(SET CMP0014 OLD)
|
||||||
|
|
||||||
|
IF(NOT CMAKE_BUILD_TYPE)
|
||||||
|
message(STATUS "CMAKE_BUILD_TYPE is not set, set to default = RELWITHDEBINFO")
|
||||||
|
SET(CMAKE_BUILD_TYPE "RELWITHDEBINFO")
|
||||||
|
ENDIF()
|
||||||
|
MESSAGE( STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE} )
|
||||||
|
|
||||||
|
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Debug;Release;MinSizeRel" CACHE STRING "" FORCE)
|
||||||
|
|
||||||
|
IF (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)")
|
||||||
|
SET(AARCH64 1)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF (NOT AARCH64)
|
||||||
|
SET(MACHINE_FLAGS "-msse4 -mpopcnt")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
SET(CMAKE_BUILD_COLOR_MAKEFILE ON)
|
||||||
|
SET(CMAKE_CXX_FLAGS "-std=gnu++1y -Wall -Werror -Wnon-virtual-dtor ${MACHINE_FLAGS}")
|
||||||
|
SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
|
||||||
|
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g")
|
||||||
|
SET(CMAKE_C_FLAGS "-Wall -Werror ${MACHINE_FLAGS}")
|
||||||
|
SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
|
||||||
|
SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g")
|
||||||
|
SET(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
|
||||||
|
|
||||||
|
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
|
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
|
||||||
|
|
||||||
|
# Флаги для test coverage
|
||||||
|
SET(TEST_COVERAGE TRUE CACHE BOOL "Add compliler flags for test coverage")
|
||||||
|
IF (TEST_COVERAGE)
|
||||||
|
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -fprofile-arcs -ftest-coverage -fPIC -DIS_DEBUG")
|
||||||
|
ENDIF(TEST_COVERAGE)
|
||||||
|
|
||||||
|
# Собирать тесты?
|
||||||
|
IF (NOT DEFINED TESTS)
|
||||||
|
MESSAGE(STATUS "Tests are enabled")
|
||||||
|
SET(TESTS YES)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
# тесты запускать с помощью "make check"
|
||||||
|
IF(TESTS)
|
||||||
|
INCLUDE(add.test.cmake)
|
||||||
|
ENDIF(TESTS)
|
||||||
|
|
||||||
|
# Префикс для установки
|
||||||
|
SET(CMAKE_INSTALL_PREFIX /usr)
|
||||||
|
|
||||||
|
# Директория для файлов, специфичных для Яндекса
|
||||||
|
SET(CLICKHOUSE_PRIVATE_DIR ${METRICA_SOURCE_DIR}/private/)
|
||||||
|
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libcityhash/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/liblz4/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libdivide/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libdouble-conversion/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libcpuid/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libzstd/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libfarmhash/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libmetrohash/src)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libsparsehash/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libre2/)
|
||||||
|
include_directories (${METRICA_BINARY_DIR}/contrib/libre2/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libboost-threadpool/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Foundation/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Util/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Net/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/NetSSL_OpenSSL/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Data/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Data/MySQL/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Data/ODBC/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Data/SQLite/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Crypto/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/XML/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/JSON/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/MongoDB/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/Zip/include/)
|
||||||
|
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/libs/libcommon/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/libs/libdaemon/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/libs/libpocoext/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/libs/libmysqlxx/include/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/libs/libzkutil/include/)
|
||||||
|
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/tools/)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR}/dbms/include)
|
||||||
|
include_directories (${METRICA_SOURCE_DIR})
|
||||||
|
include_directories (${METRICA_BINARY_DIR})
|
||||||
|
|
||||||
|
include_directories (/usr/local/include/glib-2.0/)
|
||||||
|
include_directories (/usr/local/lib/glib-2.0/include/)
|
||||||
|
include_directories (/usr/include/glib-2.0/)
|
||||||
|
|
||||||
|
IF (AARCH64)
|
||||||
|
include_directories (/usr/lib/aarch64-linux-gnu/glib-2.0/include/)
|
||||||
|
ELSE()
|
||||||
|
include_directories (/usr/lib/x86_64-linux-gnu/glib-2.0/include/)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
include_directories (/usr/local/include/)
|
||||||
|
|
||||||
|
link_directories (${METRICA_SOURCE_DIR}/contrib/libcityhash)
|
||||||
|
link_directories (${METRICA_SOURCE_DIR}/contrib/liblz4)
|
||||||
|
link_directories (${METRICA_SOURCE_DIR}/contrib/libdouble-conversion)
|
||||||
|
link_directories (${METRICA_SOURCE_DIR}/contrib/libcpuid)
|
||||||
|
link_directories (${METRICA_SOURCE_DIR}/contrib/libzstd)
|
||||||
|
link_directories (${METRICA_SOURCE_DIR}/contrib/libre2)
|
||||||
|
link_directories (${METRICA_SOURCE_DIR}/contrib/libpoco/lib)
|
||||||
|
|
||||||
|
link_directories (${METRICA_BINARY_DIR}/libs/libcommon)
|
||||||
|
link_directories (${METRICA_BINARY_DIR}/libs/libdaemon)
|
||||||
|
link_directories (${METRICA_BINARY_DIR}/libs/libpocoext)
|
||||||
|
link_directories (${METRICA_BINARY_DIR}/libs/libmysqlxx)
|
||||||
|
|
||||||
|
link_directories (${METRICA_BINARY_DIR}/dbms)
|
||||||
|
link_directories (/usr/local/lib)
|
||||||
|
|
||||||
|
add_subdirectory (contrib)
|
||||||
|
add_subdirectory (libs)
|
||||||
|
add_subdirectory (utils)
|
||||||
|
add_subdirectory (dbms)
|
13
LICENSE
Normal file
13
LICENSE
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Copyright 2016 YANDEX LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
15
add.test.cmake
Normal file
15
add.test.cmake
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# добавляем вывод программы при ошибке теста
|
||||||
|
enable_testing()
|
||||||
|
if (CMAKE_CONFIGURATION_TYPES)
|
||||||
|
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
|
||||||
|
--force-new-ctest-process --output-on-failure
|
||||||
|
--build-config "$<CONFIGURATION>")
|
||||||
|
else()
|
||||||
|
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
|
||||||
|
--force-new-ctest-process --output-on-failure)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
macro (add_check target)
|
||||||
|
add_test(NAME test_${target} COMMAND ${target} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
add_dependencies(check ${target})
|
||||||
|
endmacro (add_check)
|
13
contrib/CMakeLists.txt
vendored
Normal file
13
contrib/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
add_subdirectory (libcityhash)
|
||||||
|
add_subdirectory (liblz4)
|
||||||
|
add_subdirectory (libdouble-conversion)
|
||||||
|
add_subdirectory (libzstd)
|
||||||
|
add_subdirectory (libfarmhash)
|
||||||
|
add_subdirectory (libmetrohash)
|
||||||
|
add_subdirectory (libpoco)
|
||||||
|
add_subdirectory (libre2)
|
||||||
|
add_subdirectory (libboost-threadpool)
|
||||||
|
|
||||||
|
IF (NOT AARCH64)
|
||||||
|
add_subdirectory (libcpuid)
|
||||||
|
ENDIF()
|
5
contrib/libboost-threadpool/COPYING
Normal file
5
contrib/libboost-threadpool/COPYING
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
|
||||||
|
Use, modification, and distribution are subject to the
|
||||||
|
Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
23
contrib/libboost-threadpool/LICENSE_1_0.txt
Normal file
23
contrib/libboost-threadpool/LICENSE_1_0.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
28
contrib/libboost-threadpool/threadpool.hpp
Normal file
28
contrib/libboost-threadpool/threadpool.hpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Main include.
|
||||||
|
*
|
||||||
|
* This is the only file you have to include in order to use the
|
||||||
|
* complete threadpool library.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include "threadpool/future.hpp"
|
||||||
|
#include "threadpool/pool.hpp"
|
||||||
|
|
||||||
|
#include "threadpool/pool_adaptors.hpp"
|
||||||
|
#include "threadpool/task_adaptors.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
#endif // THREADPOOL_HPP_INCLUDED
|
||||||
|
|
215
contrib/libboost-threadpool/threadpool/detail/future.hpp
Normal file
215
contrib/libboost-threadpool/threadpool/detail/future.hpp
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief TODO.
|
||||||
|
*
|
||||||
|
* TODO.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_DETAIL_FUTURE_IMPL_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_DETAIL_FUTURE_IMPL_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
#include "locking_ptr.hpp"
|
||||||
|
|
||||||
|
#include <boost/smart_ptr.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/condition.hpp>
|
||||||
|
#include <boost/thread/xtime.hpp>
|
||||||
|
#include <boost/utility/result_of.hpp>
|
||||||
|
#include <boost/static_assert.hpp>
|
||||||
|
#include <boost/type_traits.hpp>
|
||||||
|
|
||||||
|
namespace boost { namespace threadpool { namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class Result>
|
||||||
|
class future_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Result const & result_type; //!< Indicates the functor's result type.
|
||||||
|
|
||||||
|
typedef Result future_result_type; //!< Indicates the future's result type.
|
||||||
|
typedef future_impl<future_result_type> future_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
volatile bool m_ready;
|
||||||
|
volatile future_result_type m_result;
|
||||||
|
|
||||||
|
mutable mutex m_monitor;
|
||||||
|
mutable condition m_condition_ready;
|
||||||
|
|
||||||
|
volatile bool m_is_cancelled;
|
||||||
|
volatile bool m_executing;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
future_impl()
|
||||||
|
: m_ready(false)
|
||||||
|
, m_is_cancelled(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ready() const volatile
|
||||||
|
{
|
||||||
|
return m_ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait() const volatile
|
||||||
|
{
|
||||||
|
const future_type* self = const_cast<const future_type*>(this);
|
||||||
|
mutex::scoped_lock lock(self->m_monitor);
|
||||||
|
|
||||||
|
while(!m_ready)
|
||||||
|
{
|
||||||
|
self->m_condition_ready.wait(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool timed_wait(boost::xtime const & timestamp) const
|
||||||
|
{
|
||||||
|
const future_type* self = const_cast<const future_type*>(this);
|
||||||
|
mutex::scoped_lock lock(self->m_monitor);
|
||||||
|
|
||||||
|
while(!m_ready)
|
||||||
|
{
|
||||||
|
if(!self->m_condition_ready.timed_wait(lock, timestamp)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_type operator()() const volatile
|
||||||
|
{
|
||||||
|
wait();
|
||||||
|
/*
|
||||||
|
if( throw_exception_ != 0 )
|
||||||
|
{
|
||||||
|
throw_exception_( this );
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return *(const_cast<const future_result_type*>(&m_result));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void set_value(future_result_type const & r) volatile
|
||||||
|
{
|
||||||
|
locking_ptr<future_type, mutex> lockedThis(*this, m_monitor);
|
||||||
|
if(!m_ready && !m_is_cancelled)
|
||||||
|
{
|
||||||
|
lockedThis->m_result = r;
|
||||||
|
lockedThis->m_ready = true;
|
||||||
|
lockedThis->m_condition_ready.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
template<class E> void set_exception() // throw()
|
||||||
|
{
|
||||||
|
m_impl->template set_exception<E>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class E> void set_exception( char const * what ) // throw()
|
||||||
|
{
|
||||||
|
m_impl->template set_exception<E>( what );
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
bool cancel() volatile
|
||||||
|
{
|
||||||
|
if(!m_ready || m_executing)
|
||||||
|
{
|
||||||
|
m_is_cancelled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool is_cancelled() const volatile
|
||||||
|
{
|
||||||
|
return m_is_cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void set_execution_status(bool executing) volatile
|
||||||
|
{
|
||||||
|
m_executing = executing;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<
|
||||||
|
template <typename> class Future,
|
||||||
|
typename Function
|
||||||
|
>
|
||||||
|
class future_impl_task_func
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef void result_type; //!< Indicates the functor's result type.
|
||||||
|
|
||||||
|
typedef Function function_type; //!< Indicates the function's type.
|
||||||
|
typedef typename result_of<function_type()>::type future_result_type; //!< Indicates the future's result type.
|
||||||
|
typedef Future<future_result_type> future_type; //!< Indicates the future's type.
|
||||||
|
|
||||||
|
// The task is required to be a nullary function.
|
||||||
|
BOOST_STATIC_ASSERT(function_traits<function_type()>::arity == 0);
|
||||||
|
|
||||||
|
// The task function's result type is required not to be void.
|
||||||
|
BOOST_STATIC_ASSERT(!is_void<future_result_type>::value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
function_type m_function;
|
||||||
|
shared_ptr<future_type> m_future;
|
||||||
|
|
||||||
|
public:
|
||||||
|
future_impl_task_func(function_type const & function, shared_ptr<future_type> const & future)
|
||||||
|
: m_function(function)
|
||||||
|
, m_future(future)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
if(m_function)
|
||||||
|
{
|
||||||
|
m_future->set_execution_status(true);
|
||||||
|
if(!m_future->is_cancelled())
|
||||||
|
{
|
||||||
|
// TODO future exeception handling
|
||||||
|
m_future->set_value(m_function());
|
||||||
|
}
|
||||||
|
m_future->set_execution_status(false); // TODO consider exceptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} } } // namespace boost::threadpool::detail
|
||||||
|
|
||||||
|
#endif // THREADPOOL_DETAIL_FUTURE_IMPL_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
102
contrib/libboost-threadpool/threadpool/detail/locking_ptr.hpp
Normal file
102
contrib/libboost-threadpool/threadpool/detail/locking_ptr.hpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief The locking_ptr is smart pointer with a scoped locking mechanism.
|
||||||
|
*
|
||||||
|
* The class is a wrapper for a volatile pointer. It enables synchronized access to the
|
||||||
|
* internal pointer by locking the passed mutex.
|
||||||
|
* locking_ptr is based on Andrei Alexandrescu's LockingPtr. For more information
|
||||||
|
* see article "volatile - Multithreaded Programmer's Best Friend" by A. Alexandrescu.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_DETAIL_LOCKING_PTR_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_DETAIL_LOCKING_PTR_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/utility.hpp>
|
||||||
|
|
||||||
|
// Support for old boost::thread
|
||||||
|
//**********************************************
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#ifndef BOOST_THREAD_MUTEX_HPP
|
||||||
|
#include <boost/thread/detail/lock.hpp>
|
||||||
|
#endif
|
||||||
|
//**********************************************
|
||||||
|
|
||||||
|
namespace boost { namespace threadpool { namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/*! \brief Smart pointer with a scoped locking mechanism.
|
||||||
|
*
|
||||||
|
* This class is a wrapper for a volatile pointer. It enables synchronized access to the
|
||||||
|
* internal pointer by locking the passed mutex.
|
||||||
|
*/
|
||||||
|
template <typename T, typename Mutex>
|
||||||
|
class locking_ptr
|
||||||
|
: private noncopyable
|
||||||
|
{
|
||||||
|
T* m_obj; //!< The instance pointer.
|
||||||
|
Mutex & m_mutex; //!< Mutex is used for scoped locking.
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
locking_ptr(volatile T& obj, const volatile Mutex& mtx)
|
||||||
|
: m_obj(const_cast<T*>(&obj))
|
||||||
|
, m_mutex(*const_cast<Mutex*>(&mtx))
|
||||||
|
{
|
||||||
|
// Lock mutex
|
||||||
|
|
||||||
|
#ifndef BOOST_THREAD_MUTEX_HPP
|
||||||
|
// Support for old boost::thread
|
||||||
|
boost::detail::thread::lock_ops<Mutex>::lock(m_mutex);
|
||||||
|
#else
|
||||||
|
m_mutex.lock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Destructor.
|
||||||
|
~locking_ptr()
|
||||||
|
{
|
||||||
|
// Unlock mutex
|
||||||
|
#ifndef BOOST_THREAD_MUTEX_HPP
|
||||||
|
// Support for old boost::thread
|
||||||
|
boost::detail::thread::lock_ops<Mutex>::unlock(m_mutex);
|
||||||
|
#else
|
||||||
|
m_mutex.unlock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns a reference to the stored instance.
|
||||||
|
* \return The instance's reference.
|
||||||
|
*/
|
||||||
|
T& operator*() const
|
||||||
|
{
|
||||||
|
return *m_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns a pointer to the stored instance.
|
||||||
|
* \return The instance's pointer.
|
||||||
|
*/
|
||||||
|
T* operator->() const
|
||||||
|
{
|
||||||
|
return m_obj;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} } } // namespace boost::threadpool::detail
|
||||||
|
|
||||||
|
|
||||||
|
#endif // THREADPOOL_DETAIL_LOCKING_PTR_HPP_INCLUDED
|
||||||
|
|
453
contrib/libboost-threadpool/threadpool/detail/pool_core.hpp
Normal file
453
contrib/libboost-threadpool/threadpool/detail/pool_core.hpp
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Thread pool core.
|
||||||
|
*
|
||||||
|
* This file contains the threadpool's core class: pool<Task, SchedulingPolicy>.
|
||||||
|
*
|
||||||
|
* Thread pools are a mechanism for asynchronous and parallel processing
|
||||||
|
* within the same process. The pool class provides a convenient way
|
||||||
|
* for dispatching asynchronous tasks as functions objects. The scheduling
|
||||||
|
* of these tasks can be easily controlled by using customized schedulers.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_POOL_CORE_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_POOL_CORE_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "locking_ptr.hpp"
|
||||||
|
#include "worker_thread.hpp"
|
||||||
|
|
||||||
|
#include "../task_adaptors.hpp"
|
||||||
|
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
#include <boost/thread/exceptions.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/condition.hpp>
|
||||||
|
#include <boost/smart_ptr.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/static_assert.hpp>
|
||||||
|
#include <boost/type_traits.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
/// The namespace threadpool contains a thread pool and related utility classes.
|
||||||
|
namespace boost { namespace threadpool { namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/*! \brief Thread pool.
|
||||||
|
*
|
||||||
|
* Thread pools are a mechanism for asynchronous and parallel processing
|
||||||
|
* within the same process. The pool class provides a convenient way
|
||||||
|
* for dispatching asynchronous tasks as functions objects. The scheduling
|
||||||
|
* of these tasks can be easily controlled by using customized schedulers.
|
||||||
|
* A task must not throw an exception.
|
||||||
|
*
|
||||||
|
* A pool_impl is DefaultConstructible and NonCopyable.
|
||||||
|
*
|
||||||
|
* \param Task A function object which implements the operator 'void operator() (void) const'. The operator () is called by the pool to execute the task. Exceptions are ignored.
|
||||||
|
* \param Scheduler A task container which determines how tasks are scheduled. It is guaranteed that this container is accessed only by one thread at a time. The scheduler shall not throw exceptions.
|
||||||
|
*
|
||||||
|
* \remarks The pool class is thread-safe.
|
||||||
|
*
|
||||||
|
* \see Tasks: task_func, prio_task_func
|
||||||
|
* \see Scheduling policies: fifo_scheduler, lifo_scheduler, prio_scheduler
|
||||||
|
*/
|
||||||
|
template <
|
||||||
|
typename Task,
|
||||||
|
|
||||||
|
template <typename> class SchedulingPolicy,
|
||||||
|
template <typename> class SizePolicy,
|
||||||
|
template <typename> class SizePolicyController,
|
||||||
|
template <typename> class ShutdownPolicy
|
||||||
|
>
|
||||||
|
class pool_core
|
||||||
|
: public enable_shared_from_this< pool_core<Task, SchedulingPolicy, SizePolicy, SizePolicyController, ShutdownPolicy > >
|
||||||
|
, private noncopyable
|
||||||
|
{
|
||||||
|
|
||||||
|
public: // Type definitions
|
||||||
|
typedef Task task_type; //!< Indicates the task's type.
|
||||||
|
typedef SchedulingPolicy<task_type> scheduler_type; //!< Indicates the scheduler's type.
|
||||||
|
typedef pool_core<Task,
|
||||||
|
SchedulingPolicy,
|
||||||
|
SizePolicy,
|
||||||
|
SizePolicyController,
|
||||||
|
ShutdownPolicy > pool_type; //!< Indicates the thread pool's type.
|
||||||
|
typedef SizePolicy<pool_type> size_policy_type; //!< Indicates the sizer's type.
|
||||||
|
//typedef typename size_policy_type::size_controller size_controller_type;
|
||||||
|
|
||||||
|
typedef SizePolicyController<pool_type> size_controller_type;
|
||||||
|
|
||||||
|
// typedef SizePolicy<pool_type>::size_controller size_controller_type;
|
||||||
|
typedef ShutdownPolicy<pool_type> shutdown_policy_type;//!< Indicates the shutdown policy's type.
|
||||||
|
|
||||||
|
typedef worker_thread<pool_type> worker_type;
|
||||||
|
|
||||||
|
// The task is required to be a nullary function.
|
||||||
|
BOOST_STATIC_ASSERT(function_traits<task_type()>::arity == 0);
|
||||||
|
|
||||||
|
// The task function's result type is required to be void.
|
||||||
|
BOOST_STATIC_ASSERT(is_void<typename result_of<task_type()>::type >::value);
|
||||||
|
|
||||||
|
|
||||||
|
private: // Friends
|
||||||
|
friend class worker_thread<pool_type>;
|
||||||
|
|
||||||
|
#if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x580) // Tested with CC: Sun C++ 5.8 Patch 121018-08 2006/12/06
|
||||||
|
friend class SizePolicy;
|
||||||
|
friend class ShutdownPolicy;
|
||||||
|
#else
|
||||||
|
friend class SizePolicy<pool_type>;
|
||||||
|
friend class ShutdownPolicy<pool_type>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private: // The following members may be accessed by _multiple_ threads at the same time:
|
||||||
|
volatile size_t m_worker_count;
|
||||||
|
volatile size_t m_target_worker_count;
|
||||||
|
volatile size_t m_active_worker_count;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private: // The following members are accessed only by _one_ thread at the same time:
|
||||||
|
scheduler_type m_scheduler;
|
||||||
|
scoped_ptr<size_policy_type> m_size_policy; // is never null
|
||||||
|
|
||||||
|
bool m_terminate_all_workers; // Indicates if termination of all workers was triggered.
|
||||||
|
std::vector<shared_ptr<worker_type> > m_terminated_workers; // List of workers which are terminated but not fully destructed.
|
||||||
|
|
||||||
|
private: // The following members are implemented thread-safe:
|
||||||
|
mutable recursive_mutex m_monitor;
|
||||||
|
mutable condition m_worker_idle_or_terminated_event; // A worker is idle or was terminated.
|
||||||
|
mutable condition m_task_or_terminate_workers_event; // Task is available OR total worker count should be reduced.
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
pool_core()
|
||||||
|
: m_worker_count(0)
|
||||||
|
, m_target_worker_count(0)
|
||||||
|
, m_active_worker_count(0)
|
||||||
|
, m_terminate_all_workers(false)
|
||||||
|
{
|
||||||
|
pool_type volatile & self_ref = *this;
|
||||||
|
m_size_policy.reset(new size_policy_type(self_ref));
|
||||||
|
|
||||||
|
m_scheduler.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Destructor.
|
||||||
|
~pool_core()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Gets the size controller which manages the number of threads in the pool.
|
||||||
|
* \return The size controller.
|
||||||
|
* \see SizePolicy
|
||||||
|
*/
|
||||||
|
size_controller_type size_controller()
|
||||||
|
{
|
||||||
|
return size_controller_type(*m_size_policy, this->shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Gets the number of threads in the pool.
|
||||||
|
* \return The number of threads.
|
||||||
|
*/
|
||||||
|
size_t size() const volatile
|
||||||
|
{
|
||||||
|
return m_worker_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO is only called once
|
||||||
|
void shutdown()
|
||||||
|
{
|
||||||
|
ShutdownPolicy<pool_type>::shutdown(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Schedules a task for asynchronous execution. The task will be executed once only.
|
||||||
|
* \param task The task function object. It should not throw execeptions.
|
||||||
|
* \return true, if the task could be scheduled and false otherwise.
|
||||||
|
*/
|
||||||
|
bool schedule(task_type const & task) volatile
|
||||||
|
{
|
||||||
|
locking_ptr<pool_type, recursive_mutex> lockedThis(*this, m_monitor);
|
||||||
|
|
||||||
|
if(lockedThis->m_scheduler.push(task))
|
||||||
|
{
|
||||||
|
lockedThis->m_task_or_terminate_workers_event.notify_one();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns the number of tasks which are currently executed.
|
||||||
|
* \return The number of active tasks.
|
||||||
|
*/
|
||||||
|
size_t active() const volatile
|
||||||
|
{
|
||||||
|
return m_active_worker_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns the number of tasks which are ready for execution.
|
||||||
|
* \return The number of pending tasks.
|
||||||
|
*/
|
||||||
|
size_t pending() const volatile
|
||||||
|
{
|
||||||
|
locking_ptr<const pool_type, recursive_mutex> lockedThis(*this, m_monitor);
|
||||||
|
return lockedThis->m_scheduler.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Removes all pending tasks from the pool's scheduler.
|
||||||
|
*/
|
||||||
|
void clear() volatile
|
||||||
|
{
|
||||||
|
locking_ptr<pool_type, recursive_mutex> lockedThis(*this, m_monitor);
|
||||||
|
lockedThis->m_scheduler.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Indicates that there are no tasks pending.
|
||||||
|
* \return true if there are no tasks ready for execution.
|
||||||
|
* \remarks This function is more efficient that the check 'pending() == 0'.
|
||||||
|
*/
|
||||||
|
bool empty() const volatile
|
||||||
|
{
|
||||||
|
locking_ptr<const pool_type, recursive_mutex> lockedThis(*this, m_monitor);
|
||||||
|
return lockedThis->m_scheduler.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! The current thread of execution is blocked until the sum of all active
|
||||||
|
* and pending tasks is equal or less than a given threshold.
|
||||||
|
* \param task_threshold The maximum number of tasks in pool and scheduler.
|
||||||
|
*/
|
||||||
|
void wait(size_t const task_threshold = 0) const volatile
|
||||||
|
{
|
||||||
|
const pool_type* self = const_cast<const pool_type*>(this);
|
||||||
|
recursive_mutex::scoped_lock lock(self->m_monitor);
|
||||||
|
|
||||||
|
if(0 == task_threshold)
|
||||||
|
{
|
||||||
|
while(0 != self->m_active_worker_count || !self->m_scheduler.empty())
|
||||||
|
{
|
||||||
|
self->m_worker_idle_or_terminated_event.wait(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while(task_threshold < self->m_active_worker_count + self->m_scheduler.size())
|
||||||
|
{
|
||||||
|
self->m_worker_idle_or_terminated_event.wait(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! The current thread of execution is blocked until the timestamp is met
|
||||||
|
* or the sum of all active and pending tasks is equal or less
|
||||||
|
* than a given threshold.
|
||||||
|
* \param timestamp The time when function returns at the latest.
|
||||||
|
* \param task_threshold The maximum number of tasks in pool and scheduler.
|
||||||
|
* \return true if the task sum is equal or less than the threshold, false otherwise.
|
||||||
|
*/
|
||||||
|
bool wait(xtime const & timestamp, size_t const task_threshold = 0) const volatile
|
||||||
|
{
|
||||||
|
const pool_type* self = const_cast<const pool_type*>(this);
|
||||||
|
recursive_mutex::scoped_lock lock(self->m_monitor);
|
||||||
|
|
||||||
|
if(0 == task_threshold)
|
||||||
|
{
|
||||||
|
while(0 != self->m_active_worker_count || !self->m_scheduler.empty())
|
||||||
|
{
|
||||||
|
if(!self->m_worker_idle_or_terminated_event.timed_wait(lock, timestamp)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while(task_threshold < self->m_active_worker_count + self->m_scheduler.size())
|
||||||
|
{
|
||||||
|
if(!self->m_worker_idle_or_terminated_event.timed_wait(lock, timestamp)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
void terminate_all_workers(bool const wait) volatile
|
||||||
|
{
|
||||||
|
pool_type* self = const_cast<pool_type*>(this);
|
||||||
|
recursive_mutex::scoped_lock lock(self->m_monitor);
|
||||||
|
|
||||||
|
self->m_terminate_all_workers = true;
|
||||||
|
|
||||||
|
m_target_worker_count = 0;
|
||||||
|
self->m_task_or_terminate_workers_event.notify_all();
|
||||||
|
|
||||||
|
if(wait)
|
||||||
|
{
|
||||||
|
while(m_worker_count > 0)
|
||||||
|
{
|
||||||
|
self->m_worker_idle_or_terminated_event.wait(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(typename std::vector<shared_ptr<worker_type> >::iterator it = self->m_terminated_workers.begin();
|
||||||
|
it != self->m_terminated_workers.end();
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
(*it)->join();
|
||||||
|
}
|
||||||
|
self->m_terminated_workers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Changes the number of worker threads in the pool. The resizing
|
||||||
|
* is handled by the SizePolicy.
|
||||||
|
* \param threads The new number of worker threads.
|
||||||
|
* \return true, if pool will be resized and false if not.
|
||||||
|
*/
|
||||||
|
bool resize(size_t const worker_count) volatile
|
||||||
|
{
|
||||||
|
locking_ptr<pool_type, recursive_mutex> lockedThis(*this, m_monitor);
|
||||||
|
|
||||||
|
if(!m_terminate_all_workers)
|
||||||
|
{
|
||||||
|
m_target_worker_count = worker_count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(m_worker_count <= m_target_worker_count)
|
||||||
|
{ // increase worker count
|
||||||
|
while(m_worker_count < m_target_worker_count)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
worker_thread<pool_type>::create_and_attach(lockedThis->shared_from_this());
|
||||||
|
m_worker_count++;
|
||||||
|
m_active_worker_count++;
|
||||||
|
}
|
||||||
|
catch(thread_resource_error)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // decrease worker count
|
||||||
|
lockedThis->m_task_or_terminate_workers_event.notify_all(); // TODO: Optimize number of notified workers
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// worker died with unhandled exception
|
||||||
|
void worker_died_unexpectedly(shared_ptr<worker_type> worker) volatile
|
||||||
|
{
|
||||||
|
locking_ptr<pool_type, recursive_mutex> lockedThis(*this, m_monitor);
|
||||||
|
|
||||||
|
m_worker_count--;
|
||||||
|
m_active_worker_count--;
|
||||||
|
lockedThis->m_worker_idle_or_terminated_event.notify_all();
|
||||||
|
|
||||||
|
if(m_terminate_all_workers)
|
||||||
|
{
|
||||||
|
lockedThis->m_terminated_workers.push_back(worker);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lockedThis->m_size_policy->worker_died_unexpectedly(m_worker_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker_destructed(shared_ptr<worker_type> worker) volatile
|
||||||
|
{
|
||||||
|
locking_ptr<pool_type, recursive_mutex> lockedThis(*this, m_monitor);
|
||||||
|
m_worker_count--;
|
||||||
|
m_active_worker_count--;
|
||||||
|
lockedThis->m_worker_idle_or_terminated_event.notify_all();
|
||||||
|
|
||||||
|
if(m_terminate_all_workers)
|
||||||
|
{
|
||||||
|
lockedThis->m_terminated_workers.push_back(worker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool execute_task() volatile
|
||||||
|
{
|
||||||
|
function0<void> task;
|
||||||
|
|
||||||
|
{ // fetch task
|
||||||
|
pool_type* lockedThis = const_cast<pool_type*>(this);
|
||||||
|
recursive_mutex::scoped_lock lock(lockedThis->m_monitor);
|
||||||
|
|
||||||
|
// decrease number of threads if necessary
|
||||||
|
if(m_worker_count > m_target_worker_count)
|
||||||
|
{
|
||||||
|
return false; // terminate worker
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// wait for tasks
|
||||||
|
while(lockedThis->m_scheduler.empty())
|
||||||
|
{
|
||||||
|
// decrease number of workers if necessary
|
||||||
|
if(m_worker_count > m_target_worker_count)
|
||||||
|
{
|
||||||
|
return false; // terminate worker
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_active_worker_count--;
|
||||||
|
lockedThis->m_worker_idle_or_terminated_event.notify_all();
|
||||||
|
lockedThis->m_task_or_terminate_workers_event.wait(lock);
|
||||||
|
m_active_worker_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task = lockedThis->m_scheduler.top();
|
||||||
|
lockedThis->m_scheduler.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// call task function
|
||||||
|
if(task)
|
||||||
|
{
|
||||||
|
task();
|
||||||
|
}
|
||||||
|
|
||||||
|
//guard->disable();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} } } // namespace boost::threadpool::detail
|
||||||
|
|
||||||
|
#endif // THREADPOOL_POOL_CORE_HPP_INCLUDED
|
@ -0,0 +1,65 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief TODO.
|
||||||
|
*
|
||||||
|
* TODO.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_DETAIL_SCOPE_GUARD_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_DETAIL_SCOPE_GUARD_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace boost { namespace threadpool { namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO documentation
|
||||||
|
class scope_guard
|
||||||
|
: private boost::noncopyable
|
||||||
|
{
|
||||||
|
function0<void> const m_function;
|
||||||
|
bool m_is_active;
|
||||||
|
|
||||||
|
public:
|
||||||
|
scope_guard(function0<void> const & call_on_exit)
|
||||||
|
: m_function(call_on_exit)
|
||||||
|
, m_is_active(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~scope_guard()
|
||||||
|
{
|
||||||
|
if(m_is_active && m_function)
|
||||||
|
{
|
||||||
|
m_function();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable()
|
||||||
|
{
|
||||||
|
m_is_active = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} } } // namespace boost::threadpool::detail
|
||||||
|
|
||||||
|
#endif // THREADPOOL_DETAIL_SCOPE_GUARD_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
115
contrib/libboost-threadpool/threadpool/detail/worker_thread.hpp
Normal file
115
contrib/libboost-threadpool/threadpool/detail/worker_thread.hpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Thread pool worker.
|
||||||
|
*
|
||||||
|
* The worker thread instance is attached to a pool
|
||||||
|
* and executes tasks of this pool.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_DETAIL_WORKER_THREAD_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_DETAIL_WORKER_THREAD_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
#include "scope_guard.hpp"
|
||||||
|
|
||||||
|
#include <boost/smart_ptr.hpp>
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
#include <boost/thread/exceptions.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace boost { namespace threadpool { namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
/*! \brief Thread pool worker.
|
||||||
|
*
|
||||||
|
* A worker_thread represents a thread of execution. The worker is attached to a
|
||||||
|
* thread pool and processes tasks of that pool. The lifetime of the worker and its
|
||||||
|
* internal boost::thread is managed automatically.
|
||||||
|
*
|
||||||
|
* This class is a helper class and cannot be constructed or accessed directly.
|
||||||
|
*
|
||||||
|
* \see pool_core
|
||||||
|
*/
|
||||||
|
template <typename Pool>
|
||||||
|
class worker_thread
|
||||||
|
: public enable_shared_from_this< worker_thread<Pool> >
|
||||||
|
, private noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Pool pool_type; //!< Indicates the pool's type.
|
||||||
|
|
||||||
|
private:
|
||||||
|
shared_ptr<pool_type> m_pool; //!< Pointer to the pool which created the worker.
|
||||||
|
shared_ptr<boost::thread> m_thread; //!< Pointer to the thread which executes the run loop.
|
||||||
|
|
||||||
|
|
||||||
|
/*! Constructs a new worker.
|
||||||
|
* \param pool Pointer to it's parent pool.
|
||||||
|
* \see function create_and_attach
|
||||||
|
*/
|
||||||
|
worker_thread(shared_ptr<pool_type> const & pool)
|
||||||
|
: m_pool(pool)
|
||||||
|
{
|
||||||
|
assert(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Notifies that an exception occurred in the run loop.
|
||||||
|
*/
|
||||||
|
void died_unexpectedly()
|
||||||
|
{
|
||||||
|
m_pool->worker_died_unexpectedly(this->shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Executes pool's tasks sequentially.
|
||||||
|
*/
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
scope_guard notify_exception(bind(&worker_thread::died_unexpectedly, this));
|
||||||
|
|
||||||
|
while(m_pool->execute_task()) {}
|
||||||
|
|
||||||
|
notify_exception.disable();
|
||||||
|
m_pool->worker_destructed(this->shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Joins the worker's thread.
|
||||||
|
*/
|
||||||
|
void join()
|
||||||
|
{
|
||||||
|
m_thread->join();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Constructs a new worker thread and attaches it to the pool.
|
||||||
|
* \param pool Pointer to the pool.
|
||||||
|
*/
|
||||||
|
static void create_and_attach(shared_ptr<pool_type> const & pool)
|
||||||
|
{
|
||||||
|
shared_ptr<worker_thread> worker(new worker_thread(pool));
|
||||||
|
if(worker)
|
||||||
|
{
|
||||||
|
worker->m_thread.reset(new boost::thread(bind(&worker_thread::run, worker)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} } } // namespace boost::threadpool::detail
|
||||||
|
|
||||||
|
#endif // THREADPOOL_DETAIL_WORKER_THREAD_HPP_INCLUDED
|
||||||
|
|
144
contrib/libboost-threadpool/threadpool/future.hpp
Normal file
144
contrib/libboost-threadpool/threadpool/future.hpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief TODO.
|
||||||
|
*
|
||||||
|
* TODO.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_FUTURE_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_FUTURE_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "detail/future.hpp"
|
||||||
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
|
||||||
|
//#include "pool.hpp"
|
||||||
|
//#include <boost/utility.hpp>
|
||||||
|
|
||||||
|
//#include <boost/thread/mutex.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace boost { namespace threadpool
|
||||||
|
{
|
||||||
|
|
||||||
|
/*! \brief Experimental. Do not use in production code. TODO.
|
||||||
|
*
|
||||||
|
* TODO Future
|
||||||
|
*
|
||||||
|
* \see TODO
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
template<class Result>
|
||||||
|
class future
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
shared_ptr<detail::future_impl<Result> > m_impl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef Result const & result_type; //!< Indicates the functor's result type.
|
||||||
|
typedef Result future_result_type; //!< Indicates the future's result type.
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
future()
|
||||||
|
: m_impl(new detail::future_impl<future_result_type>()) // TODO remove this
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// only for internal usage
|
||||||
|
future(shared_ptr<detail::future_impl<Result> > const & impl)
|
||||||
|
: m_impl(impl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ready() const
|
||||||
|
{
|
||||||
|
return m_impl->ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait() const
|
||||||
|
{
|
||||||
|
m_impl->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool timed_wait(boost::xtime const & timestamp) const
|
||||||
|
{
|
||||||
|
return m_impl->timed_wait(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
result_type operator()() // throw( thread::cancelation_exception, ... )
|
||||||
|
{
|
||||||
|
return (*m_impl)();
|
||||||
|
}
|
||||||
|
|
||||||
|
result_type get() // throw( thread::cancelation_exception, ... )
|
||||||
|
{
|
||||||
|
return (*m_impl)();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cancel()
|
||||||
|
{
|
||||||
|
return m_impl->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_cancelled() const
|
||||||
|
{
|
||||||
|
return m_impl->is_cancelled();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class Pool, class Function>
|
||||||
|
typename disable_if <
|
||||||
|
is_void< typename result_of< Function() >::type >,
|
||||||
|
future< typename result_of< Function() >::type >
|
||||||
|
>::type
|
||||||
|
schedule(Pool& pool, const Function& task)
|
||||||
|
{
|
||||||
|
typedef typename result_of< Function() >::type future_result_type;
|
||||||
|
|
||||||
|
// create future impl and future
|
||||||
|
shared_ptr<detail::future_impl<future_result_type> > impl(new detail::future_impl<future_result_type>);
|
||||||
|
future <future_result_type> res(impl);
|
||||||
|
|
||||||
|
// schedule future impl
|
||||||
|
pool.schedule(detail::future_impl_task_func<detail::future_impl, Function>(task, impl));
|
||||||
|
|
||||||
|
// return future
|
||||||
|
return res;
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
if(pool->schedule(bind(&Future::run, future)))
|
||||||
|
{
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// construct empty future
|
||||||
|
return error_future;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} } // namespace boost::threadpool
|
||||||
|
|
||||||
|
#endif // THREADPOOL_FUTURE_HPP_INCLUDED
|
||||||
|
|
232
contrib/libboost-threadpool/threadpool/pool.hpp
Normal file
232
contrib/libboost-threadpool/threadpool/pool.hpp
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Thread pool core.
|
||||||
|
*
|
||||||
|
* This file contains the threadpool's core class: pool<Task, SchedulingPolicy>.
|
||||||
|
*
|
||||||
|
* Thread pools are a mechanism for asynchronous and parallel processing
|
||||||
|
* within the same process. The pool class provides a convenient way
|
||||||
|
* for dispatching asynchronous tasks as functions objects. The scheduling
|
||||||
|
* of these tasks can be easily controlled by using customized schedulers.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_POOL_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_POOL_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/ref.hpp>
|
||||||
|
|
||||||
|
#include "detail/pool_core.hpp"
|
||||||
|
|
||||||
|
#include "task_adaptors.hpp"
|
||||||
|
|
||||||
|
#include "detail/locking_ptr.hpp"
|
||||||
|
|
||||||
|
#include "scheduling_policies.hpp"
|
||||||
|
#include "size_policies.hpp"
|
||||||
|
#include "shutdown_policies.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// The namespace threadpool contains a thread pool and related utility classes.
|
||||||
|
namespace boost { namespace threadpool
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Thread pool.
|
||||||
|
*
|
||||||
|
* Thread pools are a mechanism for asynchronous and parallel processing
|
||||||
|
* within the same process. The pool class provides a convenient way
|
||||||
|
* for dispatching asynchronous tasks as functions objects. The scheduling
|
||||||
|
* of these tasks can be easily controlled by using customized schedulers.
|
||||||
|
* A task must not throw an exception.
|
||||||
|
*
|
||||||
|
* A pool is DefaultConstructible, CopyConstructible and Assignable.
|
||||||
|
* It has reference semantics; all copies of the same pool are equivalent and interchangeable.
|
||||||
|
* All operations on a pool except assignment are strongly thread safe or sequentially consistent;
|
||||||
|
* that is, the behavior of concurrent calls is as if the calls have been issued sequentially in an unspecified order.
|
||||||
|
*
|
||||||
|
* \param Task A function object which implements the operator 'void operator() (void) const'. The operator () is called by the pool to execute the task. Exceptions are ignored.
|
||||||
|
* \param SchedulingPolicy A task container which determines how tasks are scheduled. It is guaranteed that this container is accessed only by one thread at a time. The scheduler shall not throw exceptions.
|
||||||
|
*
|
||||||
|
* \remarks The pool class is thread-safe.
|
||||||
|
*
|
||||||
|
* \see Tasks: task_func, prio_task_func
|
||||||
|
* \see Scheduling policies: fifo_scheduler, lifo_scheduler, prio_scheduler
|
||||||
|
*/
|
||||||
|
template <
|
||||||
|
typename Task = task_func,
|
||||||
|
template <typename> class SchedulingPolicy = fifo_scheduler,
|
||||||
|
template <typename> class SizePolicy = static_size,
|
||||||
|
template <typename> class SizePolicyController = resize_controller,
|
||||||
|
template <typename> class ShutdownPolicy = wait_for_all_tasks
|
||||||
|
>
|
||||||
|
class thread_pool
|
||||||
|
{
|
||||||
|
typedef detail::pool_core<Task,
|
||||||
|
SchedulingPolicy,
|
||||||
|
SizePolicy,
|
||||||
|
SizePolicyController,
|
||||||
|
ShutdownPolicy> pool_core_type;
|
||||||
|
shared_ptr<pool_core_type> m_core; // pimpl idiom
|
||||||
|
shared_ptr<void> m_shutdown_controller; // If the last pool holding a pointer to the core is deleted the controller shuts the pool down.
|
||||||
|
|
||||||
|
public: // Type definitions
|
||||||
|
typedef Task task_type; //!< Indicates the task's type.
|
||||||
|
typedef SchedulingPolicy<task_type> scheduler_type; //!< Indicates the scheduler's type.
|
||||||
|
/* typedef thread_pool<Task,
|
||||||
|
SchedulingPolicy,
|
||||||
|
SizePolicy,
|
||||||
|
ShutdownPolicy > pool_type; //!< Indicates the thread pool's type.
|
||||||
|
*/
|
||||||
|
typedef SizePolicy<pool_core_type> size_policy_type;
|
||||||
|
typedef SizePolicyController<pool_core_type> size_controller_type;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Constructor.
|
||||||
|
* \param initial_threads The pool is immediately resized to set the specified number of threads. The pool's actual number threads depends on the SizePolicy.
|
||||||
|
*/
|
||||||
|
thread_pool(size_t initial_threads = 0)
|
||||||
|
: m_core(new pool_core_type)
|
||||||
|
, m_shutdown_controller(static_cast<void*>(0), bind(&pool_core_type::shutdown, m_core))
|
||||||
|
{
|
||||||
|
size_policy_type::init(*m_core, initial_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Gets the size controller which manages the number of threads in the pool.
|
||||||
|
* \return The size controller.
|
||||||
|
* \see SizePolicy
|
||||||
|
*/
|
||||||
|
size_controller_type size_controller()
|
||||||
|
{
|
||||||
|
return m_core->size_controller();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Gets the number of threads in the pool.
|
||||||
|
* \return The number of threads.
|
||||||
|
*/
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return m_core->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Schedules a task for asynchronous execution. The task will be executed once only.
|
||||||
|
* \param task The task function object. It should not throw execeptions.
|
||||||
|
* \return true, if the task could be scheduled and false otherwise.
|
||||||
|
*/
|
||||||
|
bool schedule(task_type const & task)
|
||||||
|
{
|
||||||
|
return m_core->schedule(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns the number of tasks which are currently executed.
|
||||||
|
* \return The number of active tasks.
|
||||||
|
*/
|
||||||
|
size_t active() const
|
||||||
|
{
|
||||||
|
return m_core->active();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Returns the number of tasks which are ready for execution.
|
||||||
|
* \return The number of pending tasks.
|
||||||
|
*/
|
||||||
|
size_t pending() const
|
||||||
|
{
|
||||||
|
return m_core->pending();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Removes all pending tasks from the pool's scheduler.
|
||||||
|
*/
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_core->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Indicates that there are no tasks pending.
|
||||||
|
* \return true if there are no tasks ready for execution.
|
||||||
|
* \remarks This function is more efficient that the check 'pending() == 0'.
|
||||||
|
*/
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return m_core->empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! The current thread of execution is blocked until the sum of all active
|
||||||
|
* and pending tasks is equal or less than a given threshold.
|
||||||
|
* \param task_threshold The maximum number of tasks in pool and scheduler.
|
||||||
|
*/
|
||||||
|
void wait(size_t task_threshold = 0) const
|
||||||
|
{
|
||||||
|
m_core->wait(task_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! The current thread of execution is blocked until the timestamp is met
|
||||||
|
* or the sum of all active and pending tasks is equal or less
|
||||||
|
* than a given threshold.
|
||||||
|
* \param timestamp The time when function returns at the latest.
|
||||||
|
* \param task_threshold The maximum number of tasks in pool and scheduler.
|
||||||
|
* \return true if the task sum is equal or less than the threshold, false otherwise.
|
||||||
|
*/
|
||||||
|
bool wait(xtime const & timestamp, size_t task_threshold = 0) const
|
||||||
|
{
|
||||||
|
return m_core->wait(timestamp, task_threshold);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Fifo pool.
|
||||||
|
*
|
||||||
|
* The pool's tasks are fifo scheduled task_func functors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef thread_pool<task_func, fifo_scheduler, static_size, resize_controller, wait_for_all_tasks> fifo_pool;
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Lifo pool.
|
||||||
|
*
|
||||||
|
* The pool's tasks are lifo scheduled task_func functors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef thread_pool<task_func, lifo_scheduler, static_size, resize_controller, wait_for_all_tasks> lifo_pool;
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Pool for prioritized task.
|
||||||
|
*
|
||||||
|
* The pool's tasks are prioritized prio_task_func functors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef thread_pool<prio_task_func, prio_scheduler, static_size, resize_controller, wait_for_all_tasks> prio_pool;
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief A standard pool.
|
||||||
|
*
|
||||||
|
* The pool's tasks are fifo scheduled task_func functors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef fifo_pool pool;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} } // namespace boost::threadpool
|
||||||
|
|
||||||
|
#endif // THREADPOOL_POOL_HPP_INCLUDED
|
70
contrib/libboost-threadpool/threadpool/pool_adaptors.hpp
Normal file
70
contrib/libboost-threadpool/threadpool/pool_adaptors.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Pool adaptors.
|
||||||
|
*
|
||||||
|
* This file contains an easy-to-use adaptor similar to a smart
|
||||||
|
* pointer for the pool class.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_POOL_ADAPTORS_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_POOL_ADAPTORS_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/smart_ptr.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace boost { namespace threadpool
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// TODO convenience scheduling function
|
||||||
|
/*! Schedules a Runnable for asynchronous execution. A Runnable is an arbitrary class with a run()
|
||||||
|
* member function. This a convenience shorthand for pool->schedule(bind(&Runnable::run, task_object)).
|
||||||
|
* \param
|
||||||
|
* \param obj The Runnable object. The member function run() will be exectued and should not throw execeptions.
|
||||||
|
* \return true, if the task could be scheduled and false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Pool, typename Runnable>
|
||||||
|
bool schedule(Pool& pool, shared_ptr<Runnable> const & obj)
|
||||||
|
{
|
||||||
|
return pool->schedule(bind(&Runnable::run, obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Schedules a task for asynchronous execution. The task will be executed once only.
|
||||||
|
* \param task The task function object.
|
||||||
|
*/
|
||||||
|
template<typename Pool>
|
||||||
|
typename enable_if <
|
||||||
|
is_void< typename result_of< typename Pool::task_type() >::type >,
|
||||||
|
bool
|
||||||
|
>::type
|
||||||
|
schedule(Pool& pool, typename Pool::task_type const & task)
|
||||||
|
{
|
||||||
|
return pool.schedule(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Pool>
|
||||||
|
typename enable_if <
|
||||||
|
is_void< typename result_of< typename Pool::task_type() >::type >,
|
||||||
|
bool
|
||||||
|
>::type
|
||||||
|
schedule(shared_ptr<Pool> const pool, typename Pool::task_type const & task)
|
||||||
|
{
|
||||||
|
return pool->schedule(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} } // namespace boost::threadpool
|
||||||
|
|
||||||
|
#endif // THREADPOOL_POOL_ADAPTORS_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
262
contrib/libboost-threadpool/threadpool/scheduling_policies.hpp
Normal file
262
contrib/libboost-threadpool/threadpool/scheduling_policies.hpp
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Task scheduling policies.
|
||||||
|
*
|
||||||
|
* This file contains some fundamental scheduling policies for the pool class.
|
||||||
|
* A scheduling policy is realized by a task container which controls the access to
|
||||||
|
* the tasks. Fundamentally the container determines the order the tasks are processed
|
||||||
|
* by the thread pool.
|
||||||
|
* The task containers need not to be thread-safe because they are used by the pool
|
||||||
|
* in thread-safe way.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_SCHEDULING_POLICIES_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_SCHEDULING_POLICIES_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include "task_adaptors.hpp"
|
||||||
|
|
||||||
|
namespace boost { namespace threadpool
|
||||||
|
{
|
||||||
|
|
||||||
|
/*! \brief SchedulingPolicy which implements FIFO ordering.
|
||||||
|
*
|
||||||
|
* This container implements a FIFO scheduling policy.
|
||||||
|
* The first task to be added to the scheduler will be the first to be removed.
|
||||||
|
* The processing proceeds sequentially in the same order.
|
||||||
|
* FIFO stands for "first in, first out".
|
||||||
|
*
|
||||||
|
* \param Task A function object which implements the operator()(void).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <typename Task = task_func>
|
||||||
|
class fifo_scheduler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Task task_type; //!< Indicates the scheduler's task type.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::deque<task_type> m_container; //!< Internal task container.
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Adds a new task to the scheduler.
|
||||||
|
* \param task The task object.
|
||||||
|
* \return true, if the task could be scheduled and false otherwise.
|
||||||
|
*/
|
||||||
|
bool push(task_type const & task)
|
||||||
|
{
|
||||||
|
m_container.push_back(task);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes the task which should be executed next.
|
||||||
|
*/
|
||||||
|
void pop()
|
||||||
|
{
|
||||||
|
m_container.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Gets the task which should be executed next.
|
||||||
|
* \return The task object to be executed.
|
||||||
|
*/
|
||||||
|
task_type const & top() const
|
||||||
|
{
|
||||||
|
return m_container.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Gets the current number of tasks in the scheduler.
|
||||||
|
* \return The number of tasks.
|
||||||
|
* \remarks Prefer empty() to size() == 0 to check if the scheduler is empty.
|
||||||
|
*/
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return m_container.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Checks if the scheduler is empty.
|
||||||
|
* \return true if the scheduler contains no tasks, false otherwise.
|
||||||
|
* \remarks Is more efficient than size() == 0.
|
||||||
|
*/
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return m_container.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes all tasks from the scheduler.
|
||||||
|
*/
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_container.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief SchedulingPolicy which implements LIFO ordering.
|
||||||
|
*
|
||||||
|
* This container implements a LIFO scheduling policy.
|
||||||
|
* The last task to be added to the scheduler will be the first to be removed.
|
||||||
|
* LIFO stands for "last in, first out".
|
||||||
|
*
|
||||||
|
* \param Task A function object which implements the operator()(void).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <typename Task = task_func>
|
||||||
|
class lifo_scheduler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Task task_type; //!< Indicates the scheduler's task type.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::deque<task_type> m_container; //!< Internal task container.
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Adds a new task to the scheduler.
|
||||||
|
* \param task The task object.
|
||||||
|
* \return true, if the task could be scheduled and false otherwise.
|
||||||
|
*/
|
||||||
|
bool push(task_type const & task)
|
||||||
|
{
|
||||||
|
m_container.push_front(task);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes the task which should be executed next.
|
||||||
|
*/
|
||||||
|
void pop()
|
||||||
|
{
|
||||||
|
m_container.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Gets the task which should be executed next.
|
||||||
|
* \return The task object to be executed.
|
||||||
|
*/
|
||||||
|
task_type const & top() const
|
||||||
|
{
|
||||||
|
return m_container.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Gets the current number of tasks in the scheduler.
|
||||||
|
* \return The number of tasks.
|
||||||
|
* \remarks Prefer empty() to size() == 0 to check if the scheduler is empty.
|
||||||
|
*/
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return m_container.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Checks if the scheduler is empty.
|
||||||
|
* \return true if the scheduler contains no tasks, false otherwise.
|
||||||
|
* \remarks Is more efficient than size() == 0.
|
||||||
|
*/
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return m_container.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes all tasks from the scheduler.
|
||||||
|
*/
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_container.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief SchedulingPolicy which implements prioritized ordering.
|
||||||
|
*
|
||||||
|
* This container implements a scheduling policy based on task priorities.
|
||||||
|
* The task with highest priority will be the first to be removed.
|
||||||
|
* It must be possible to compare two tasks using operator<.
|
||||||
|
*
|
||||||
|
* \param Task A function object which implements the operator() and operator<. operator< must be a partial ordering.
|
||||||
|
*
|
||||||
|
* \see prio_thread_func
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <typename Task = prio_task_func>
|
||||||
|
class prio_scheduler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Task task_type; //!< Indicates the scheduler's task type.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::priority_queue<task_type> m_container; //!< Internal task container.
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Adds a new task to the scheduler.
|
||||||
|
* \param task The task object.
|
||||||
|
* \return true, if the task could be scheduled and false otherwise.
|
||||||
|
*/
|
||||||
|
bool push(task_type const & task)
|
||||||
|
{
|
||||||
|
m_container.push(task);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes the task which should be executed next.
|
||||||
|
*/
|
||||||
|
void pop()
|
||||||
|
{
|
||||||
|
m_container.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Gets the task which should be executed next.
|
||||||
|
* \return The task object to be executed.
|
||||||
|
*/
|
||||||
|
task_type const & top() const
|
||||||
|
{
|
||||||
|
return m_container.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Gets the current number of tasks in the scheduler.
|
||||||
|
* \return The number of tasks.
|
||||||
|
* \remarks Prefer empty() to size() == 0 to check if the scheduler is empty.
|
||||||
|
*/
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return m_container.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Checks if the scheduler is empty.
|
||||||
|
* \return true if the scheduler contains no tasks, false otherwise.
|
||||||
|
* \remarks Is more efficient than size() == 0.
|
||||||
|
*/
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return m_container.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Removes all tasks from the scheduler.
|
||||||
|
*/
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
while(!m_container.empty())
|
||||||
|
{
|
||||||
|
m_container.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} } // namespace boost::threadpool
|
||||||
|
|
||||||
|
|
||||||
|
#endif // THREADPOOL_SCHEDULING_POLICIES_HPP_INCLUDED
|
||||||
|
|
83
contrib/libboost-threadpool/threadpool/shutdown_policies.hpp
Normal file
83
contrib/libboost-threadpool/threadpool/shutdown_policies.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Shutdown policies.
|
||||||
|
*
|
||||||
|
* This file contains shutdown policies for thread_pool.
|
||||||
|
* A shutdown policy controls the pool's behavior from the time
|
||||||
|
* when the pool is not referenced any longer.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_SHUTDOWN_POLICIES_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_SHUTDOWN_POLICIES_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// The namespace threadpool contains a thread pool and related utility classes.
|
||||||
|
namespace boost { namespace threadpool
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief ShutdownPolicy which waits for the completion of all tasks
|
||||||
|
* and the worker termination afterwards.
|
||||||
|
*
|
||||||
|
* \param Pool The pool's core type.
|
||||||
|
*/
|
||||||
|
template<typename Pool>
|
||||||
|
class wait_for_all_tasks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void shutdown(Pool& pool)
|
||||||
|
{
|
||||||
|
pool.wait();
|
||||||
|
pool.terminate_all_workers(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief ShutdownPolicy which waits for the completion of all active tasks
|
||||||
|
* and the worker termination afterwards.
|
||||||
|
*
|
||||||
|
* \param Pool The pool's core type.
|
||||||
|
*/
|
||||||
|
template<typename Pool>
|
||||||
|
class wait_for_active_tasks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void shutdown(Pool& pool)
|
||||||
|
{
|
||||||
|
pool.clear();
|
||||||
|
pool.wait();
|
||||||
|
pool.terminate_all_workers(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief ShutdownPolicy which does not wait for any tasks or worker termination.
|
||||||
|
*
|
||||||
|
* This policy does not wait for any tasks. Nevertheless all active tasks will be processed completely.
|
||||||
|
*
|
||||||
|
* \param Pool The pool's core type.
|
||||||
|
*/
|
||||||
|
template<typename Pool>
|
||||||
|
class immediately
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void shutdown(Pool& pool)
|
||||||
|
{
|
||||||
|
pool.clear();
|
||||||
|
pool.terminate_all_workers(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} } // namespace boost::threadpool
|
||||||
|
|
||||||
|
#endif // THREADPOOL_SHUTDOWN_POLICIES_HPP_INCLUDED
|
99
contrib/libboost-threadpool/threadpool/size_policies.hpp
Normal file
99
contrib/libboost-threadpool/threadpool/size_policies.hpp
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Size policies.
|
||||||
|
*
|
||||||
|
* This file contains size policies for thread_pool. A size
|
||||||
|
* policy controls the number of worker threads in the pool.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_SIZE_POLICIES_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_SIZE_POLICIES_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// The namespace threadpool contains a thread pool and related utility classes.
|
||||||
|
namespace boost { namespace threadpool
|
||||||
|
{
|
||||||
|
|
||||||
|
/*! \brief SizePolicyController which provides no functionality.
|
||||||
|
*
|
||||||
|
* \param Pool The pool's core type.
|
||||||
|
*/
|
||||||
|
template<typename Pool>
|
||||||
|
struct empty_controller
|
||||||
|
{
|
||||||
|
empty_controller(typename Pool::size_policy_type&, shared_ptr<Pool>) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief SizePolicyController which allows resizing.
|
||||||
|
*
|
||||||
|
* \param Pool The pool's core type.
|
||||||
|
*/
|
||||||
|
template< typename Pool >
|
||||||
|
class resize_controller
|
||||||
|
{
|
||||||
|
typedef typename Pool::size_policy_type size_policy_type;
|
||||||
|
reference_wrapper<size_policy_type> m_policy;
|
||||||
|
shared_ptr<Pool> m_pool; //!< to make sure that the pool is alive (the policy pointer is valid) as long as the controller exists
|
||||||
|
|
||||||
|
public:
|
||||||
|
resize_controller(size_policy_type& policy, shared_ptr<Pool> pool)
|
||||||
|
: m_policy(policy)
|
||||||
|
, m_pool(pool)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool resize(size_t worker_count)
|
||||||
|
{
|
||||||
|
return m_policy.get().resize(worker_count);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief SizePolicy which preserves the thread count.
|
||||||
|
*
|
||||||
|
* \param Pool The pool's core type.
|
||||||
|
*/
|
||||||
|
template<typename Pool>
|
||||||
|
class static_size
|
||||||
|
{
|
||||||
|
reference_wrapper<Pool volatile> m_pool;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void init(Pool& pool, size_t const worker_count)
|
||||||
|
{
|
||||||
|
pool.resize(worker_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_size(Pool volatile & pool)
|
||||||
|
: m_pool(pool)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool resize(size_t const worker_count)
|
||||||
|
{
|
||||||
|
return m_pool.get().resize(worker_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker_died_unexpectedly(size_t const new_worker_count)
|
||||||
|
{
|
||||||
|
m_pool.get().resize(new_worker_count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO this functions are not called yet
|
||||||
|
void task_scheduled() {}
|
||||||
|
void task_finished() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} } // namespace boost::threadpool
|
||||||
|
|
||||||
|
#endif // THREADPOOL_SIZE_POLICIES_HPP_INCLUDED
|
176
contrib/libboost-threadpool/threadpool/task_adaptors.hpp
Normal file
176
contrib/libboost-threadpool/threadpool/task_adaptors.hpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/*! \file
|
||||||
|
* \brief Task adaptors.
|
||||||
|
*
|
||||||
|
* This file contains adaptors for task function objects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2007 Philipp Henkel
|
||||||
|
*
|
||||||
|
* Use, modification, and distribution are subject to the
|
||||||
|
* Boost Software License, Version 1.0. (See accompanying file
|
||||||
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*
|
||||||
|
* http://threadpool.sourceforge.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef THREADPOOL_TASK_ADAPTERS_HPP_INCLUDED
|
||||||
|
#define THREADPOOL_TASK_ADAPTERS_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
|
||||||
|
#if BOOST_VERSION >= 105000
|
||||||
|
#ifndef TIME_UTC
|
||||||
|
#define TIME_UTC TIME_UTC_
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <boost/smart_ptr.hpp>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace boost { namespace threadpool
|
||||||
|
{
|
||||||
|
|
||||||
|
/*! \brief Standard task function object.
|
||||||
|
*
|
||||||
|
* This function object wraps a nullary function which returns void.
|
||||||
|
* The wrapped function is invoked by calling the operator ().
|
||||||
|
*
|
||||||
|
* \see boost function library
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef function0<void> task_func;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Prioritized task function object.
|
||||||
|
*
|
||||||
|
* This function object wraps a task_func object and binds a priority to it.
|
||||||
|
* prio_task_funcs can be compared using the operator < which realises a partial ordering.
|
||||||
|
* The wrapped task function is invoked by calling the operator ().
|
||||||
|
*
|
||||||
|
* \see prio_scheduler
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class prio_task_func
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
unsigned int m_priority; //!< The priority of the task's function.
|
||||||
|
task_func m_function; //!< The task's function.
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef void result_type; //!< Indicates the functor's result type.
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Constructor.
|
||||||
|
* \param priority The priority of the task.
|
||||||
|
* \param function The task's function object.
|
||||||
|
*/
|
||||||
|
prio_task_func(unsigned int const priority, task_func const & function)
|
||||||
|
: m_priority(priority)
|
||||||
|
, m_function(function)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Executes the task function.
|
||||||
|
*/
|
||||||
|
void operator() (void) const
|
||||||
|
{
|
||||||
|
if(m_function)
|
||||||
|
{
|
||||||
|
m_function();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Comparison operator which realises a partial ordering based on priorities.
|
||||||
|
* \param rhs The object to compare with.
|
||||||
|
* \return true if the priority of *this is less than right hand side's priority, false otherwise.
|
||||||
|
*/
|
||||||
|
bool operator< (const prio_task_func& rhs) const
|
||||||
|
{
|
||||||
|
return m_priority < rhs.m_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // prio_task_func
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Looped task function object.
|
||||||
|
*
|
||||||
|
* This function object wraps a boolean thread function object.
|
||||||
|
* The wrapped task function is invoked by calling the operator () and it is executed in regular
|
||||||
|
* time intervals until false is returned. The interval length may be zero.
|
||||||
|
* Please note that a pool's thread is engaged as long as the task is looped.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class looped_task_func
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
function0<bool> m_function; //!< The task's function.
|
||||||
|
unsigned int m_break_s; //!< Duration of breaks in seconds.
|
||||||
|
unsigned int m_break_ns; //!< Duration of breaks in nano seconds.
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef void result_type; //!< Indicates the functor's result type.
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! Constructor.
|
||||||
|
* \param function The task's function object which is looped until false is returned.
|
||||||
|
* \param interval The minimum break time in milli seconds before the first execution of the task function and between the following ones.
|
||||||
|
*/
|
||||||
|
looped_task_func(function0<bool> const & function, unsigned int const interval = 0)
|
||||||
|
: m_function(function)
|
||||||
|
{
|
||||||
|
m_break_s = interval / 1000;
|
||||||
|
m_break_ns = (interval - m_break_s * 1000) * 1000 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Executes the task function.
|
||||||
|
*/
|
||||||
|
void operator() (void) const
|
||||||
|
{
|
||||||
|
if(m_function)
|
||||||
|
{
|
||||||
|
if(m_break_s > 0 || m_break_ns > 0)
|
||||||
|
{ // Sleep some time before first execution
|
||||||
|
xtime xt;
|
||||||
|
xtime_get(&xt, TIME_UTC);
|
||||||
|
xt.nsec += m_break_ns;
|
||||||
|
xt.sec += m_break_s;
|
||||||
|
thread::sleep(xt);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(m_function())
|
||||||
|
{
|
||||||
|
if(m_break_s > 0 || m_break_ns > 0)
|
||||||
|
{
|
||||||
|
xtime xt;
|
||||||
|
xtime_get(&xt, TIME_UTC);
|
||||||
|
xt.nsec += m_break_ns;
|
||||||
|
xt.sec += m_break_s;
|
||||||
|
thread::sleep(xt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
thread::yield(); // Be fair to other threads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // looped_task_func
|
||||||
|
|
||||||
|
|
||||||
|
} } // namespace boost::threadpool
|
||||||
|
|
||||||
|
#endif // THREADPOOL_TASK_ADAPTERS_HPP_INCLUDED
|
||||||
|
|
6
contrib/libcityhash/CMakeLists.txt
Normal file
6
contrib/libcityhash/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
add_library(cityhash
|
||||||
|
city.cc
|
||||||
|
|
||||||
|
citycrc.h
|
||||||
|
city.h
|
||||||
|
config.h)
|
19
contrib/libcityhash/COPYING
Normal file
19
contrib/libcityhash/COPYING
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2011 Google, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
470
contrib/libcityhash/city.cc
Normal file
470
contrib/libcityhash/city.cc
Normal file
@ -0,0 +1,470 @@
|
|||||||
|
// Copyright (c) 2011 Google, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// CityHash, by Geoff Pike and Jyrki Alakuijala
|
||||||
|
//
|
||||||
|
// This file provides CityHash64() and related functions.
|
||||||
|
//
|
||||||
|
// It's probably possible to create even faster hash functions by
|
||||||
|
// writing a program that systematically explores some of the space of
|
||||||
|
// possible hash functions, by using SIMD instructions, or by
|
||||||
|
// compromising on hash quality.
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <city.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string.h> // for memcpy and memset
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
static uint64 UNALIGNED_LOAD64(const char *p) {
|
||||||
|
uint64 result;
|
||||||
|
memcpy(&result, p, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32 UNALIGNED_LOAD32(const char *p) {
|
||||||
|
uint32 result;
|
||||||
|
memcpy(&result, p, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(WORDS_BIGENDIAN)
|
||||||
|
|
||||||
|
#define uint32_in_expected_order(x) (x)
|
||||||
|
#define uint64_in_expected_order(x) (x)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define bswap_32(x) _byteswap_ulong(x)
|
||||||
|
#define bswap_64(x) _byteswap_uint64(x)
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
// Mac OS X / Darwin features
|
||||||
|
#include <libkern/OSByteOrder.h>
|
||||||
|
#define bswap_32(x) OSSwapInt32(x)
|
||||||
|
#define bswap_64(x) OSSwapInt64(x)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <byteswap.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define uint32_in_expected_order(x) (bswap_32(x))
|
||||||
|
#define uint64_in_expected_order(x) (bswap_64(x))
|
||||||
|
|
||||||
|
#endif // WORDS_BIGENDIAN
|
||||||
|
|
||||||
|
#if !defined(LIKELY)
|
||||||
|
#if HAVE_BUILTIN_EXPECT
|
||||||
|
#define LIKELY(x) (__builtin_expect(!!(x), 1))
|
||||||
|
#else
|
||||||
|
#define LIKELY(x) (x)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uint64 Fetch64(const char *p) {
|
||||||
|
return uint64_in_expected_order(UNALIGNED_LOAD64(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32 Fetch32(const char *p) {
|
||||||
|
return uint32_in_expected_order(UNALIGNED_LOAD32(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some primes between 2^63 and 2^64 for various uses.
|
||||||
|
static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
|
||||||
|
static const uint64 k1 = 0xb492b66fbe98f273ULL;
|
||||||
|
static const uint64 k2 = 0x9ae16a3b2f90404fULL;
|
||||||
|
static const uint64 k3 = 0xc949d7c7509e6557ULL;
|
||||||
|
|
||||||
|
// Bitwise right rotate. Normally this will compile to a single
|
||||||
|
// instruction, especially if the shift is a manifest constant.
|
||||||
|
static uint64 Rotate(uint64 val, int shift) {
|
||||||
|
// Avoid shifting by 64: doing so yields an undefined result.
|
||||||
|
return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent to Rotate(), but requires the second arg to be non-zero.
|
||||||
|
// On x86-64, and probably others, it's possible for this to compile
|
||||||
|
// to a single instruction if both args are already in registers.
|
||||||
|
static uint64 RotateByAtLeast1(uint64 val, int shift) {
|
||||||
|
return (val >> shift) | (val << (64 - shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64 ShiftMix(uint64 val) {
|
||||||
|
return val ^ (val >> 47);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64 HashLen16(uint64 u, uint64 v) {
|
||||||
|
return Hash128to64(uint128(u, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64 HashLen0to16(const char *s, size_t len) {
|
||||||
|
if (len > 8) {
|
||||||
|
uint64 a = Fetch64(s);
|
||||||
|
uint64 b = Fetch64(s + len - 8);
|
||||||
|
return HashLen16(a, RotateByAtLeast1(b + len, len)) ^ b;
|
||||||
|
}
|
||||||
|
if (len >= 4) {
|
||||||
|
uint64 a = Fetch32(s);
|
||||||
|
return HashLen16(len + (a << 3), Fetch32(s + len - 4));
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
uint8 a = s[0];
|
||||||
|
uint8 b = s[len >> 1];
|
||||||
|
uint8 c = s[len - 1];
|
||||||
|
uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
|
||||||
|
uint32 z = len + (static_cast<uint32>(c) << 2);
|
||||||
|
return ShiftMix(y * k2 ^ z * k3) * k2;
|
||||||
|
}
|
||||||
|
return k2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This probably works well for 16-byte strings as well, but it may be overkill
|
||||||
|
// in that case.
|
||||||
|
static uint64 HashLen17to32(const char *s, size_t len) {
|
||||||
|
uint64 a = Fetch64(s) * k1;
|
||||||
|
uint64 b = Fetch64(s + 8);
|
||||||
|
uint64 c = Fetch64(s + len - 8) * k2;
|
||||||
|
uint64 d = Fetch64(s + len - 16) * k0;
|
||||||
|
return HashLen16(Rotate(a - b, 43) + Rotate(c, 30) + d,
|
||||||
|
a + Rotate(b ^ k3, 20) - c + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a 16-byte hash for 48 bytes. Quick and dirty.
|
||||||
|
// Callers do best to use "random-looking" values for a and b.
|
||||||
|
static pair<uint64, uint64> WeakHashLen32WithSeeds(
|
||||||
|
uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) {
|
||||||
|
a += w;
|
||||||
|
b = Rotate(b + a + z, 21);
|
||||||
|
uint64 c = a;
|
||||||
|
a += x;
|
||||||
|
a += y;
|
||||||
|
b += Rotate(a, 44);
|
||||||
|
return make_pair(a + z, b + c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
|
||||||
|
static pair<uint64, uint64> WeakHashLen32WithSeeds(
|
||||||
|
const char* s, uint64 a, uint64 b) {
|
||||||
|
return WeakHashLen32WithSeeds(Fetch64(s),
|
||||||
|
Fetch64(s + 8),
|
||||||
|
Fetch64(s + 16),
|
||||||
|
Fetch64(s + 24),
|
||||||
|
a,
|
||||||
|
b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an 8-byte hash for 33 to 64 bytes.
|
||||||
|
static uint64 HashLen33to64(const char *s, size_t len) {
|
||||||
|
uint64 z = Fetch64(s + 24);
|
||||||
|
uint64 a = Fetch64(s) + (len + Fetch64(s + len - 16)) * k0;
|
||||||
|
uint64 b = Rotate(a + z, 52);
|
||||||
|
uint64 c = Rotate(a, 37);
|
||||||
|
a += Fetch64(s + 8);
|
||||||
|
c += Rotate(a, 7);
|
||||||
|
a += Fetch64(s + 16);
|
||||||
|
uint64 vf = a + z;
|
||||||
|
uint64 vs = b + Rotate(a, 31) + c;
|
||||||
|
a = Fetch64(s + 16) + Fetch64(s + len - 32);
|
||||||
|
z = Fetch64(s + len - 8);
|
||||||
|
b = Rotate(a + z, 52);
|
||||||
|
c = Rotate(a, 37);
|
||||||
|
a += Fetch64(s + len - 24);
|
||||||
|
c += Rotate(a, 7);
|
||||||
|
a += Fetch64(s + len - 16);
|
||||||
|
uint64 wf = a + z;
|
||||||
|
uint64 ws = b + Rotate(a, 31) + c;
|
||||||
|
uint64 r = ShiftMix((vf + ws) * k2 + (wf + vs) * k0);
|
||||||
|
return ShiftMix(r * k0 + vs) * k2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 CityHash64(const char *s, size_t len) {
|
||||||
|
if (len <= 32) {
|
||||||
|
if (len <= 16) {
|
||||||
|
return HashLen0to16(s, len);
|
||||||
|
} else {
|
||||||
|
return HashLen17to32(s, len);
|
||||||
|
}
|
||||||
|
} else if (len <= 64) {
|
||||||
|
return HashLen33to64(s, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For strings over 64 bytes we hash the end first, and then as we
|
||||||
|
// loop we keep 56 bytes of state: v, w, x, y, and z.
|
||||||
|
uint64 x = Fetch64(s);
|
||||||
|
uint64 y = Fetch64(s + len - 16) ^ k1;
|
||||||
|
uint64 z = Fetch64(s + len - 56) ^ k0;
|
||||||
|
pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, y);
|
||||||
|
pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, len * k1, k0);
|
||||||
|
z += ShiftMix(v.second) * k1;
|
||||||
|
x = Rotate(z + x, 39) * k1;
|
||||||
|
y = Rotate(y, 33) * k1;
|
||||||
|
|
||||||
|
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
|
||||||
|
len = (len - 1) & ~static_cast<size_t>(63);
|
||||||
|
do {
|
||||||
|
x = Rotate(x + y + v.first + Fetch64(s + 16), 37) * k1;
|
||||||
|
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
|
||||||
|
x ^= w.second;
|
||||||
|
y ^= v.first;
|
||||||
|
z = Rotate(z ^ w.first, 33);
|
||||||
|
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
|
||||||
|
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y);
|
||||||
|
std::swap(z, x);
|
||||||
|
s += 64;
|
||||||
|
len -= 64;
|
||||||
|
} while (len != 0);
|
||||||
|
return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,
|
||||||
|
HashLen16(v.second, w.second) + x);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) {
|
||||||
|
return CityHash64WithSeeds(s, len, k2, seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 CityHash64WithSeeds(const char *s, size_t len,
|
||||||
|
uint64 seed0, uint64 seed1) {
|
||||||
|
return HashLen16(CityHash64(s, len) - seed0, seed1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
|
||||||
|
// of any length representable in ssize_t. Based on City and Murmur.
|
||||||
|
static uint128 CityMurmur(const char *s, size_t len, uint128 seed) {
|
||||||
|
uint64 a = Uint128Low64(seed);
|
||||||
|
uint64 b = Uint128High64(seed);
|
||||||
|
uint64 c = 0;
|
||||||
|
uint64 d = 0;
|
||||||
|
ssize_t l = len - 16;
|
||||||
|
if (l <= 0) { // len <= 16
|
||||||
|
a = ShiftMix(a * k1) * k1;
|
||||||
|
c = b * k1 + HashLen0to16(s, len);
|
||||||
|
d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c));
|
||||||
|
} else { // len > 16
|
||||||
|
c = HashLen16(Fetch64(s + len - 8) + k1, a);
|
||||||
|
d = HashLen16(b + len, c + Fetch64(s + len - 16));
|
||||||
|
a += d;
|
||||||
|
do {
|
||||||
|
a ^= ShiftMix(Fetch64(s) * k1) * k1;
|
||||||
|
a *= k1;
|
||||||
|
b ^= a;
|
||||||
|
c ^= ShiftMix(Fetch64(s + 8) * k1) * k1;
|
||||||
|
c *= k1;
|
||||||
|
d ^= c;
|
||||||
|
s += 16;
|
||||||
|
l -= 16;
|
||||||
|
} while (l > 0);
|
||||||
|
}
|
||||||
|
a = HashLen16(a, c);
|
||||||
|
b = HashLen16(d, b);
|
||||||
|
return uint128(a ^ b, HashLen16(b, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) {
|
||||||
|
if (len < 128) {
|
||||||
|
return CityMurmur(s, len, seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect len >= 128 to be the common case. Keep 56 bytes of state:
|
||||||
|
// v, w, x, y, and z.
|
||||||
|
pair<uint64, uint64> v, w;
|
||||||
|
uint64 x = Uint128Low64(seed);
|
||||||
|
uint64 y = Uint128High64(seed);
|
||||||
|
uint64 z = len * k1;
|
||||||
|
v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
|
||||||
|
v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
|
||||||
|
w.first = Rotate(y + z, 35) * k1 + x;
|
||||||
|
w.second = Rotate(x + Fetch64(s + 88), 53) * k1;
|
||||||
|
|
||||||
|
// This is the same inner loop as CityHash64(), manually unrolled.
|
||||||
|
do {
|
||||||
|
x = Rotate(x + y + v.first + Fetch64(s + 16), 37) * k1;
|
||||||
|
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
|
||||||
|
x ^= w.second;
|
||||||
|
y ^= v.first;
|
||||||
|
z = Rotate(z ^ w.first, 33);
|
||||||
|
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
|
||||||
|
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y);
|
||||||
|
std::swap(z, x);
|
||||||
|
s += 64;
|
||||||
|
x = Rotate(x + y + v.first + Fetch64(s + 16), 37) * k1;
|
||||||
|
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
|
||||||
|
x ^= w.second;
|
||||||
|
y ^= v.first;
|
||||||
|
z = Rotate(z ^ w.first, 33);
|
||||||
|
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
|
||||||
|
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y);
|
||||||
|
std::swap(z, x);
|
||||||
|
s += 64;
|
||||||
|
len -= 128;
|
||||||
|
} while (LIKELY(len >= 128));
|
||||||
|
y += Rotate(w.first, 37) * k0 + z;
|
||||||
|
x += Rotate(v.first + z, 49) * k0;
|
||||||
|
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
|
||||||
|
for (size_t tail_done = 0; tail_done < len; ) {
|
||||||
|
tail_done += 32;
|
||||||
|
y = Rotate(y - x, 42) * k0 + v.second;
|
||||||
|
w.first += Fetch64(s + len - tail_done + 16);
|
||||||
|
x = Rotate(x, 49) * k0 + w.first;
|
||||||
|
w.first += v.first;
|
||||||
|
v = WeakHashLen32WithSeeds(s + len - tail_done, v.first, v.second);
|
||||||
|
}
|
||||||
|
// At this point our 48 bytes of state should contain more than
|
||||||
|
// enough information for a strong 128-bit hash. We use two
|
||||||
|
// different 48-byte-to-8-byte hashes to get a 16-byte final result.
|
||||||
|
x = HashLen16(x, v.first);
|
||||||
|
y = HashLen16(y, w.first);
|
||||||
|
return uint128(HashLen16(x + v.second, w.second) + y,
|
||||||
|
HashLen16(x + w.second, y + v.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint128 CityHash128(const char *s, size_t len) {
|
||||||
|
if (len >= 16) {
|
||||||
|
return CityHash128WithSeed(s + 16,
|
||||||
|
len - 16,
|
||||||
|
uint128(Fetch64(s) ^ k3,
|
||||||
|
Fetch64(s + 8)));
|
||||||
|
} else if (len >= 8) {
|
||||||
|
return CityHash128WithSeed(NULL,
|
||||||
|
0,
|
||||||
|
uint128(Fetch64(s) ^ (len * k0),
|
||||||
|
Fetch64(s + len - 8) ^ k1));
|
||||||
|
} else {
|
||||||
|
return CityHash128WithSeed(s, len, uint128(k0, k1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __SSE4_2__
|
||||||
|
#include <citycrc.h>
|
||||||
|
#include <nmmintrin.h>
|
||||||
|
|
||||||
|
// Requires len >= 240.
|
||||||
|
static void CityHashCrc256Long(const char *s, size_t len,
|
||||||
|
uint32 seed, uint64 *result) {
|
||||||
|
uint64 a = Fetch64(s + 56) + k0;
|
||||||
|
uint64 b = Fetch64(s + 96) + k0;
|
||||||
|
uint64 c = result[1] = HashLen16(b, len);
|
||||||
|
uint64 d = result[2] = Fetch64(s + 120) * k0 + len;
|
||||||
|
uint64 e = Fetch64(s + 184) + seed;
|
||||||
|
uint64 f = seed;
|
||||||
|
uint64 g = 0;
|
||||||
|
uint64 h = 0;
|
||||||
|
uint64 i = 0;
|
||||||
|
uint64 j = 0;
|
||||||
|
uint64 t = c + d;
|
||||||
|
|
||||||
|
// 240 bytes of input per iter.
|
||||||
|
size_t iters = len / 240;
|
||||||
|
len -= iters * 240;
|
||||||
|
do {
|
||||||
|
#define CHUNK(multiplier, z) \
|
||||||
|
{ \
|
||||||
|
uint64 old_a = a; \
|
||||||
|
a = Rotate(b, 41 ^ z) * multiplier + Fetch64(s); \
|
||||||
|
b = Rotate(c, 27 ^ z) * multiplier + Fetch64(s + 8); \
|
||||||
|
c = Rotate(d, 41 ^ z) * multiplier + Fetch64(s + 16); \
|
||||||
|
d = Rotate(e, 33 ^ z) * multiplier + Fetch64(s + 24); \
|
||||||
|
e = Rotate(t, 25 ^ z) * multiplier + Fetch64(s + 32); \
|
||||||
|
t = old_a; \
|
||||||
|
} \
|
||||||
|
f = _mm_crc32_u64(f, a); \
|
||||||
|
g = _mm_crc32_u64(g, b); \
|
||||||
|
h = _mm_crc32_u64(h, c); \
|
||||||
|
i = _mm_crc32_u64(i, d); \
|
||||||
|
j = _mm_crc32_u64(j, e); \
|
||||||
|
s += 40
|
||||||
|
|
||||||
|
CHUNK(1, 1); CHUNK(k0, 0);
|
||||||
|
CHUNK(1, 1); CHUNK(k0, 0);
|
||||||
|
CHUNK(1, 1); CHUNK(k0, 0);
|
||||||
|
} while (--iters > 0);
|
||||||
|
j += i << 32;
|
||||||
|
a = HashLen16(a, j);
|
||||||
|
h += g << 32;
|
||||||
|
b = b * k0 + h;
|
||||||
|
c = HashLen16(c, f) + i;
|
||||||
|
d = HashLen16(d, e);
|
||||||
|
pair<uint64, uint64> v(j + e, HashLen16(h, t));
|
||||||
|
h = v.second + f;
|
||||||
|
// If 0 < len < 240, hash chunks of 32 bytes each from the end of s.
|
||||||
|
for (size_t tail_done = 0; tail_done < len; ) {
|
||||||
|
tail_done += 32;
|
||||||
|
c = Rotate(c - a, 42) * k0 + v.second;
|
||||||
|
d += Fetch64(s + len - tail_done + 16);
|
||||||
|
a = Rotate(a, 49) * k0 + d;
|
||||||
|
d += v.first;
|
||||||
|
v = WeakHashLen32WithSeeds(s + len - tail_done, v.first, v.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final mix.
|
||||||
|
e = HashLen16(a, d) + v.first;
|
||||||
|
f = HashLen16(b, c) + a;
|
||||||
|
g = HashLen16(v.first, v.second) + c;
|
||||||
|
result[0] = e + f + g + h;
|
||||||
|
a = ShiftMix((a + g) * k0) * k0 + b;
|
||||||
|
result[1] += a + result[0];
|
||||||
|
a = ShiftMix(a * k0) * k0 + c;
|
||||||
|
result[2] += a + result[1];
|
||||||
|
a = ShiftMix((a + e) * k0) * k0;
|
||||||
|
result[3] = a + result[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requires len < 240.
|
||||||
|
static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) {
|
||||||
|
char buf[240];
|
||||||
|
memcpy(buf, s, len);
|
||||||
|
memset(buf + len, 0, 240 - len);
|
||||||
|
CityHashCrc256Long(buf, 240, ~static_cast<uint32>(len), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CityHashCrc256(const char *s, size_t len, uint64 *result) {
|
||||||
|
if (LIKELY(len >= 240)) {
|
||||||
|
CityHashCrc256Long(s, len, 0, result);
|
||||||
|
} else {
|
||||||
|
CityHashCrc256Short(s, len, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) {
|
||||||
|
if (len <= 900) {
|
||||||
|
return CityHash128WithSeed(s, len, seed);
|
||||||
|
} else {
|
||||||
|
uint64 result[4];
|
||||||
|
CityHashCrc256(s, len, result);
|
||||||
|
uint64 u = Uint128High64(seed) + result[0];
|
||||||
|
uint64 v = Uint128Low64(seed) + result[1];
|
||||||
|
return uint128(HashLen16(u, v + result[2]),
|
||||||
|
HashLen16(Rotate(v, 32), u * k0 + result[3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint128 CityHashCrc128(const char *s, size_t len) {
|
||||||
|
if (len <= 900) {
|
||||||
|
return CityHash128(s, len);
|
||||||
|
} else {
|
||||||
|
uint64 result[4];
|
||||||
|
CityHashCrc256(s, len, result);
|
||||||
|
return uint128(result[2], result[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
90
contrib/libcityhash/city.h
Normal file
90
contrib/libcityhash/city.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (c) 2011 Google, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// CityHash, by Geoff Pike and Jyrki Alakuijala
|
||||||
|
//
|
||||||
|
// This file provides a few functions for hashing strings. On x86-64
|
||||||
|
// hardware in 2011, CityHash64() is faster than other high-quality
|
||||||
|
// hash functions, such as Murmur. This is largely due to higher
|
||||||
|
// instruction-level parallelism. CityHash64() and CityHash128() also perform
|
||||||
|
// well on hash-quality tests.
|
||||||
|
//
|
||||||
|
// CityHash128() is optimized for relatively long strings and returns
|
||||||
|
// a 128-bit hash. For strings more than about 2000 bytes it can be
|
||||||
|
// faster than CityHash64().
|
||||||
|
//
|
||||||
|
// Functions in the CityHash family are not suitable for cryptography.
|
||||||
|
//
|
||||||
|
// WARNING: This code has not been tested on big-endian platforms!
|
||||||
|
// It is known to work well on little-endian platforms that have a small penalty
|
||||||
|
// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
|
||||||
|
//
|
||||||
|
// By the way, for some hash functions, given strings a and b, the hash
|
||||||
|
// of a+b is easily derived from the hashes of a and b. This property
|
||||||
|
// doesn't hold for any hash functions in this file.
|
||||||
|
|
||||||
|
#ifndef CITY_HASH_H_
|
||||||
|
#define CITY_HASH_H_
|
||||||
|
|
||||||
|
#include <stdlib.h> // for size_t.
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
typedef uint8_t uint8;
|
||||||
|
typedef uint32_t uint32;
|
||||||
|
typedef uint64_t uint64;
|
||||||
|
typedef std::pair<uint64, uint64> uint128;
|
||||||
|
|
||||||
|
inline uint64 Uint128Low64(const uint128& x) { return x.first; }
|
||||||
|
inline uint64 Uint128High64(const uint128& x) { return x.second; }
|
||||||
|
|
||||||
|
// Hash function for a byte array.
|
||||||
|
uint64 CityHash64(const char *buf, size_t len);
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 64-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed);
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, two seeds are also
|
||||||
|
// hashed into the result.
|
||||||
|
uint64 CityHash64WithSeeds(const char *buf, size_t len,
|
||||||
|
uint64 seed0, uint64 seed1);
|
||||||
|
|
||||||
|
// Hash function for a byte array.
|
||||||
|
uint128 CityHash128(const char *s, size_t len);
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 128-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed);
|
||||||
|
|
||||||
|
// Hash 128 input bits down to 64 bits of output.
|
||||||
|
// This is intended to be a reasonably good hash function.
|
||||||
|
inline uint64 Hash128to64(const uint128& x) {
|
||||||
|
// Murmur-inspired hashing.
|
||||||
|
const uint64 kMul = 0x9ddfea08eb382d69ULL;
|
||||||
|
uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
|
||||||
|
a ^= (a >> 47);
|
||||||
|
uint64 b = (Uint128High64(x) ^ a) * kMul;
|
||||||
|
b ^= (b >> 47);
|
||||||
|
b *= kMul;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CITY_HASH_H_
|
43
contrib/libcityhash/citycrc.h
Normal file
43
contrib/libcityhash/citycrc.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2011 Google, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// CityHash, by Geoff Pike and Jyrki Alakuijala
|
||||||
|
//
|
||||||
|
// This file declares the subset of the CityHash functions that require
|
||||||
|
// _mm_crc32_u64(). See the CityHash README for details.
|
||||||
|
//
|
||||||
|
// Functions in the CityHash family are not suitable for cryptography.
|
||||||
|
|
||||||
|
#ifndef CITY_HASH_CRC_H_
|
||||||
|
#define CITY_HASH_CRC_H_
|
||||||
|
|
||||||
|
#include <city.h>
|
||||||
|
|
||||||
|
// Hash function for a byte array.
|
||||||
|
uint128 CityHashCrc128(const char *s, size_t len);
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 128-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed);
|
||||||
|
|
||||||
|
// Hash function for a byte array. Sets result[0] ... result[3].
|
||||||
|
void CityHashCrc256(const char *s, size_t len, uint64 *result);
|
||||||
|
|
||||||
|
#endif // CITY_HASH_CRC_H_
|
114
contrib/libcityhash/config.h
Normal file
114
contrib/libcityhash/config.h
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/* config.h. Generated from config.h.in by configure. */
|
||||||
|
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define if building universal (internal helper macro) */
|
||||||
|
/* #undef AC_APPLE_UNIVERSAL_BUILD */
|
||||||
|
|
||||||
|
/* Define to 1 if the compiler supports __builtin_expect. */
|
||||||
|
#define HAVE_BUILTIN_EXPECT 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
#define HAVE_DLFCN_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#define HAVE_MEMORY_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#define HAVE_STDINT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#define HAVE_STDLIB_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#define HAVE_STRINGS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#define HAVE_STRING_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#define HAVE_SYS_STAT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#define HAVE_SYS_TYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#define HAVE_UNISTD_H 1
|
||||||
|
|
||||||
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
|
*/
|
||||||
|
#define LT_OBJDIR ".libs/"
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#define PACKAGE_BUGREPORT "cityhash-discuss@googlegroups.com"
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#define PACKAGE_NAME "CityHash"
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#define PACKAGE_STRING "CityHash 1.0.2"
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#define PACKAGE_TARNAME "cityhash"
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#define PACKAGE_URL ""
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#define PACKAGE_VERSION "1.0.2"
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#define STDC_HEADERS 1
|
||||||
|
|
||||||
|
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||||
|
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||||
|
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||||
|
# if defined __BIG_ENDIAN__
|
||||||
|
# define WORDS_BIGENDIAN 1
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# ifndef WORDS_BIGENDIAN
|
||||||
|
/* # undef WORDS_BIGENDIAN */
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
/* #undef _UINT32_T */
|
||||||
|
|
||||||
|
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
/* #undef _UINT64_T */
|
||||||
|
|
||||||
|
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
/* #undef _UINT8_T */
|
||||||
|
|
||||||
|
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||||
|
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
/* #undef inline */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||||
|
/* #undef size_t */
|
||||||
|
|
||||||
|
/* Define to `int' if <sys/types.h> does not define. */
|
||||||
|
/* #undef ssize_t */
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 32 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
/* #undef uint32_t */
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 64 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
/* #undef uint64_t */
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 8 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
/* #undef uint8_t */
|
18
contrib/libcpuid/CMakeLists.txt
Normal file
18
contrib/libcpuid/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
add_library(cpuid
|
||||||
|
include/cpuid/asm-bits.c
|
||||||
|
include/cpuid/cpuid_main.c
|
||||||
|
include/cpuid/libcpuid_util.c
|
||||||
|
include/cpuid/rdtsc.c
|
||||||
|
include/cpuid/recog_amd.c
|
||||||
|
include/cpuid/recog_intel.c
|
||||||
|
|
||||||
|
include/cpuid/asm-bits.h
|
||||||
|
include/cpuid/config.h
|
||||||
|
include/cpuid/libcpuid_constants.h
|
||||||
|
include/cpuid/libcpuid.h
|
||||||
|
include/cpuid/libcpuid_types.h
|
||||||
|
include/cpuid/libcpuid_util.h
|
||||||
|
include/cpuid/rdtsc.h
|
||||||
|
include/cpuid/recog_amd.h
|
||||||
|
include/cpuid/recog_intel.h
|
||||||
|
)
|
23
contrib/libcpuid/COPYING
Normal file
23
contrib/libcpuid/COPYING
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Copyright 2008 Veselin Georgiev,
|
||||||
|
anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
1
contrib/libcpuid/README.txt
Normal file
1
contrib/libcpuid/README.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://github.com/anrieff/libcpuid.git
|
823
contrib/libcpuid/include/cpuid/asm-bits.c
Normal file
823
contrib/libcpuid/include/cpuid/asm-bits.c
Normal file
@ -0,0 +1,823 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libcpuid.h"
|
||||||
|
#include "asm-bits.h"
|
||||||
|
|
||||||
|
int cpuid_exists_by_eflags(void)
|
||||||
|
{
|
||||||
|
#if defined(PLATFORM_X64)
|
||||||
|
return 1; /* CPUID is always present on the x86_64 */
|
||||||
|
#elif defined(PLATFORM_X86)
|
||||||
|
# if defined(COMPILER_GCC)
|
||||||
|
int result;
|
||||||
|
__asm __volatile(
|
||||||
|
" pushfl\n"
|
||||||
|
" pop %%eax\n"
|
||||||
|
" mov %%eax, %%ecx\n"
|
||||||
|
" xor $0x200000, %%eax\n"
|
||||||
|
" push %%eax\n"
|
||||||
|
" popfl\n"
|
||||||
|
" pushfl\n"
|
||||||
|
" pop %%eax\n"
|
||||||
|
" xor %%ecx, %%eax\n"
|
||||||
|
" mov %%eax, %0\n"
|
||||||
|
" push %%ecx\n"
|
||||||
|
" popfl\n"
|
||||||
|
: "=m"(result)
|
||||||
|
: :"eax", "ecx", "memory");
|
||||||
|
return (result != 0);
|
||||||
|
# elif defined(COMPILER_MICROSOFT)
|
||||||
|
int result;
|
||||||
|
__asm {
|
||||||
|
pushfd
|
||||||
|
pop eax
|
||||||
|
mov ecx, eax
|
||||||
|
xor eax, 0x200000
|
||||||
|
push eax
|
||||||
|
popfd
|
||||||
|
pushfd
|
||||||
|
pop eax
|
||||||
|
xor eax, ecx
|
||||||
|
mov result, eax
|
||||||
|
push ecx
|
||||||
|
popfd
|
||||||
|
};
|
||||||
|
return (result != 0);
|
||||||
|
# else
|
||||||
|
return 0;
|
||||||
|
# endif /* COMPILER_MICROSOFT */
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif /* PLATFORM_X86 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* with MSVC/AMD64, the exec_cpuid() and cpu_rdtsc() functions
|
||||||
|
* are implemented in separate .asm files. Otherwise, use inline assembly
|
||||||
|
*/
|
||||||
|
void exec_cpuid(uint32_t *regs)
|
||||||
|
{
|
||||||
|
#ifdef INLINE_ASM_SUPPORTED
|
||||||
|
#ifdef COMPILER_GCC
|
||||||
|
# ifdef PLATFORM_X64
|
||||||
|
__asm __volatile(
|
||||||
|
" mov %0, %%rdi\n"
|
||||||
|
|
||||||
|
" push %%rbx\n"
|
||||||
|
" push %%rcx\n"
|
||||||
|
" push %%rdx\n"
|
||||||
|
|
||||||
|
" mov (%%rdi), %%eax\n"
|
||||||
|
" mov 4(%%rdi), %%ebx\n"
|
||||||
|
" mov 8(%%rdi), %%ecx\n"
|
||||||
|
" mov 12(%%rdi), %%edx\n"
|
||||||
|
|
||||||
|
" cpuid\n"
|
||||||
|
|
||||||
|
" movl %%eax, (%%rdi)\n"
|
||||||
|
" movl %%ebx, 4(%%rdi)\n"
|
||||||
|
" movl %%ecx, 8(%%rdi)\n"
|
||||||
|
" movl %%edx, 12(%%rdi)\n"
|
||||||
|
" pop %%rdx\n"
|
||||||
|
" pop %%rcx\n"
|
||||||
|
" pop %%rbx\n"
|
||||||
|
:
|
||||||
|
:"m"(regs)
|
||||||
|
:"memory", "eax", "rdi"
|
||||||
|
);
|
||||||
|
# else
|
||||||
|
__asm __volatile(
|
||||||
|
" mov %0, %%edi\n"
|
||||||
|
|
||||||
|
" push %%ebx\n"
|
||||||
|
" push %%ecx\n"
|
||||||
|
" push %%edx\n"
|
||||||
|
|
||||||
|
" mov (%%edi), %%eax\n"
|
||||||
|
" mov 4(%%edi), %%ebx\n"
|
||||||
|
" mov 8(%%edi), %%ecx\n"
|
||||||
|
" mov 12(%%edi), %%edx\n"
|
||||||
|
|
||||||
|
" cpuid\n"
|
||||||
|
|
||||||
|
" mov %%eax, (%%edi)\n"
|
||||||
|
" mov %%ebx, 4(%%edi)\n"
|
||||||
|
" mov %%ecx, 8(%%edi)\n"
|
||||||
|
" mov %%edx, 12(%%edi)\n"
|
||||||
|
" pop %%edx\n"
|
||||||
|
" pop %%ecx\n"
|
||||||
|
" pop %%ebx\n"
|
||||||
|
:
|
||||||
|
:"m"(regs)
|
||||||
|
:"memory", "eax", "edi"
|
||||||
|
);
|
||||||
|
# endif /* COMPILER_GCC */
|
||||||
|
#else
|
||||||
|
# ifdef COMPILER_MICROSOFT
|
||||||
|
__asm {
|
||||||
|
push ebx
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
push edi
|
||||||
|
mov edi, regs
|
||||||
|
|
||||||
|
mov eax, [edi]
|
||||||
|
mov ebx, [edi+4]
|
||||||
|
mov ecx, [edi+8]
|
||||||
|
mov edx, [edi+12]
|
||||||
|
|
||||||
|
cpuid
|
||||||
|
|
||||||
|
mov [edi], eax
|
||||||
|
mov [edi+4], ebx
|
||||||
|
mov [edi+8], ecx
|
||||||
|
mov [edi+12], edx
|
||||||
|
|
||||||
|
pop edi
|
||||||
|
pop edx
|
||||||
|
pop ecx
|
||||||
|
pop ebx
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
# error "Unsupported compiler"
|
||||||
|
# endif /* COMPILER_MICROSOFT */
|
||||||
|
#endif
|
||||||
|
#endif /* INLINE_ASSEMBLY_SUPPORTED */
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef INLINE_ASM_SUPPORTED
|
||||||
|
void cpu_rdtsc(uint64_t* result)
|
||||||
|
{
|
||||||
|
uint32_t low_part, hi_part;
|
||||||
|
#ifdef COMPILER_GCC
|
||||||
|
__asm __volatile (
|
||||||
|
" rdtsc\n"
|
||||||
|
" mov %%eax, %0\n"
|
||||||
|
" mov %%edx, %1\n"
|
||||||
|
:"=m"(low_part), "=m"(hi_part)::"memory", "eax", "edx"
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
# ifdef COMPILER_MICROSOFT
|
||||||
|
__asm {
|
||||||
|
rdtsc
|
||||||
|
mov low_part, eax
|
||||||
|
mov hi_part, edx
|
||||||
|
};
|
||||||
|
# else
|
||||||
|
# error "Unsupported compiler"
|
||||||
|
# endif /* COMPILER_MICROSOFT */
|
||||||
|
#endif /* COMPILER_GCC */
|
||||||
|
*result = (uint64_t)low_part + (((uint64_t) hi_part) << 32);
|
||||||
|
}
|
||||||
|
#endif /* INLINE_ASM_SUPPORTED */
|
||||||
|
|
||||||
|
#ifdef INLINE_ASM_SUPPORTED
|
||||||
|
void busy_sse_loop(int cycles)
|
||||||
|
{
|
||||||
|
#ifdef COMPILER_GCC
|
||||||
|
#ifndef __APPLE__
|
||||||
|
# define XALIGN ".balign 16\n"
|
||||||
|
#else
|
||||||
|
# define XALIGN ".align 4\n"
|
||||||
|
#endif
|
||||||
|
__asm __volatile (
|
||||||
|
" xorps %%xmm0, %%xmm0\n"
|
||||||
|
" xorps %%xmm1, %%xmm1\n"
|
||||||
|
" xorps %%xmm2, %%xmm2\n"
|
||||||
|
" xorps %%xmm3, %%xmm3\n"
|
||||||
|
" xorps %%xmm4, %%xmm4\n"
|
||||||
|
" xorps %%xmm5, %%xmm5\n"
|
||||||
|
" xorps %%xmm6, %%xmm6\n"
|
||||||
|
" xorps %%xmm7, %%xmm7\n"
|
||||||
|
XALIGN
|
||||||
|
".bsLoop:\n"
|
||||||
|
// 0:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 1:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 2:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 3:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 4:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 5:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 6:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 7:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 8:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
// 9:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//10:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//11:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//12:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//13:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//14:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//15:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//16:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//17:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//18:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//19:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//20:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//21:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//22:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//23:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//24:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//25:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//26:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//27:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//28:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//29:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//30:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
//31:
|
||||||
|
" addps %%xmm1, %%xmm0\n"
|
||||||
|
" addps %%xmm2, %%xmm1\n"
|
||||||
|
" addps %%xmm3, %%xmm2\n"
|
||||||
|
" addps %%xmm4, %%xmm3\n"
|
||||||
|
" addps %%xmm5, %%xmm4\n"
|
||||||
|
" addps %%xmm6, %%xmm5\n"
|
||||||
|
" addps %%xmm7, %%xmm6\n"
|
||||||
|
" addps %%xmm0, %%xmm7\n"
|
||||||
|
|
||||||
|
" dec %%eax\n"
|
||||||
|
" jnz .bsLoop\n"
|
||||||
|
::"a"(cycles)
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
# ifdef COMPILER_MICROSOFT
|
||||||
|
__asm {
|
||||||
|
mov eax, cycles
|
||||||
|
xorps xmm0, xmm0
|
||||||
|
xorps xmm1, xmm1
|
||||||
|
xorps xmm2, xmm2
|
||||||
|
xorps xmm3, xmm3
|
||||||
|
xorps xmm4, xmm4
|
||||||
|
xorps xmm5, xmm5
|
||||||
|
xorps xmm6, xmm6
|
||||||
|
xorps xmm7, xmm7
|
||||||
|
//--
|
||||||
|
align 16
|
||||||
|
bsLoop:
|
||||||
|
// 0:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 1:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 2:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 3:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 4:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 5:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 6:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 7:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 8:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 9:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 10:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 11:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 12:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 13:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 14:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 15:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 16:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 17:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 18:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 19:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 20:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 21:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 22:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 23:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 24:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 25:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 26:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 27:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 28:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 29:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 30:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
// 31:
|
||||||
|
addps xmm0, xmm1
|
||||||
|
addps xmm1, xmm2
|
||||||
|
addps xmm2, xmm3
|
||||||
|
addps xmm3, xmm4
|
||||||
|
addps xmm4, xmm5
|
||||||
|
addps xmm5, xmm6
|
||||||
|
addps xmm6, xmm7
|
||||||
|
addps xmm7, xmm0
|
||||||
|
//----------------------
|
||||||
|
dec eax
|
||||||
|
jnz bsLoop
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
# error "Unsupported compiler"
|
||||||
|
# endif /* COMPILER_MICROSOFT */
|
||||||
|
#endif /* COMPILER_GCC */
|
||||||
|
}
|
||||||
|
#endif /* INLINE_ASSEMBLY_SUPPORTED */
|
53
contrib/libcpuid/include/cpuid/asm-bits.h
Normal file
53
contrib/libcpuid/include/cpuid/asm-bits.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __ASM_BITS_H__
|
||||||
|
#define __ASM_BITS_H__
|
||||||
|
#include "libcpuid.h"
|
||||||
|
|
||||||
|
/* Determine Compiler: */
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# define COMPILER_MICROSOFT
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# define COMPILER_GCC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Determine Platform */
|
||||||
|
#if defined(__x86_64__) || defined(_M_AMD64)
|
||||||
|
# define PLATFORM_X64
|
||||||
|
#elif defined(__i386__) || defined(_M_IX86)
|
||||||
|
# define PLATFORM_X86
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Under Windows/AMD64 with MSVC, inline assembly isn't supported */
|
||||||
|
#if (defined(COMPILER_GCC) && defined(PLATFORM_X64)) || defined(PLATFORM_X86)
|
||||||
|
# define INLINE_ASM_SUPPORTED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int cpuid_exists_by_eflags(void);
|
||||||
|
void exec_cpuid(uint32_t *regs);
|
||||||
|
void busy_sse_loop(int cycles);
|
||||||
|
|
||||||
|
#endif /* __ASM_BITS_H__ */
|
2
contrib/libcpuid/include/cpuid/config.h
Normal file
2
contrib/libcpuid/include/cpuid/config.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/* Version number of package */
|
||||||
|
#define VERSION "0.2.1"
|
674
contrib/libcpuid/include/cpuid/cpuid_main.c
Normal file
674
contrib/libcpuid/include/cpuid/cpuid_main.c
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#include "libcpuid.h"
|
||||||
|
#include "recog_intel.h"
|
||||||
|
#include "recog_amd.h"
|
||||||
|
#include "asm-bits.h"
|
||||||
|
#include "libcpuid_util.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Implementation: */
|
||||||
|
|
||||||
|
static int _libcpiud_errno = ERR_OK;
|
||||||
|
|
||||||
|
int set_error(cpu_error_t err)
|
||||||
|
{
|
||||||
|
_libcpiud_errno = (int) err;
|
||||||
|
return (int) err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raw_data_t_constructor(struct cpu_raw_data_t* raw)
|
||||||
|
{
|
||||||
|
memset(raw, 0, sizeof(struct cpu_raw_data_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cpu_id_t_constructor(struct cpu_id_t* id)
|
||||||
|
{
|
||||||
|
memset(id, 0, sizeof(struct cpu_id_t));
|
||||||
|
id->l1_data_cache = id->l1_instruction_cache = id->l2_cache = id->l3_cache = -1;
|
||||||
|
id->l1_assoc = id->l2_assoc = id->l3_assoc = -1;
|
||||||
|
id->l1_cacheline = id->l2_cacheline = id->l3_cacheline = -1;
|
||||||
|
id->sse_size = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_token(const char* expected_token, const char *token,
|
||||||
|
const char *value, uint32_t array[][4], int limit, int *recognized)
|
||||||
|
{
|
||||||
|
char format[32];
|
||||||
|
int veax, vebx, vecx, vedx;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (*recognized) return 1; /* already recognized */
|
||||||
|
if (strncmp(token, expected_token, strlen(expected_token))) return 1; /* not what we search for */
|
||||||
|
sprintf(format, "%s[%%d]", expected_token);
|
||||||
|
*recognized = 1;
|
||||||
|
if (1 == sscanf(token, format, &index) && index >=0 && index < limit) {
|
||||||
|
if (4 == sscanf(value, "%x%x%x%x", &veax, &vebx, &vecx, &vedx)) {
|
||||||
|
array[index][0] = veax;
|
||||||
|
array[index][1] = vebx;
|
||||||
|
array[index][2] = vecx;
|
||||||
|
array[index][3] = vedx;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get_total_cpus() system specific code: uses OS routines to determine total number of CPUs */
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <mach/clock_types.h>
|
||||||
|
#include <mach/clock.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
static int get_total_cpus(void)
|
||||||
|
{
|
||||||
|
kern_return_t kr;
|
||||||
|
host_basic_info_data_t basic_info;
|
||||||
|
host_info_t info = (host_info_t)&basic_info;
|
||||||
|
host_flavor_t flavor = HOST_BASIC_INFO;
|
||||||
|
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
|
||||||
|
kr = host_info(mach_host_self(), flavor, info, &count);
|
||||||
|
if (kr != KERN_SUCCESS) return 1;
|
||||||
|
return basic_info.avail_cpus;
|
||||||
|
}
|
||||||
|
#define GET_TOTAL_CPUS_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
static int get_total_cpus(void)
|
||||||
|
{
|
||||||
|
SYSTEM_INFO system_info;
|
||||||
|
GetSystemInfo(&system_info);
|
||||||
|
return system_info.dwNumberOfProcessors;
|
||||||
|
}
|
||||||
|
#define GET_TOTAL_CPUS_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined linux || defined __linux__ || defined __sun
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static int get_total_cpus(void)
|
||||||
|
{
|
||||||
|
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
}
|
||||||
|
#define GET_TOTAL_CPUS_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ || defined __bsdi__ || defined __QNX__
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
static int get_total_cpus(void)
|
||||||
|
{
|
||||||
|
int mib[2] = { CTL_HW, HW_NCPU };
|
||||||
|
int ncpus;
|
||||||
|
size_t len = sizeof(ncpus);
|
||||||
|
if (sysctl(mib, 2, &ncpus, &len, (void *) 0, 0) != 0) return 1;
|
||||||
|
return ncpus;
|
||||||
|
}
|
||||||
|
#define GET_TOTAL_CPUS_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef GET_TOTAL_CPUS_DEFINED
|
||||||
|
static int get_total_cpus(void)
|
||||||
|
{
|
||||||
|
static int warning_printed = 0;
|
||||||
|
if (!warning_printed) {
|
||||||
|
warning_printed = 1;
|
||||||
|
warnf("Your system is not supported by libcpuid -- don't know how to detect the\n");
|
||||||
|
warnf("total number of CPUs on your system. It will be reported as 1.\n");
|
||||||
|
printf("Please use cpu_id_t.logical_cpus field instead.\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif /* GET_TOTAL_CPUS_DEFINED */
|
||||||
|
|
||||||
|
|
||||||
|
static void load_features_common(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
const struct feature_map_t matchtable_edx1[] = {
|
||||||
|
{ 0, CPU_FEATURE_FPU },
|
||||||
|
{ 1, CPU_FEATURE_VME },
|
||||||
|
{ 2, CPU_FEATURE_DE },
|
||||||
|
{ 3, CPU_FEATURE_PSE },
|
||||||
|
{ 4, CPU_FEATURE_TSC },
|
||||||
|
{ 5, CPU_FEATURE_MSR },
|
||||||
|
{ 6, CPU_FEATURE_PAE },
|
||||||
|
{ 7, CPU_FEATURE_MCE },
|
||||||
|
{ 8, CPU_FEATURE_CX8 },
|
||||||
|
{ 9, CPU_FEATURE_APIC },
|
||||||
|
{ 11, CPU_FEATURE_SEP },
|
||||||
|
{ 12, CPU_FEATURE_MTRR },
|
||||||
|
{ 13, CPU_FEATURE_PGE },
|
||||||
|
{ 14, CPU_FEATURE_MCA },
|
||||||
|
{ 15, CPU_FEATURE_CMOV },
|
||||||
|
{ 16, CPU_FEATURE_PAT },
|
||||||
|
{ 17, CPU_FEATURE_PSE36 },
|
||||||
|
{ 19, CPU_FEATURE_CLFLUSH },
|
||||||
|
{ 23, CPU_FEATURE_MMX },
|
||||||
|
{ 24, CPU_FEATURE_FXSR },
|
||||||
|
{ 25, CPU_FEATURE_SSE },
|
||||||
|
{ 26, CPU_FEATURE_SSE2 },
|
||||||
|
{ 28, CPU_FEATURE_HT },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_ecx1[] = {
|
||||||
|
{ 0, CPU_FEATURE_PNI },
|
||||||
|
{ 3, CPU_FEATURE_MONITOR },
|
||||||
|
{ 9, CPU_FEATURE_SSSE3 },
|
||||||
|
{ 12, CPU_FEATURE_FMA3 },
|
||||||
|
{ 13, CPU_FEATURE_CX16 },
|
||||||
|
{ 19, CPU_FEATURE_SSE4_1 },
|
||||||
|
{ 21, CPU_FEATURE_X2APIC },
|
||||||
|
{ 23, CPU_FEATURE_POPCNT },
|
||||||
|
{ 29, CPU_FEATURE_F16C },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_edx81[] = {
|
||||||
|
{ 11, CPU_FEATURE_SYSCALL },
|
||||||
|
{ 27, CPU_FEATURE_RDTSCP },
|
||||||
|
{ 29, CPU_FEATURE_LM },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_ecx81[] = {
|
||||||
|
{ 0, CPU_FEATURE_LAHF_LM },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_edx87[] = {
|
||||||
|
{ 8, CPU_FEATURE_CONSTANT_TSC },
|
||||||
|
};
|
||||||
|
if (raw->basic_cpuid[0][0] >= 1) {
|
||||||
|
match_features(matchtable_edx1, COUNT_OF(matchtable_edx1), raw->basic_cpuid[1][3], data);
|
||||||
|
match_features(matchtable_ecx1, COUNT_OF(matchtable_ecx1), raw->basic_cpuid[1][2], data);
|
||||||
|
}
|
||||||
|
if (raw->ext_cpuid[0][0] >= 0x80000001) {
|
||||||
|
match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data);
|
||||||
|
match_features(matchtable_ecx81, COUNT_OF(matchtable_ecx81), raw->ext_cpuid[1][2], data);
|
||||||
|
}
|
||||||
|
if (raw->ext_cpuid[0][0] >= 0x80000007) {
|
||||||
|
match_features(matchtable_edx87, COUNT_OF(matchtable_edx87), raw->ext_cpuid[7][3], data);
|
||||||
|
}
|
||||||
|
if (data->flags[CPU_FEATURE_SSE]) {
|
||||||
|
/* apply guesswork to check if the SSE unit width is 128 bit */
|
||||||
|
switch (data->vendor) {
|
||||||
|
case VENDOR_AMD:
|
||||||
|
data->sse_size = (data->ext_family >= 16 && data->ext_family != 17) ? 128 : 64;
|
||||||
|
break;
|
||||||
|
case VENDOR_INTEL:
|
||||||
|
data->sse_size = (data->family == 6 && data->ext_model >= 15) ? 128 : 64;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* leave the CPU_FEATURE_128BIT_SSE_AUTH 0; the advanced per-vendor detection routines
|
||||||
|
* will set it accordingly if they detect the needed bit */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int i, j, basic, xmodel, xfamily, ext;
|
||||||
|
char brandstr[64] = {0};
|
||||||
|
const struct { cpu_vendor_t vendor; char match[16]; }
|
||||||
|
matchtable[NUM_CPU_VENDORS] = {
|
||||||
|
/* source: http://www.sandpile.org/ia32/cpuid.htm */
|
||||||
|
{ VENDOR_INTEL , "GenuineIntel" },
|
||||||
|
{ VENDOR_AMD , "AuthenticAMD" },
|
||||||
|
{ VENDOR_CYRIX , "CyrixInstead" },
|
||||||
|
{ VENDOR_NEXGEN , "NexGenDriven" },
|
||||||
|
{ VENDOR_TRANSMETA , "GenuineTMx86" },
|
||||||
|
{ VENDOR_UMC , "UMC UMC UMC " },
|
||||||
|
{ VENDOR_CENTAUR , "CentaurHauls" },
|
||||||
|
{ VENDOR_RISE , "RiseRiseRise" },
|
||||||
|
{ VENDOR_SIS , "SiS SiS SiS " },
|
||||||
|
{ VENDOR_NSC , "Geode by NSC" },
|
||||||
|
};
|
||||||
|
|
||||||
|
memcpy(data->vendor_str + 0, &raw->basic_cpuid[0][1], 4);
|
||||||
|
memcpy(data->vendor_str + 4, &raw->basic_cpuid[0][3], 4);
|
||||||
|
memcpy(data->vendor_str + 8, &raw->basic_cpuid[0][2], 4);
|
||||||
|
data->vendor_str[12] = 0;
|
||||||
|
/* Determine vendor: */
|
||||||
|
data->vendor = VENDOR_UNKNOWN;
|
||||||
|
for (i = 0; i < NUM_CPU_VENDORS; i++)
|
||||||
|
if (!strcmp(data->vendor_str, matchtable[i].match)) {
|
||||||
|
data->vendor = matchtable[i].vendor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (data->vendor == VENDOR_UNKNOWN)
|
||||||
|
return set_error(ERR_CPU_UNKN);
|
||||||
|
basic = raw->basic_cpuid[0][0];
|
||||||
|
if (basic >= 1) {
|
||||||
|
data->family = (raw->basic_cpuid[1][0] >> 8) & 0xf;
|
||||||
|
data->model = (raw->basic_cpuid[1][0] >> 4) & 0xf;
|
||||||
|
data->stepping = raw->basic_cpuid[1][0] & 0xf;
|
||||||
|
xmodel = (raw->basic_cpuid[1][0] >> 16) & 0xf;
|
||||||
|
xfamily = (raw->basic_cpuid[1][0] >> 20) & 0xff;
|
||||||
|
if (data->vendor == VENDOR_AMD && data->family < 0xf)
|
||||||
|
data->ext_family = data->family;
|
||||||
|
else
|
||||||
|
data->ext_family = data->family + xfamily;
|
||||||
|
data->ext_model = data->model + (xmodel << 4);
|
||||||
|
}
|
||||||
|
ext = raw->ext_cpuid[0][0] - 0x8000000;
|
||||||
|
|
||||||
|
/* obtain the brand string, if present: */
|
||||||
|
if (ext >= 4) {
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
memcpy(brandstr + i * 16 + j * 4,
|
||||||
|
&raw->ext_cpuid[2 + i][j], 4);
|
||||||
|
brandstr[48] = 0;
|
||||||
|
i = 0;
|
||||||
|
while (brandstr[i] == ' ') i++;
|
||||||
|
strncpy(data->brand_str, brandstr + i, sizeof(data->brand_str));
|
||||||
|
data->brand_str[48] = 0;
|
||||||
|
}
|
||||||
|
load_features_common(raw, data);
|
||||||
|
data->total_logical_cpus = get_total_cpus();
|
||||||
|
return set_error(ERR_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void make_list_from_string(const char* csv, struct cpu_list_t* list)
|
||||||
|
{
|
||||||
|
int i, n, l, last;
|
||||||
|
l = (int) strlen(csv);
|
||||||
|
n = 0;
|
||||||
|
for (i = 0; i < l; i++) if (csv[i] == ',') n++;
|
||||||
|
n++;
|
||||||
|
list->num_entries = n;
|
||||||
|
list->names = (char**) malloc(sizeof(char*) * n);
|
||||||
|
last = -1;
|
||||||
|
n = 0;
|
||||||
|
for (i = 0; i <= l; i++) if (i == l || csv[i] == ',') {
|
||||||
|
list->names[n] = (char*) malloc(i - last);
|
||||||
|
memcpy(list->names[n], &csv[last + 1], i - last - 1);
|
||||||
|
list->names[n][i - last - 1] = '\0';
|
||||||
|
n++;
|
||||||
|
last = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Interface: */
|
||||||
|
|
||||||
|
int cpuid_get_total_cpus(void)
|
||||||
|
{
|
||||||
|
return get_total_cpus();
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpuid_present(void)
|
||||||
|
{
|
||||||
|
return cpuid_exists_by_eflags();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_exec_cpuid(uint32_t eax, uint32_t* regs)
|
||||||
|
{
|
||||||
|
regs[0] = eax;
|
||||||
|
regs[1] = regs[2] = regs[3] = 0;
|
||||||
|
exec_cpuid(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_exec_cpuid_ext(uint32_t* regs)
|
||||||
|
{
|
||||||
|
exec_cpuid(regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpuid_get_raw_data(struct cpu_raw_data_t* data)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
if (!cpuid_present())
|
||||||
|
return set_error(ERR_NO_CPUID);
|
||||||
|
for (i = 0; i < 32; i++)
|
||||||
|
cpu_exec_cpuid(i, data->basic_cpuid[i]);
|
||||||
|
for (i = 0; i < 32; i++)
|
||||||
|
cpu_exec_cpuid(0x80000000 + i, data->ext_cpuid[i]);
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
memset(data->intel_fn4[i], 0, sizeof(data->intel_fn4[i]));
|
||||||
|
data->intel_fn4[i][0] = 4;
|
||||||
|
data->intel_fn4[i][2] = i;
|
||||||
|
cpu_exec_cpuid_ext(data->intel_fn4[i]);
|
||||||
|
}
|
||||||
|
for (i = 0; i < MAX_INTELFN11_LEVEL; i++) {
|
||||||
|
memset(data->intel_fn11[i], 0, sizeof(data->intel_fn11[i]));
|
||||||
|
data->intel_fn11[i][0] = 11;
|
||||||
|
data->intel_fn11[i][2] = i;
|
||||||
|
cpu_exec_cpuid_ext(data->intel_fn11[i]);
|
||||||
|
}
|
||||||
|
return set_error(ERR_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (!strcmp(filename, ""))
|
||||||
|
f = stdout;
|
||||||
|
else
|
||||||
|
f = fopen(filename, "wt");
|
||||||
|
if (!f) return set_error(ERR_OPEN);
|
||||||
|
|
||||||
|
fprintf(f, "version=%s\n", VERSION);
|
||||||
|
for (i = 0; i < MAX_CPUID_LEVEL; i++)
|
||||||
|
fprintf(f, "basic_cpuid[%d]=%08x %08x %08x %08x\n", i,
|
||||||
|
data->basic_cpuid[i][0], data->basic_cpuid[i][1],
|
||||||
|
data->basic_cpuid[i][2], data->basic_cpuid[i][3]);
|
||||||
|
for (i = 0; i < MAX_EXT_CPUID_LEVEL; i++)
|
||||||
|
fprintf(f, "ext_cpuid[%d]=%08x %08x %08x %08x\n", i,
|
||||||
|
data->ext_cpuid[i][0], data->ext_cpuid[i][1],
|
||||||
|
data->ext_cpuid[i][2], data->ext_cpuid[i][3]);
|
||||||
|
for (i = 0; i < MAX_INTELFN4_LEVEL; i++)
|
||||||
|
fprintf(f, "intel_fn4[%d]=%08x %08x %08x %08x\n", i,
|
||||||
|
data->intel_fn4[i][0], data->intel_fn4[i][1],
|
||||||
|
data->intel_fn4[i][2], data->intel_fn4[i][3]);
|
||||||
|
for (i = 0; i < MAX_INTELFN11_LEVEL; i++)
|
||||||
|
fprintf(f, "intel_fn11[%d]=%08x %08x %08x %08x\n", i,
|
||||||
|
data->intel_fn11[i][0], data->intel_fn11[i][1],
|
||||||
|
data->intel_fn11[i][2], data->intel_fn11[i][3]);
|
||||||
|
|
||||||
|
if (strcmp(filename, ""))
|
||||||
|
fclose(f);
|
||||||
|
return set_error(ERR_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename)
|
||||||
|
{
|
||||||
|
int i, len;
|
||||||
|
char line[100];
|
||||||
|
char token[100];
|
||||||
|
char *value;
|
||||||
|
int syntax;
|
||||||
|
int cur_line = 0;
|
||||||
|
int recognized;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
raw_data_t_constructor(data);
|
||||||
|
|
||||||
|
if (!strcmp(filename, ""))
|
||||||
|
f = stdin;
|
||||||
|
else
|
||||||
|
f = fopen(filename, "rt");
|
||||||
|
if (!f) return set_error(ERR_OPEN);
|
||||||
|
while (fgets(line, sizeof(line), f)) {
|
||||||
|
++cur_line;
|
||||||
|
len = (int) strlen(line);
|
||||||
|
if (len < 2) continue;
|
||||||
|
if (line[len - 1] == '\n')
|
||||||
|
line[--len] = '\0';
|
||||||
|
for (i = 0; i < len && line[i] != '='; i++)
|
||||||
|
if (i >= len && i < 1 && len - i - 1 <= 0) {
|
||||||
|
fclose(f);
|
||||||
|
return set_error(ERR_BADFMT);
|
||||||
|
}
|
||||||
|
strncpy(token, line, i);
|
||||||
|
token[i] = '\0';
|
||||||
|
value = &line[i + 1];
|
||||||
|
/* try to recognize the line */
|
||||||
|
recognized = 0;
|
||||||
|
if (!strcmp(token, "version") || !strcmp(token, "build_date")) {
|
||||||
|
recognized = 1;
|
||||||
|
}
|
||||||
|
syntax = 1;
|
||||||
|
syntax = syntax && parse_token("basic_cpuid", token, value, data->basic_cpuid, 32, &recognized);
|
||||||
|
syntax = syntax && parse_token("ext_cpuid", token, value, data->ext_cpuid, 32, &recognized);
|
||||||
|
syntax = syntax && parse_token("intel_fn4", token, value, data->intel_fn4, 4, &recognized);
|
||||||
|
syntax = syntax && parse_token("intel_fn11", token, value, data->intel_fn11, 4, &recognized);
|
||||||
|
if (!syntax) {
|
||||||
|
warnf("Error: %s:%d: Syntax error\n", filename, cur_line);
|
||||||
|
fclose(f);
|
||||||
|
return set_error(ERR_BADFMT);
|
||||||
|
}
|
||||||
|
if (!recognized) {
|
||||||
|
warnf("Warning: %s:%d not understood!\n", filename, cur_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(filename, ""))
|
||||||
|
fclose(f);
|
||||||
|
return set_error(ERR_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct cpu_raw_data_t myraw;
|
||||||
|
if (!raw) {
|
||||||
|
if ((r = cpuid_get_raw_data(&myraw)) < 0)
|
||||||
|
return set_error(r);
|
||||||
|
raw = &myraw;
|
||||||
|
}
|
||||||
|
cpu_id_t_constructor(data);
|
||||||
|
if ((r = cpuid_basic_identify(raw, data)) < 0)
|
||||||
|
return set_error(r);
|
||||||
|
switch (data->vendor) {
|
||||||
|
case VENDOR_INTEL:
|
||||||
|
r = cpuid_identify_intel(raw, data);
|
||||||
|
break;
|
||||||
|
case VENDOR_AMD:
|
||||||
|
r = cpuid_identify_amd(raw, data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return set_error(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* cpu_feature_str(cpu_feature_t feature)
|
||||||
|
{
|
||||||
|
const struct { cpu_feature_t feature; const char* name; }
|
||||||
|
matchtable[] = {
|
||||||
|
{ CPU_FEATURE_FPU, "fpu" },
|
||||||
|
{ CPU_FEATURE_VME, "vme" },
|
||||||
|
{ CPU_FEATURE_DE, "de" },
|
||||||
|
{ CPU_FEATURE_PSE, "pse" },
|
||||||
|
{ CPU_FEATURE_TSC, "tsc" },
|
||||||
|
{ CPU_FEATURE_MSR, "msr" },
|
||||||
|
{ CPU_FEATURE_PAE, "pae" },
|
||||||
|
{ CPU_FEATURE_MCE, "mce" },
|
||||||
|
{ CPU_FEATURE_CX8, "cx8" },
|
||||||
|
{ CPU_FEATURE_APIC, "apic" },
|
||||||
|
{ CPU_FEATURE_MTRR, "mtrr" },
|
||||||
|
{ CPU_FEATURE_SEP, "sep" },
|
||||||
|
{ CPU_FEATURE_PGE, "pge" },
|
||||||
|
{ CPU_FEATURE_MCA, "mca" },
|
||||||
|
{ CPU_FEATURE_CMOV, "cmov" },
|
||||||
|
{ CPU_FEATURE_PAT, "pat" },
|
||||||
|
{ CPU_FEATURE_PSE36, "pse36" },
|
||||||
|
{ CPU_FEATURE_PN, "pn" },
|
||||||
|
{ CPU_FEATURE_CLFLUSH, "clflush" },
|
||||||
|
{ CPU_FEATURE_DTS, "dts" },
|
||||||
|
{ CPU_FEATURE_ACPI, "acpi" },
|
||||||
|
{ CPU_FEATURE_MMX, "mmx" },
|
||||||
|
{ CPU_FEATURE_FXSR, "fxsr" },
|
||||||
|
{ CPU_FEATURE_SSE, "sse" },
|
||||||
|
{ CPU_FEATURE_SSE2, "sse2" },
|
||||||
|
{ CPU_FEATURE_SS, "ss" },
|
||||||
|
{ CPU_FEATURE_HT, "ht" },
|
||||||
|
{ CPU_FEATURE_TM, "tm" },
|
||||||
|
{ CPU_FEATURE_IA64, "ia64" },
|
||||||
|
{ CPU_FEATURE_PBE, "pbe" },
|
||||||
|
{ CPU_FEATURE_PNI, "pni" },
|
||||||
|
{ CPU_FEATURE_PCLMUL, "pclmul" },
|
||||||
|
{ CPU_FEATURE_DTS64, "dts64" },
|
||||||
|
{ CPU_FEATURE_MONITOR, "monitor" },
|
||||||
|
{ CPU_FEATURE_DS_CPL, "ds_cpl" },
|
||||||
|
{ CPU_FEATURE_VMX, "vmx" },
|
||||||
|
{ CPU_FEATURE_SMX, "smx" },
|
||||||
|
{ CPU_FEATURE_EST, "est" },
|
||||||
|
{ CPU_FEATURE_TM2, "tm2" },
|
||||||
|
{ CPU_FEATURE_SSSE3, "ssse3" },
|
||||||
|
{ CPU_FEATURE_CID, "cid" },
|
||||||
|
{ CPU_FEATURE_CX16, "cx16" },
|
||||||
|
{ CPU_FEATURE_XTPR, "xtpr" },
|
||||||
|
{ CPU_FEATURE_PDCM, "pdcm" },
|
||||||
|
{ CPU_FEATURE_DCA, "dca" },
|
||||||
|
{ CPU_FEATURE_SSE4_1, "sse4_1" },
|
||||||
|
{ CPU_FEATURE_SSE4_2, "sse4_2" },
|
||||||
|
{ CPU_FEATURE_SYSCALL, "syscall" },
|
||||||
|
{ CPU_FEATURE_XD, "xd" },
|
||||||
|
{ CPU_FEATURE_X2APIC, "x2apic"},
|
||||||
|
{ CPU_FEATURE_MOVBE, "movbe" },
|
||||||
|
{ CPU_FEATURE_POPCNT, "popcnt" },
|
||||||
|
{ CPU_FEATURE_AES, "aes" },
|
||||||
|
{ CPU_FEATURE_XSAVE, "xsave" },
|
||||||
|
{ CPU_FEATURE_OSXSAVE, "osxsave" },
|
||||||
|
{ CPU_FEATURE_AVX, "avx" },
|
||||||
|
{ CPU_FEATURE_MMXEXT, "mmxext" },
|
||||||
|
{ CPU_FEATURE_3DNOW, "3dnow" },
|
||||||
|
{ CPU_FEATURE_3DNOWEXT, "3dnowext" },
|
||||||
|
{ CPU_FEATURE_NX, "nx" },
|
||||||
|
{ CPU_FEATURE_FXSR_OPT, "fxsr_opt" },
|
||||||
|
{ CPU_FEATURE_RDTSCP, "rdtscp" },
|
||||||
|
{ CPU_FEATURE_LM, "lm" },
|
||||||
|
{ CPU_FEATURE_LAHF_LM, "lahf_lm" },
|
||||||
|
{ CPU_FEATURE_CMP_LEGACY, "cmp_legacy" },
|
||||||
|
{ CPU_FEATURE_SVM, "svm" },
|
||||||
|
{ CPU_FEATURE_SSE4A, "sse4a" },
|
||||||
|
{ CPU_FEATURE_MISALIGNSSE, "misalignsse" },
|
||||||
|
{ CPU_FEATURE_ABM, "abm" },
|
||||||
|
{ CPU_FEATURE_3DNOWPREFETCH, "3dnowprefetch" },
|
||||||
|
{ CPU_FEATURE_OSVW, "osvw" },
|
||||||
|
{ CPU_FEATURE_IBS, "ibs" },
|
||||||
|
{ CPU_FEATURE_SSE5, "sse5" },
|
||||||
|
{ CPU_FEATURE_SKINIT, "skinit" },
|
||||||
|
{ CPU_FEATURE_WDT, "wdt" },
|
||||||
|
{ CPU_FEATURE_TS, "ts" },
|
||||||
|
{ CPU_FEATURE_FID, "fid" },
|
||||||
|
{ CPU_FEATURE_VID, "vid" },
|
||||||
|
{ CPU_FEATURE_TTP, "ttp" },
|
||||||
|
{ CPU_FEATURE_TM_AMD, "tm_amd" },
|
||||||
|
{ CPU_FEATURE_STC, "stc" },
|
||||||
|
{ CPU_FEATURE_100MHZSTEPS, "100mhzsteps" },
|
||||||
|
{ CPU_FEATURE_HWPSTATE, "hwpstate" },
|
||||||
|
{ CPU_FEATURE_CONSTANT_TSC, "constant_tsc" },
|
||||||
|
{ CPU_FEATURE_XOP, "xop" },
|
||||||
|
{ CPU_FEATURE_FMA3, "fma3" },
|
||||||
|
{ CPU_FEATURE_FMA4, "fma4" },
|
||||||
|
{ CPU_FEATURE_TBM, "tbm" },
|
||||||
|
{ CPU_FEATURE_F16C, "f16c" },
|
||||||
|
{ CPU_FEATURE_RDRAND, "rdrand" },
|
||||||
|
{ CPU_FEATURE_CPB, "cpb" },
|
||||||
|
{ CPU_FEATURE_APERFMPERF, "aperfmperf" },
|
||||||
|
{ CPU_FEATURE_PFI, "pfi" },
|
||||||
|
{ CPU_FEATURE_PA, "pa" },
|
||||||
|
{ CPU_FEATURE_AVX2, "avx2" },
|
||||||
|
};
|
||||||
|
unsigned i, n = COUNT_OF(matchtable);
|
||||||
|
if (n != NUM_CPU_FEATURES) {
|
||||||
|
warnf("Warning: incomplete library, feature matchtable size differs from the actual number of features.\n");
|
||||||
|
}
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
if (matchtable[i].feature == feature)
|
||||||
|
return matchtable[i].name;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* cpuid_error(void)
|
||||||
|
{
|
||||||
|
const struct { cpu_error_t error; const char *description; }
|
||||||
|
matchtable[] = {
|
||||||
|
{ ERR_OK , "No error"},
|
||||||
|
{ ERR_NO_CPUID , "CPUID instruction is not supported"},
|
||||||
|
{ ERR_NO_RDTSC , "RDTSC instruction is not supported"},
|
||||||
|
{ ERR_NO_MEM , "Memory allocation failed"},
|
||||||
|
{ ERR_OPEN , "File open operation failed"},
|
||||||
|
{ ERR_BADFMT , "Bad file format"},
|
||||||
|
{ ERR_NOT_IMP , "Not implemented"},
|
||||||
|
{ ERR_CPU_UNKN , "Unsupported processor"},
|
||||||
|
};
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < COUNT_OF(matchtable); i++)
|
||||||
|
if (_libcpiud_errno == matchtable[i].error)
|
||||||
|
return matchtable[i].description;
|
||||||
|
return "Unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* cpuid_lib_version(void)
|
||||||
|
{
|
||||||
|
return VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
libcpuid_warn_fn_t cpuid_set_warn_function(libcpuid_warn_fn_t new_fn)
|
||||||
|
{
|
||||||
|
libcpuid_warn_fn_t ret = _warn_fun;
|
||||||
|
_warn_fun = new_fn;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpuid_set_verbosiness_level(int level)
|
||||||
|
{
|
||||||
|
_current_verboselevel = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpuid_get_cpu_list(cpu_vendor_t vendor, struct cpu_list_t* list)
|
||||||
|
{
|
||||||
|
switch (vendor) {
|
||||||
|
case VENDOR_INTEL:
|
||||||
|
cpuid_get_list_intel(list);
|
||||||
|
break;
|
||||||
|
case VENDOR_AMD:
|
||||||
|
cpuid_get_list_amd(list);
|
||||||
|
break;
|
||||||
|
case VENDOR_CYRIX:
|
||||||
|
make_list_from_string("Cx486,Cx5x86,6x86,6x86MX,M II,MediaGX,MediaGXi,MediaGXm", list);
|
||||||
|
break;
|
||||||
|
case VENDOR_NEXGEN:
|
||||||
|
make_list_from_string("Nx586", list);
|
||||||
|
break;
|
||||||
|
case VENDOR_TRANSMETA:
|
||||||
|
make_list_from_string("Crusoe,Efficeon", list);
|
||||||
|
break;
|
||||||
|
case VENDOR_UMC:
|
||||||
|
make_list_from_string("UMC x86 CPU", list);
|
||||||
|
break;
|
||||||
|
case VENDOR_CENTAUR:
|
||||||
|
make_list_from_string("VIA C3,VIA C7,VIA Nano", list);
|
||||||
|
break;
|
||||||
|
case VENDOR_RISE:
|
||||||
|
make_list_from_string("Rise mP6", list);
|
||||||
|
break;
|
||||||
|
case VENDOR_SIS:
|
||||||
|
make_list_from_string("SiS mP6", list);
|
||||||
|
break;
|
||||||
|
case VENDOR_NSC:
|
||||||
|
make_list_from_string("Geode GXm,Geode GXLV,Geode GX1,Geode GX2", list);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warnf("Unknown vendor passed to cpuid_get_cpu_list()\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpuid_free_cpu_list(struct cpu_list_t* list)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (list->num_entries <= 0) return;
|
||||||
|
for (i = 0; i < list->num_entries; i++)
|
||||||
|
free(list->names[i]);
|
||||||
|
free(list->names);
|
||||||
|
}
|
885
contrib/libcpuid/include/cpuid/libcpuid.h
Normal file
885
contrib/libcpuid/include/cpuid/libcpuid.h
Normal file
@ -0,0 +1,885 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __LIBCPUID_H__
|
||||||
|
#define __LIBCPUID_H__
|
||||||
|
/**
|
||||||
|
* @File libcpuid.h
|
||||||
|
* @Author Veselin Georgiev
|
||||||
|
* @Date Oct 2008
|
||||||
|
* @Version 0.2.1
|
||||||
|
*
|
||||||
|
* Version history:
|
||||||
|
*
|
||||||
|
* 0.1.0 (2008-10-15): initial adaptation from wxfractgui sources
|
||||||
|
* 0.1.1 (2009-07-06): Added intel_fn11 fields to cpu_raw_data_t to handle
|
||||||
|
* new processor topology enumeration required on Core i7
|
||||||
|
* 0.1.2 (2009-09-26): Added support for MSR reading through self-extracting
|
||||||
|
* kernel driver on Win32.
|
||||||
|
* 0.1.3 (2010-04-20): Added support for greater more accurate CPU clock
|
||||||
|
* measurements with cpu_clock_by_ic()
|
||||||
|
* 0.2.0 (2011-10-11): Support for AMD Bulldozer CPUs, 128-bit SSE unit size
|
||||||
|
* checking. A backwards-incompatible change, since the
|
||||||
|
* sizeof cpu_id_t is now different.
|
||||||
|
* 0.2.1 (2012-05-26): Support for Ivy Bridge, and detecting the presence of
|
||||||
|
* the RdRand instruction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @mainpage A simple libcpuid introduction
|
||||||
|
*
|
||||||
|
* LibCPUID provides CPU identification and access to the CPUID and RDTSC
|
||||||
|
* instructions on the x86.
|
||||||
|
* <p>
|
||||||
|
* To execute CPUID, use \ref cpu_exec_cpuid <br>
|
||||||
|
* To execute RDTSC, use \ref cpu_rdtsc <br>
|
||||||
|
* To fetch the CPUID info needed for CPU identification, use
|
||||||
|
* \ref cpuid_get_raw_data <br>
|
||||||
|
* To make sense of that data (decode, extract features), use \ref cpu_identify <br>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup libcpuid LibCPUID
|
||||||
|
@{ */
|
||||||
|
|
||||||
|
/* Include some integer type specifications: */
|
||||||
|
#include "libcpuid_types.h"
|
||||||
|
|
||||||
|
/* Some limits and other constants */
|
||||||
|
#include "libcpuid_constants.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CPU vendor, as guessed from the Vendor String.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
VENDOR_INTEL = 0, /*!< Intel CPU */
|
||||||
|
VENDOR_AMD, /*!< AMD CPU */
|
||||||
|
VENDOR_CYRIX, /*!< Cyrix CPU */
|
||||||
|
VENDOR_NEXGEN, /*!< NexGen CPU */
|
||||||
|
VENDOR_TRANSMETA, /*!< Transmeta CPU */
|
||||||
|
VENDOR_UMC, /*!< x86 CPU by UMC */
|
||||||
|
VENDOR_CENTAUR, /*!< x86 CPU by IDT */
|
||||||
|
VENDOR_RISE, /*!< x86 CPU by Rise Technology */
|
||||||
|
VENDOR_SIS, /*!< x86 CPU by SiS */
|
||||||
|
VENDOR_NSC, /*!< x86 CPU by National Semiconductor */
|
||||||
|
|
||||||
|
NUM_CPU_VENDORS, /*!< Valid CPU vendor ids: 0..NUM_CPU_VENDORS - 1 */
|
||||||
|
VENDOR_UNKNOWN = -1,
|
||||||
|
} cpu_vendor_t;
|
||||||
|
#define NUM_CPU_VENDORS NUM_CPU_VENDORS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Contains just the raw CPUID data.
|
||||||
|
*
|
||||||
|
* This contains only the most basic CPU data, required to do identification
|
||||||
|
* and feature recognition. Every processor should be identifiable using this
|
||||||
|
* data only.
|
||||||
|
*/
|
||||||
|
struct cpu_raw_data_t {
|
||||||
|
/** contains results of CPUID for eax = 0, 1, ...*/
|
||||||
|
uint32_t basic_cpuid[MAX_CPUID_LEVEL][4];
|
||||||
|
|
||||||
|
/** contains results of CPUID for eax = 0x80000000, 0x80000001, ...*/
|
||||||
|
uint32_t ext_cpuid[MAX_EXT_CPUID_LEVEL][4];
|
||||||
|
|
||||||
|
/** when the CPU is intel and it supports deterministic cache
|
||||||
|
information: this contains the results of CPUID for eax = 4
|
||||||
|
and ecx = 0, 1, ... */
|
||||||
|
uint32_t intel_fn4[MAX_INTELFN4_LEVEL][4];
|
||||||
|
|
||||||
|
/** when the CPU is intel and it supports leaf 0Bh (Extended Topology
|
||||||
|
enumeration leaf), this stores the result of CPUID with
|
||||||
|
eax = 11 and ecx = 0, 1, 2... */
|
||||||
|
uint32_t intel_fn11[MAX_INTELFN11_LEVEL][4];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This contains the recognized CPU features/info
|
||||||
|
*/
|
||||||
|
struct cpu_id_t {
|
||||||
|
/** contains the CPU vendor string, e.g. "GenuineIntel" */
|
||||||
|
char vendor_str[VENDOR_STR_MAX];
|
||||||
|
|
||||||
|
/** contains the brand string, e.g. "Intel(R) Xeon(TM) CPU 2.40GHz" */
|
||||||
|
char brand_str[BRAND_STR_MAX];
|
||||||
|
|
||||||
|
/** contains the recognized CPU vendor */
|
||||||
|
cpu_vendor_t vendor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contain CPU flags. Used to test for features. See
|
||||||
|
* the CPU_FEATURE_* macros below. @see Features
|
||||||
|
*/
|
||||||
|
uint8_t flags[CPU_FLAGS_MAX];
|
||||||
|
|
||||||
|
/** CPU family */
|
||||||
|
int32_t family;
|
||||||
|
|
||||||
|
/** CPU model */
|
||||||
|
int32_t model;
|
||||||
|
|
||||||
|
/** CPU stepping */
|
||||||
|
int32_t stepping;
|
||||||
|
|
||||||
|
/** CPU extended family */
|
||||||
|
int32_t ext_family;
|
||||||
|
|
||||||
|
/** CPU extended model */
|
||||||
|
int32_t ext_model;
|
||||||
|
|
||||||
|
/** Number of CPU cores on the current processor */
|
||||||
|
int32_t num_cores;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of logical processors on the current processor.
|
||||||
|
* Could be more than the number of physical cores,
|
||||||
|
* e.g. when the processor has HyperThreading.
|
||||||
|
*/
|
||||||
|
int32_t num_logical_cpus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of logical processors.
|
||||||
|
*
|
||||||
|
* This is num_logical_cpus * {total physical processors in the system}
|
||||||
|
*
|
||||||
|
* If you're writing a multithreaded program and you want to run it on
|
||||||
|
* all CPUs, this is the number of threads you need.
|
||||||
|
*/
|
||||||
|
int32_t total_logical_cpus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* L1 data cache size in KB. Could be zero, if the CPU lacks cache.
|
||||||
|
* If the size cannot be determined, it will be -1.
|
||||||
|
*/
|
||||||
|
int32_t l1_data_cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* L1 instruction cache size in KB. Could be zero, if the CPU lacks
|
||||||
|
* cache. If the size cannot be determined, it will be -1.
|
||||||
|
* @note On some Intel CPUs, whose instruction cache is in fact
|
||||||
|
* a trace cache, the size will be expressed in K uOps.
|
||||||
|
*/
|
||||||
|
int32_t l1_instruction_cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* L2 cache size in KB. Could be zero, if the CPU lacks L2 cache.
|
||||||
|
* If the size of the cache could not be determined, it will be -1
|
||||||
|
*/
|
||||||
|
int32_t l2_cache;
|
||||||
|
|
||||||
|
/** L3 cache size in KB. Zero on most systems */
|
||||||
|
int32_t l3_cache;
|
||||||
|
|
||||||
|
/** Cache associativity for the L1 data cache. -1 if undetermined */
|
||||||
|
int32_t l1_assoc;
|
||||||
|
|
||||||
|
/** Cache associativity for the L2 cache. -1 if undetermined */
|
||||||
|
int32_t l2_assoc;
|
||||||
|
|
||||||
|
/** Cache associativity for the L3 cache. -1 if undetermined */
|
||||||
|
int32_t l3_assoc;
|
||||||
|
|
||||||
|
/** Cache-line size for L1 data cache. -1 if undetermined */
|
||||||
|
int32_t l1_cacheline;
|
||||||
|
|
||||||
|
/** Cache-line size for L2 cache. -1 if undetermined */
|
||||||
|
int32_t l2_cacheline;
|
||||||
|
|
||||||
|
/** Cache-line size for L3 cache. -1 if undetermined */
|
||||||
|
int32_t l3_cacheline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The brief and human-friendly CPU codename, which was recognized.<br>
|
||||||
|
* Examples:
|
||||||
|
* @code
|
||||||
|
* +--------+--------+-------+-------+-------+---------------------------------------+-----------------------+
|
||||||
|
* | Vendor | Family | Model | Step. | Cache | Brand String | cpu_id_t.cpu_codename |
|
||||||
|
* +--------+--------+-------+-------+-------+---------------------------------------+-----------------------+
|
||||||
|
* | AMD | 6 | 8 | 0 | 256 | (not available - will be ignored) | "K6-2" |
|
||||||
|
* | Intel | 15 | 2 | 5 | 512 | "Intel(R) Xeon(TM) CPU 2.40GHz" | "Xeon (Prestonia)" |
|
||||||
|
* | Intel | 6 | 15 | 11 | 4096 | "Intel(R) Core(TM)2 Duo CPU E6550..." | "Conroe (Core 2 Duo)" |
|
||||||
|
* | AMD | 15 | 35 | 2 | 1024 | "Dual Core AMD Opteron(tm) Proces..." | "Opteron (Dual Core)" |
|
||||||
|
* +--------+--------+-------+-------+-------+---------------------------------------+-----------------------+
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
char cpu_codename[64];
|
||||||
|
|
||||||
|
/** SSE execution unit size (64 or 128; -1 if N/A) */
|
||||||
|
int32_t sse_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contain miscellaneous detection information. Used to test about specifics of
|
||||||
|
* certain detected features. See CPU_HINT_* macros below. @see Hints
|
||||||
|
*/
|
||||||
|
uint8_t detection_hints[CPU_HINTS_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CPU feature identifiers
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* @code
|
||||||
|
* ...
|
||||||
|
* struct cpu_raw_data_t raw;
|
||||||
|
* struct cpu_id_t id;
|
||||||
|
* if (cpuid_get_raw_data(&raw) == 0 && cpu_identify(&raw, &id) == 0) {
|
||||||
|
* if (id.flags[CPU_FEATURE_SSE2]) {
|
||||||
|
* // The CPU has SSE2...
|
||||||
|
* ...
|
||||||
|
* } else {
|
||||||
|
* // no SSE2
|
||||||
|
* }
|
||||||
|
* } else {
|
||||||
|
* // processor cannot be determined.
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
CPU_FEATURE_FPU = 0, /*!< Floating point unit */
|
||||||
|
CPU_FEATURE_VME, /*!< Virtual mode extension */
|
||||||
|
CPU_FEATURE_DE, /*!< Debugging extension */
|
||||||
|
CPU_FEATURE_PSE, /*!< Page size extension */
|
||||||
|
CPU_FEATURE_TSC, /*!< Time-stamp counter */
|
||||||
|
CPU_FEATURE_MSR, /*!< Model-specific regsisters, RDMSR/WRMSR supported */
|
||||||
|
CPU_FEATURE_PAE, /*!< Physical address extension */
|
||||||
|
CPU_FEATURE_MCE, /*!< Machine check exception */
|
||||||
|
CPU_FEATURE_CX8, /*!< CMPXCHG8B instruction supported */
|
||||||
|
CPU_FEATURE_APIC, /*!< APIC support */
|
||||||
|
CPU_FEATURE_MTRR, /*!< Memory type range registers */
|
||||||
|
CPU_FEATURE_SEP, /*!< SYSENTER / SYSEXIT instructions supported */
|
||||||
|
CPU_FEATURE_PGE, /*!< Page global enable */
|
||||||
|
CPU_FEATURE_MCA, /*!< Machine check architecture */
|
||||||
|
CPU_FEATURE_CMOV, /*!< CMOVxx instructions supported */
|
||||||
|
CPU_FEATURE_PAT, /*!< Page attribute table */
|
||||||
|
CPU_FEATURE_PSE36, /*!< 36-bit page address extension */
|
||||||
|
CPU_FEATURE_PN, /*!< Processor serial # implemented (Intel P3 only) */
|
||||||
|
CPU_FEATURE_CLFLUSH, /*!< CLFLUSH instruction supported */
|
||||||
|
CPU_FEATURE_DTS, /*!< Debug store supported */
|
||||||
|
CPU_FEATURE_ACPI, /*!< ACPI support (power states) */
|
||||||
|
CPU_FEATURE_MMX, /*!< MMX instruction set supported */
|
||||||
|
CPU_FEATURE_FXSR, /*!< FXSAVE / FXRSTOR supported */
|
||||||
|
CPU_FEATURE_SSE, /*!< Streaming-SIMD Extensions (SSE) supported */
|
||||||
|
CPU_FEATURE_SSE2, /*!< SSE2 instructions supported */
|
||||||
|
CPU_FEATURE_SS, /*!< Self-snoop */
|
||||||
|
CPU_FEATURE_HT, /*!< Hyper-threading supported (but might be disabled) */
|
||||||
|
CPU_FEATURE_TM, /*!< Thermal monitor */
|
||||||
|
CPU_FEATURE_IA64, /*!< IA64 supported (Itanium only) */
|
||||||
|
CPU_FEATURE_PBE, /*!< Pending-break enable */
|
||||||
|
CPU_FEATURE_PNI, /*!< PNI (SSE3) instructions supported */
|
||||||
|
CPU_FEATURE_PCLMUL, /*!< PCLMULQDQ instruction supported */
|
||||||
|
CPU_FEATURE_DTS64, /*!< 64-bit Debug store supported */
|
||||||
|
CPU_FEATURE_MONITOR, /*!< MONITOR / MWAIT supported */
|
||||||
|
CPU_FEATURE_DS_CPL, /*!< CPL Qualified Debug Store */
|
||||||
|
CPU_FEATURE_VMX, /*!< Virtualization technology supported */
|
||||||
|
CPU_FEATURE_SMX, /*!< Safer mode exceptions */
|
||||||
|
CPU_FEATURE_EST, /*!< Enhanced SpeedStep */
|
||||||
|
CPU_FEATURE_TM2, /*!< Thermal monitor 2 */
|
||||||
|
CPU_FEATURE_SSSE3, /*!< SSSE3 instructionss supported (this is different from SSE3!) */
|
||||||
|
CPU_FEATURE_CID, /*!< Context ID supported */
|
||||||
|
CPU_FEATURE_CX16, /*!< CMPXCHG16B instruction supported */
|
||||||
|
CPU_FEATURE_XTPR, /*!< Send Task Priority Messages disable */
|
||||||
|
CPU_FEATURE_PDCM, /*!< Performance capabilities MSR supported */
|
||||||
|
CPU_FEATURE_DCA, /*!< Direct cache access supported */
|
||||||
|
CPU_FEATURE_SSE4_1, /*!< SSE 4.1 instructions supported */
|
||||||
|
CPU_FEATURE_SSE4_2, /*!< SSE 4.2 instructions supported */
|
||||||
|
CPU_FEATURE_SYSCALL, /*!< SYSCALL / SYSRET instructions supported */
|
||||||
|
CPU_FEATURE_XD, /*!< Execute disable bit supported */
|
||||||
|
CPU_FEATURE_MOVBE, /*!< MOVBE instruction supported */
|
||||||
|
CPU_FEATURE_POPCNT, /*!< POPCNT instruction supported */
|
||||||
|
CPU_FEATURE_AES, /*!< AES* instructions supported */
|
||||||
|
CPU_FEATURE_XSAVE, /*!< XSAVE/XRSTOR/etc instructions supported */
|
||||||
|
CPU_FEATURE_OSXSAVE, /*!< non-privileged copy of OSXSAVE supported */
|
||||||
|
CPU_FEATURE_AVX, /*!< Advanced vector extensions supported */
|
||||||
|
CPU_FEATURE_MMXEXT, /*!< AMD MMX-extended instructions supported */
|
||||||
|
CPU_FEATURE_3DNOW, /*!< AMD 3DNow! instructions supported */
|
||||||
|
CPU_FEATURE_3DNOWEXT, /*!< AMD 3DNow! extended instructions supported */
|
||||||
|
CPU_FEATURE_NX, /*!< No-execute bit supported */
|
||||||
|
CPU_FEATURE_FXSR_OPT, /*!< FFXSR: FXSAVE and FXRSTOR optimizations */
|
||||||
|
CPU_FEATURE_RDTSCP, /*!< RDTSCP instruction supported (AMD-only) */
|
||||||
|
CPU_FEATURE_LM, /*!< Long mode (x86_64/EM64T) supported */
|
||||||
|
CPU_FEATURE_LAHF_LM, /*!< LAHF/SAHF supported in 64-bit mode */
|
||||||
|
CPU_FEATURE_CMP_LEGACY, /*!< core multi-processing legacy mode */
|
||||||
|
CPU_FEATURE_SVM, /*!< AMD Secure virtual machine */
|
||||||
|
CPU_FEATURE_ABM, /*!< LZCNT instruction support */
|
||||||
|
CPU_FEATURE_MISALIGNSSE,/*!< Misaligned SSE supported */
|
||||||
|
CPU_FEATURE_SSE4A, /*!< SSE 4a from AMD */
|
||||||
|
CPU_FEATURE_3DNOWPREFETCH, /*!< PREFETCH/PREFETCHW support */
|
||||||
|
CPU_FEATURE_OSVW, /*!< OS Visible Workaround (AMD) */
|
||||||
|
CPU_FEATURE_IBS, /*!< Instruction-based sampling */
|
||||||
|
CPU_FEATURE_SSE5, /*!< SSE 5 instructions supported (deprecated, will never be 1) */
|
||||||
|
CPU_FEATURE_SKINIT, /*!< SKINIT / STGI supported */
|
||||||
|
CPU_FEATURE_WDT, /*!< Watchdog timer support */
|
||||||
|
CPU_FEATURE_TS, /*!< Temperature sensor */
|
||||||
|
CPU_FEATURE_FID, /*!< Frequency ID control */
|
||||||
|
CPU_FEATURE_VID, /*!< Voltage ID control */
|
||||||
|
CPU_FEATURE_TTP, /*!< THERMTRIP */
|
||||||
|
CPU_FEATURE_TM_AMD, /*!< AMD-specified hardware thermal control */
|
||||||
|
CPU_FEATURE_STC, /*!< Software thermal control */
|
||||||
|
CPU_FEATURE_100MHZSTEPS,/*!< 100 MHz multiplier control */
|
||||||
|
CPU_FEATURE_HWPSTATE, /*!< Hardware P-state control */
|
||||||
|
CPU_FEATURE_CONSTANT_TSC, /*!< TSC ticks at constant rate */
|
||||||
|
CPU_FEATURE_XOP, /*!< The XOP instruction set (same as the old CPU_FEATURE_SSE5) */
|
||||||
|
CPU_FEATURE_FMA3, /*!< The FMA3 instruction set */
|
||||||
|
CPU_FEATURE_FMA4, /*!< The FMA4 instruction set */
|
||||||
|
CPU_FEATURE_TBM, /*!< Trailing bit manipulation instruction support */
|
||||||
|
CPU_FEATURE_F16C, /*!< 16-bit FP convert instruction support */
|
||||||
|
CPU_FEATURE_RDRAND, /*!< RdRand instruction */
|
||||||
|
CPU_FEATURE_X2APIC, /*!< x2APIC, APIC_BASE.EXTD, MSRs 0000_0800h...0000_0BFFh 64-bit ICR (+030h but not +031h), no DFR (+00Eh), SELF_IPI (+040h) also see standard level 0000_000Bh */
|
||||||
|
CPU_FEATURE_CPB, /*!< Core performance boost */
|
||||||
|
CPU_FEATURE_APERFMPERF, /*!< MPERF/APERF MSRs support */
|
||||||
|
CPU_FEATURE_PFI, /*!< Processor Feedback Interface support */
|
||||||
|
CPU_FEATURE_PA, /*!< Processor accumulator */
|
||||||
|
CPU_FEATURE_AVX2, /*!< AVX2 instructions */
|
||||||
|
/* termination: */
|
||||||
|
NUM_CPU_FEATURES,
|
||||||
|
} cpu_feature_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CPU detection hints identifiers
|
||||||
|
*
|
||||||
|
* Usage: similar to the flags usage
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
CPU_HINT_SSE_SIZE_AUTH = 0, /*!< SSE unit size is authoritative (not only a Family/Model guesswork, but based on an actual CPUID bit) */
|
||||||
|
/* termination */
|
||||||
|
NUM_CPU_HINTS,
|
||||||
|
} cpu_hint_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes common library error codes
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
ERR_OK = 0, /*!< "No error" */
|
||||||
|
ERR_NO_CPUID = -1, /*!< "CPUID instruction is not supported" */
|
||||||
|
ERR_NO_RDTSC = -2, /*!< "RDTSC instruction is not supported" */
|
||||||
|
ERR_NO_MEM = -3, /*!< "Memory allocation failed" */
|
||||||
|
ERR_OPEN = -4, /*!< "File open operation failed" */
|
||||||
|
ERR_BADFMT = -5, /*!< "Bad file format" */
|
||||||
|
ERR_NOT_IMP = -6, /*!< "Not implemented" */
|
||||||
|
ERR_CPU_UNKN = -7, /*!< "Unsupported processor" */
|
||||||
|
ERR_NO_RDMSR = -8, /*!< "RDMSR instruction is not supported" */
|
||||||
|
ERR_NO_DRIVER= -9, /*!< "RDMSR driver error (generic)" */
|
||||||
|
ERR_NO_PERMS = -10, /*!< "No permissions to install RDMSR driver" */
|
||||||
|
ERR_EXTRACT = -11, /*!< "Cannot extract RDMSR driver (read only media?)" */
|
||||||
|
ERR_HANDLE = -12, /*!< "Bad handle" */
|
||||||
|
ERR_INVMSR = -13, /*!< "Invalid MSR" */
|
||||||
|
} cpu_error_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal structure, used in cpu_tsc_mark, cpu_tsc_unmark and
|
||||||
|
* cpu_clock_by_mark
|
||||||
|
*/
|
||||||
|
struct cpu_mark_t {
|
||||||
|
uint64_t tsc; /*!< Time-stamp from RDTSC */
|
||||||
|
uint64_t sys_clock; /*!< In microsecond resolution */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the total number of CPUs even if CPUID is not present
|
||||||
|
* @retval Number of CPUs available
|
||||||
|
*/
|
||||||
|
int cpuid_get_total_cpus(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the CPUID instruction is supported
|
||||||
|
* @retval 1 if CPUID is present
|
||||||
|
* @retval 0 the CPU doesn't have CPUID.
|
||||||
|
*/
|
||||||
|
int cpuid_present(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Executes the CPUID instruction
|
||||||
|
* @param eax - the value of the EAX register when executing CPUID
|
||||||
|
* @param regs - the results will be stored here. regs[0] = EAX, regs[1] = EBX, ...
|
||||||
|
* @note CPUID will be executed with EAX set to the given value and EBX, ECX,
|
||||||
|
* EDX set to zero.
|
||||||
|
*/
|
||||||
|
void cpu_exec_cpuid(uint32_t eax, uint32_t* regs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Executes the CPUID instruction with the given input registers
|
||||||
|
* @note This is just a bit more generic version of cpu_exec_cpuid - it allows
|
||||||
|
* you to control all the registers.
|
||||||
|
* @param regs - Input/output. Prior to executing CPUID, EAX, EBX, ECX and
|
||||||
|
* EDX will be set to regs[0], regs[1], regs[2] and regs[3].
|
||||||
|
* After CPUID, this array will contain the results.
|
||||||
|
*/
|
||||||
|
void cpu_exec_cpuid_ext(uint32_t* regs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Obtains the raw CPUID data from the current CPU
|
||||||
|
* @param data - a pointer to cpu_raw_data_t structure
|
||||||
|
* @returns zero if successful, and some negative number on error.
|
||||||
|
* The error message can be obtained by calling \ref cpuid_error.
|
||||||
|
* @see cpu_error_t
|
||||||
|
*/
|
||||||
|
int cpuid_get_raw_data(struct cpu_raw_data_t* data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes the raw CPUID data to a text file
|
||||||
|
* @param data - a pointer to cpu_raw_data_t structure
|
||||||
|
* @param filename - the path of the file, where the serialized data should be
|
||||||
|
* written. If empty, stdout will be used.
|
||||||
|
* @note This is intended primarily for debugging. On some processor, which is
|
||||||
|
* not currently supported or not completely recognized by cpu_identify,
|
||||||
|
* one can still successfully get the raw data and write it to a file.
|
||||||
|
* libcpuid developers can later import this file and debug the detection
|
||||||
|
* code as if running on the actual hardware.
|
||||||
|
* The file is simple text format of "something=value" pairs. Version info
|
||||||
|
* is also written, but the format is not intended to be neither backward-
|
||||||
|
* nor forward compatible.
|
||||||
|
* @returns zero if successful, and some negative number on error.
|
||||||
|
* The error message can be obtained by calling \ref cpuid_error.
|
||||||
|
* @see cpu_error_t
|
||||||
|
*/
|
||||||
|
int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads raw CPUID data from file
|
||||||
|
* @param data - a pointer to cpu_raw_data_t structure. The deserialized data will
|
||||||
|
* be written here.
|
||||||
|
* @param filename - the path of the file, containing the serialized raw data.
|
||||||
|
* If empty, stdin will be used.
|
||||||
|
* @note This function may fail, if the file is created by different version of
|
||||||
|
* the library. Also, see the notes on cpuid_serialize_raw_data.
|
||||||
|
* @returns zero if successful, and some negative number on error.
|
||||||
|
* The error message can be obtained by calling \ref cpuid_error.
|
||||||
|
* @see cpu_error_t
|
||||||
|
*/
|
||||||
|
int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Identifies the CPU
|
||||||
|
* @param raw - Input - a pointer to the raw CPUID data, which is obtained
|
||||||
|
* either by cpuid_get_raw_data or cpuid_deserialize_raw_data.
|
||||||
|
* Can also be NULL, in which case the functions calls
|
||||||
|
* cpuid_get_raw_data itself.
|
||||||
|
* @param data - Output - the decoded CPU features/info is written here.
|
||||||
|
* @note The function will not fail, even if some of the information
|
||||||
|
* cannot be obtained. Even when the CPU is new and thus unknown to
|
||||||
|
* libcpuid, some generic info, such as "AMD K9 family CPU" will be
|
||||||
|
* written to data.cpu_codename, and most other things, such as the
|
||||||
|
* CPU flags, cache sizes, etc. should be detected correctly anyway.
|
||||||
|
* However, the function CAN fail, if the CPU is completely alien to
|
||||||
|
* libcpuid.
|
||||||
|
* @note While cpu_identify() and cpuid_get_raw_data() are fast for most
|
||||||
|
* purposes, running them several thousand times per second can hamper
|
||||||
|
* performance significantly. Specifically, avoid writing "cpu feature
|
||||||
|
* checker" wrapping function, which calls cpu_identify and returns the
|
||||||
|
* value of some flag, if that function is going to be called frequently.
|
||||||
|
* @returns zero if successful, and some negative number on error.
|
||||||
|
* The error message can be obtained by calling \ref cpuid_error.
|
||||||
|
* @see cpu_error_t
|
||||||
|
*/
|
||||||
|
int cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the short textual representation of a CPU flag
|
||||||
|
* @param feature - the feature, whose textual representation is wanted.
|
||||||
|
* @returns a constant string like "fpu", "tsc", "sse2", etc.
|
||||||
|
* @note the names of the returned flags are compatible with those from
|
||||||
|
* /proc/cpuinfo in Linux, with the exception of `tm_amd'
|
||||||
|
*/
|
||||||
|
const char* cpu_feature_str(cpu_feature_t feature);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns textual description of the last error
|
||||||
|
*
|
||||||
|
* libcpuid stores an `errno'-style error status, whose description
|
||||||
|
* can be obtained with this function.
|
||||||
|
* @note This function is not thread-safe
|
||||||
|
* @see cpu_error_t
|
||||||
|
*/
|
||||||
|
const char* cpuid_error(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Executes RDTSC
|
||||||
|
*
|
||||||
|
* The RDTSC (ReaD Time Stamp Counter) instruction gives access to an
|
||||||
|
* internal 64-bit counter, which usually increments at each clock cycle.
|
||||||
|
* This can be used for various timing routines, and as a very precise
|
||||||
|
* clock source. It is set to zero on system startup. Beware that may not
|
||||||
|
* increment at the same frequency as the CPU. Consecutive calls of RDTSC
|
||||||
|
* are, however, guaranteed to return monotonically-increasing values.
|
||||||
|
*
|
||||||
|
* @param result - a pointer to a 64-bit unsigned integer, where the TSC value
|
||||||
|
* will be stored
|
||||||
|
*
|
||||||
|
* @note If 100% compatibility is a concern, you must first check if the
|
||||||
|
* RDTSC instruction is present (if it is not, your program will crash
|
||||||
|
* with "invalid opcode" exception). Only some very old processors (i486,
|
||||||
|
* early AMD K5 and some Cyrix CPUs) lack that instruction - they should
|
||||||
|
* have become exceedingly rare these days. To verify RDTSC presence,
|
||||||
|
* run cpu_identify() and check flags[CPU_FEATURE_TSC].
|
||||||
|
*
|
||||||
|
* @note The monotonically increasing nature of the TSC may be violated
|
||||||
|
* on SMP systems, if their TSC clocks run at different rate. If the OS
|
||||||
|
* doesn't account for that, the TSC drift may become arbitrary large.
|
||||||
|
*/
|
||||||
|
void cpu_rdtsc(uint64_t* result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Store TSC and timing info
|
||||||
|
*
|
||||||
|
* This function stores the current TSC value and current
|
||||||
|
* time info from a precise OS-specific clock source in the cpu_mark_t
|
||||||
|
* structure. The sys_clock field contains time with microsecond resolution.
|
||||||
|
* The values can later be used to measure time intervals, number of clocks,
|
||||||
|
* FPU frequency, etc.
|
||||||
|
* @see cpu_rdtsc
|
||||||
|
*
|
||||||
|
* @param mark [out] - a pointer to a cpu_mark_t structure
|
||||||
|
*/
|
||||||
|
void cpu_tsc_mark(struct cpu_mark_t* mark);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate TSC and timing difference
|
||||||
|
*
|
||||||
|
* @param mark - input/output: a pointer to a cpu_mark_t sturcture, which has
|
||||||
|
* already been initialized by cpu_tsc_mark. The difference in
|
||||||
|
* TSC and time will be written here.
|
||||||
|
*
|
||||||
|
* This function calculates the TSC and time difference, by obtaining the
|
||||||
|
* current TSC and timing values and subtracting the contents of the `mark'
|
||||||
|
* structure from them. Results are written in the same structure.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* ...
|
||||||
|
* struct cpu_mark_t mark;
|
||||||
|
* cpu_tsc_mark(&mark);
|
||||||
|
* foo();
|
||||||
|
* cpu_tsc_unmark(&mark);
|
||||||
|
* printf("Foo finished. Executed in %llu cycles and %llu usecs\n",
|
||||||
|
* mark.tsc, mark.sys_clock);
|
||||||
|
* ...
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
void cpu_tsc_unmark(struct cpu_mark_t* mark);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the CPU clock
|
||||||
|
*
|
||||||
|
* @param mark - pointer to a cpu_mark_t structure, which has been initialized
|
||||||
|
* with cpu_tsc_mark and later `stopped' with cpu_tsc_unmark.
|
||||||
|
*
|
||||||
|
* @note For reliable results, the marked time interval should be at least about
|
||||||
|
* 10 ms.
|
||||||
|
*
|
||||||
|
* @returns the CPU clock frequency, in MHz. Due to measurement error, it will
|
||||||
|
* differ from the true value in a few least-significant bits. Accuracy depends
|
||||||
|
* on the timing interval - the more, the better. If the timing interval is
|
||||||
|
* insufficient, the result is -1. Also, see the comment on cpu_clock_measure
|
||||||
|
* for additional issues and pitfalls in using RDTSC for CPU frequency
|
||||||
|
* measurements.
|
||||||
|
*/
|
||||||
|
int cpu_clock_by_mark(struct cpu_mark_t* mark);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the CPU clock, as reported by the OS
|
||||||
|
*
|
||||||
|
* This function uses OS-specific functions to obtain the CPU clock. It may
|
||||||
|
* differ from the true clock for several reasons:<br><br>
|
||||||
|
*
|
||||||
|
* i) The CPU might be in some power saving state, while the OS reports its
|
||||||
|
* full-power frequency, or vice-versa.<br>
|
||||||
|
* ii) In some cases you can raise or lower the CPU frequency with overclocking
|
||||||
|
* utilities and the OS will not notice.
|
||||||
|
*
|
||||||
|
* @returns the CPU clock frequency in MHz. If the OS is not (yet) supported
|
||||||
|
* or lacks the necessary reporting machinery, the return value is -1
|
||||||
|
*/
|
||||||
|
int cpu_clock_by_os(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Measure the CPU clock frequency
|
||||||
|
*
|
||||||
|
* @param millis - How much time to waste in the busy-wait cycle. In millisecs.
|
||||||
|
* Useful values 10 - 1000
|
||||||
|
* @param quad_check - Do a more thorough measurement if nonzero
|
||||||
|
* (see the explanation).
|
||||||
|
*
|
||||||
|
* The function performs a busy-wait cycle for the given time and calculates
|
||||||
|
* the CPU frequency by the difference of the TSC values. The accuracy of the
|
||||||
|
* calculation depends on the length of the busy-wait cycle: more is better,
|
||||||
|
* but 100ms should be enough for most purposes.
|
||||||
|
*
|
||||||
|
* While this will calculate the CPU frequency correctly in most cases, there are
|
||||||
|
* several reasons why it might be incorrect:<br>
|
||||||
|
*
|
||||||
|
* i) RDTSC doesn't guarantee it will run at the same clock as the CPU.
|
||||||
|
* Apparently there aren't CPUs at the moment, but still, there's no
|
||||||
|
* guarantee.<br>
|
||||||
|
* ii) The CPU might be in a low-frequency power saving mode, and the CPU
|
||||||
|
* might be switched to higher frequency at any time. If this happens
|
||||||
|
* during the measurement, the result can be anywhere between the
|
||||||
|
* low and high frequencies. Also, if you're interested in the
|
||||||
|
* high frequency value only, this function might return the low one
|
||||||
|
* instead.<br>
|
||||||
|
* iii) On SMP systems exhibiting TSC drift (see \ref cpu_rdtsc)
|
||||||
|
*
|
||||||
|
* the quad_check option will run four consecutive measurements and
|
||||||
|
* then return the average of the two most-consistent results. The total
|
||||||
|
* runtime of the function will still be `millis' - consider using
|
||||||
|
* a bit more time for the timing interval.
|
||||||
|
*
|
||||||
|
* Finally, for benchmarking / CPU intensive applications, the best strategy is
|
||||||
|
* to use the cpu_tsc_mark() / cpu_tsc_unmark() / cpu_clock_by_mark() method.
|
||||||
|
* Begin by mark()-ing about one second after application startup (allowing the
|
||||||
|
* power-saving manager to kick in and rise the frequency during that time),
|
||||||
|
* then unmark() just before application finishing. The result will most
|
||||||
|
* acurately represent at what frequency your app was running.
|
||||||
|
*
|
||||||
|
* @returns the CPU clock frequency in MHz (within some measurement error
|
||||||
|
* margin). If RDTSC is not supported, the result is -1.
|
||||||
|
*/
|
||||||
|
int cpu_clock_measure(int millis, int quad_check);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Measure the CPU clock frequency using instruction-counting
|
||||||
|
*
|
||||||
|
* @param millis - how much time to allocate for each run, in milliseconds
|
||||||
|
* @param runs - how many runs to perform
|
||||||
|
*
|
||||||
|
* The function performs a busy-wait cycle using a known number of "heavy" (SSE)
|
||||||
|
* instructions. These instructions run at (more or less guaranteed) 1 IPC rate,
|
||||||
|
* so by running a busy loop for a fixed amount of time, and measuring the
|
||||||
|
* amount of instructions done, the CPU clock is accurately measured.
|
||||||
|
*
|
||||||
|
* Of course, this function is still affected by the power-saving schemes, so
|
||||||
|
* the warnings as of cpu_clock_measure() still apply. However, this function is
|
||||||
|
* immune to problems with detection, related to the Intel Nehalem's "Turbo"
|
||||||
|
* mode, where the internal clock is raised, but the RDTSC rate is unaffected.
|
||||||
|
*
|
||||||
|
* The function will run for about (millis * runs) milliseconds.
|
||||||
|
* You can make only a single busy-wait run (runs == 1); however, this can
|
||||||
|
* be affected by task scheduling (which will break the counting), so allowing
|
||||||
|
* more than one run is recommended. As run length is not imperative for
|
||||||
|
* accurate readings (e.g., 50ms is sufficient), you can afford a lot of short
|
||||||
|
* runs, e.g. 10 runs of 50ms or 20 runs of 25ms.
|
||||||
|
*
|
||||||
|
* Recommended values - millis = 50, runs = 4. For more robustness,
|
||||||
|
* increase the number of runs.
|
||||||
|
*
|
||||||
|
* NOTE: on Bulldozer and later CPUs, the busy-wait cycle runs at 1.4 IPC, thus
|
||||||
|
* the results are skewed. This is corrected internally by dividing the resulting
|
||||||
|
* value by 1.4.
|
||||||
|
* However, this only occurs if the thread is executed on a single CMT
|
||||||
|
* module - if there are other threads competing for resources, the results are
|
||||||
|
* unpredictable. Make sure you run cpu_clock_by_ic() on a CPU that is free from
|
||||||
|
* competing threads, or if there are such threads, they shouldn't exceed the
|
||||||
|
* number of modules. On a Bulldozer X8, that means 4 threads.
|
||||||
|
*
|
||||||
|
* @returns the CPU clock frequency in MHz (within some measurement error
|
||||||
|
* margin). If SSE is not supported, the result is -1. If the input parameters
|
||||||
|
* are incorrect, or some other internal fault is detected, the result is -2.
|
||||||
|
*/
|
||||||
|
int cpu_clock_by_ic(int millis, int runs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the CPU clock frequency (all-in-one method)
|
||||||
|
*
|
||||||
|
* This is an all-in-one method for getting the CPU clock frequency.
|
||||||
|
* It tries to use the OS for that. If the OS doesn't have this info, it
|
||||||
|
* uses cpu_clock_measure with 200ms time interval and quadruple checking.
|
||||||
|
*
|
||||||
|
* @returns the CPU clock frequency in MHz. If every possible method fails,
|
||||||
|
* the result is -1.
|
||||||
|
*/
|
||||||
|
int cpu_clock(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the libcpuid version
|
||||||
|
*
|
||||||
|
* @returns the string representation of the libcpuid version, like "0.1.1"
|
||||||
|
*/
|
||||||
|
const char* cpuid_lib_version(void);
|
||||||
|
|
||||||
|
typedef void (*libcpuid_warn_fn_t) (const char *msg);
|
||||||
|
/**
|
||||||
|
* @brief Sets the warning print function
|
||||||
|
*
|
||||||
|
* In some cases, the internal libcpuid machinery would like to emit useful
|
||||||
|
* debug warnings. By default, these warnings are written to stderr. However,
|
||||||
|
* you can set a custom function that will receive those warnings.
|
||||||
|
*
|
||||||
|
* @param warn_fun - the warning function you want to set. If NULL, warnings
|
||||||
|
* are disabled. The function takes const char* argument.
|
||||||
|
*
|
||||||
|
* @returns the current warning function. You can use the return value to
|
||||||
|
* keep the previous warning function and restore it at your discretion.
|
||||||
|
*/
|
||||||
|
libcpuid_warn_fn_t cpuid_set_warn_function(libcpuid_warn_fn_t warn_fun);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the verbosiness level
|
||||||
|
*
|
||||||
|
* When the verbosiness level is above zero, some functions might print
|
||||||
|
* diagnostic information about what are they doing. The higher the level is,
|
||||||
|
* the more detail is printed. Level zero is guaranteed to omit all such
|
||||||
|
* output. The output is written using the same machinery as the warnings,
|
||||||
|
* @see cpuid_set_warn_function()
|
||||||
|
*
|
||||||
|
* @param level the desired verbosiness level. Useful values 0..2 inclusive
|
||||||
|
*/
|
||||||
|
void cpuid_set_verbosiness_level(int level);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief a structure that holds a list of processor names
|
||||||
|
*/
|
||||||
|
struct cpu_list_t {
|
||||||
|
/** Number of entries in the list */
|
||||||
|
int num_entries;
|
||||||
|
/** Pointers to names. There will be num_entries of them */
|
||||||
|
char **names;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets a list of all known CPU names from a specific vendor.
|
||||||
|
*
|
||||||
|
* This function compiles a list of all known CPU (code)names
|
||||||
|
* (i.e. the possible values of cpu_id_t::cpu_codename) for the given vendor.
|
||||||
|
*
|
||||||
|
* There are about 100 entries for Intel and AMD, and a few for the other
|
||||||
|
* vendors. The list is written out in approximate chronological introduction
|
||||||
|
* order of the parts.
|
||||||
|
*
|
||||||
|
* @param vendor the vendor to be queried
|
||||||
|
* @param list [out] the resulting list will be written here.
|
||||||
|
* NOTE: As the memory is dynamically allocated, be sure to call
|
||||||
|
* cpuid_free_cpu_list() after you're done with the data
|
||||||
|
* @see cpu_list_t
|
||||||
|
*/
|
||||||
|
void cpuid_get_cpu_list(cpu_vendor_t vendor, struct cpu_list_t* list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees a CPU list
|
||||||
|
*
|
||||||
|
* This function deletes all the memory associated with a CPU list, as obtained
|
||||||
|
* by cpuid_get_cpu_list()
|
||||||
|
*
|
||||||
|
* @param list - the list to be free()'d.
|
||||||
|
*/
|
||||||
|
void cpuid_free_cpu_list(struct cpu_list_t* list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Starts/opens a driver, needed to read MSRs (Model Specific Registers)
|
||||||
|
*
|
||||||
|
* On systems that support it, this function will create a temporary
|
||||||
|
* system driver, that has privileges to execute the RDMSR instruction.
|
||||||
|
* After the driver is created, you can read MSRs by calling \ref cpu_rdmsr
|
||||||
|
*
|
||||||
|
* @returns a handle to the driver on success, and NULL on error.
|
||||||
|
* The error message can be obtained by calling \ref cpuid_error.
|
||||||
|
* @see cpu_error_t
|
||||||
|
*/
|
||||||
|
struct msr_driver_t;
|
||||||
|
struct msr_driver_t* cpu_msr_driver_open(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads a Model-Specific Register (MSR)
|
||||||
|
*
|
||||||
|
* If the CPU has MSRs (as indicated by the CPU_FEATURE_MSR flag), you can
|
||||||
|
* read a MSR with the given index by calling this function.
|
||||||
|
*
|
||||||
|
* There are several prerequisites you must do before reading MSRs:
|
||||||
|
* 1) You must ensure the CPU has RDMSR. Check the CPU_FEATURE_MSR flag
|
||||||
|
* in cpu_id_t::flags
|
||||||
|
* 2) You must ensure that the CPU implements the specific MSR you intend to
|
||||||
|
* read.
|
||||||
|
* 3) You must open a MSR-reader driver. RDMSR is a privileged instruction and
|
||||||
|
* needs ring-0 access in order to work. This temporary driver is created
|
||||||
|
* by calling \ref cpu_msr_driver_open
|
||||||
|
*
|
||||||
|
* @param handle - a handle to the MSR reader driver, as created by
|
||||||
|
* cpu_msr_driver_open
|
||||||
|
* @param msr_index - the numeric ID of the MSR you want to read
|
||||||
|
* @param result - a pointer to a 64-bit integer, where the MSR value is stored
|
||||||
|
*
|
||||||
|
* @returns zero if successful, and some negative number on error.
|
||||||
|
* The error message can be obtained by calling \ref cpuid_error.
|
||||||
|
* @see cpu_error_t
|
||||||
|
*/
|
||||||
|
int cpu_rdmsr(struct msr_driver_t* handle, int msr_index, uint64_t* result);
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
INFO_MPERF, /*!< Maximum performance frequency clock. This
|
||||||
|
is a counter, which increments as a
|
||||||
|
proportion of the actual processor speed */
|
||||||
|
INFO_APERF, /*!< Actual performance frequency clock. This
|
||||||
|
accumulates the core clock counts when the
|
||||||
|
core is active. */
|
||||||
|
INFO_CUR_MULTIPLIER, /*!< Current CPU:FSB ratio, multiplied by 100.
|
||||||
|
e.g., a CPU:FSB value of 18.5 reads as
|
||||||
|
1850. */
|
||||||
|
INFO_MAX_MULTIPLIER, /*!< Maxumum CPU:FSB ratio for this CPU,
|
||||||
|
multiplied by 100 */
|
||||||
|
INFO_TEMPERATURE, /*!< The current core temperature in Celsius */
|
||||||
|
INFO_THROTTLING, /*!< 1 if the current logical processor is
|
||||||
|
throttling. 0 if it is running normally. */
|
||||||
|
} cpu_msrinfo_request_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads extended CPU information from Model-Specific Registers.
|
||||||
|
* @param handle - a handle to an open MSR driver, @see cpu_msr_driver_open
|
||||||
|
* @param which - which info field should be returned. A list of
|
||||||
|
* available information entities is listed in the
|
||||||
|
* cpu_msrinfo_request_t enum.
|
||||||
|
* @retval - if the requested information is available for the current
|
||||||
|
* processor model, the respective value is returned.
|
||||||
|
* if no information is available, or the CPU doesn't support
|
||||||
|
* the query, the special value CPU_INVALID_VALUE is returned
|
||||||
|
*/
|
||||||
|
int cpu_msrinfo(struct msr_driver_t* handle, cpu_msrinfo_request_t which);
|
||||||
|
#define CPU_INVALID_VALUE 0x3fffffff
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Closes an open MSR driver
|
||||||
|
*
|
||||||
|
* This function unloads the MSR driver opened by cpu_msr_driver_open and
|
||||||
|
* frees any resources associated with it.
|
||||||
|
*
|
||||||
|
* @param handle - a handle to the MSR reader driver, as created by
|
||||||
|
* cpu_msr_driver_open
|
||||||
|
*
|
||||||
|
* @returns zero if successful, and some negative number on error.
|
||||||
|
* The error message can be obtained by calling \ref cpuid_error.
|
||||||
|
* @see cpu_error_t
|
||||||
|
*/
|
||||||
|
int cpu_msr_driver_close(struct msr_driver_t* handle);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}; /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#endif /* __LIBCPUID_H__ */
|
44
contrib/libcpuid/include/cpuid/libcpuid_constants.h
Normal file
44
contrib/libcpuid/include/cpuid/libcpuid_constants.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @File libcpuid_constants.h
|
||||||
|
* @Author Veselin Georgiev
|
||||||
|
* @Brief Some limits and constants for libcpuid
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBCPUID_CONSTANTS_H__
|
||||||
|
#define __LIBCPUID_CONSTANTS_H__
|
||||||
|
|
||||||
|
#define VENDOR_STR_MAX 16
|
||||||
|
#define BRAND_STR_MAX 64
|
||||||
|
#define CPU_FLAGS_MAX 128
|
||||||
|
#define MAX_CPUID_LEVEL 32
|
||||||
|
#define MAX_EXT_CPUID_LEVEL 32
|
||||||
|
#define MAX_INTELFN4_LEVEL 4
|
||||||
|
#define MAX_INTELFN11_LEVEL 4
|
||||||
|
#define CPU_HINTS_MAX 16
|
||||||
|
|
||||||
|
#endif /* __LIBCPUID_CONSTANTS_H__ */
|
37
contrib/libcpuid/include/cpuid/libcpuid_types.h
Normal file
37
contrib/libcpuid/include/cpuid/libcpuid_types.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @File libcpuid_types.h
|
||||||
|
* @Author Veselin Georgiev
|
||||||
|
* @Brief Type specifications for libcpuid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBCPUID_TYPES_H__
|
||||||
|
#define __LIBCPUID_TYPES_H__
|
||||||
|
|
||||||
|
# include <stdint.h>
|
||||||
|
|
||||||
|
#endif /* __LIBCPUID_TYPES_H__ */
|
182
contrib/libcpuid/include/cpuid/libcpuid_util.c
Normal file
182
contrib/libcpuid/include/cpuid/libcpuid_util.c
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "libcpuid.h"
|
||||||
|
#include "libcpuid_util.h"
|
||||||
|
|
||||||
|
int _current_verboselevel;
|
||||||
|
|
||||||
|
void match_features(const struct feature_map_t* matchtable, int count, uint32_t reg, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
if (reg & (1 << matchtable[i].bit))
|
||||||
|
data->flags[matchtable[i].feature] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void default_warn(const char *msg)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
libcpuid_warn_fn_t _warn_fun = default_warn;
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# define vsnprintf _vsnprintf
|
||||||
|
#endif
|
||||||
|
void warnf(const char* format, ...)
|
||||||
|
{
|
||||||
|
char buff[1024];
|
||||||
|
va_list va;
|
||||||
|
if (!_warn_fun) return;
|
||||||
|
va_start(va, format);
|
||||||
|
vsnprintf(buff, sizeof(buff), format, va);
|
||||||
|
va_end(va);
|
||||||
|
_warn_fun(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugf(int verboselevel, const char* format, ...)
|
||||||
|
{
|
||||||
|
char buff[1024];
|
||||||
|
va_list va;
|
||||||
|
if (verboselevel > _current_verboselevel) return;
|
||||||
|
va_start(va, format);
|
||||||
|
vsnprintf(buff, sizeof(buff), format, va);
|
||||||
|
va_end(va);
|
||||||
|
_warn_fun(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int score(const struct match_entry_t* entry, const struct cpu_id_t* data,
|
||||||
|
int brand_code, int model_code)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
if (entry->family == data->family ) res++;
|
||||||
|
if (entry->model == data->model ) res++;
|
||||||
|
if (entry->stepping == data->stepping ) res++;
|
||||||
|
if (entry->ext_family == data->ext_family) res++;
|
||||||
|
if (entry->ext_model == data->ext_model ) res++;
|
||||||
|
if (entry->ncores == data->num_cores ) res++;
|
||||||
|
if (entry->l2cache == data->l2_cache ) res++;
|
||||||
|
if (entry->l3cache == data->l3_cache ) res++;
|
||||||
|
if (entry->brand_code == brand_code ) res++;
|
||||||
|
if (entry->model_code == model_code ) res++;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void match_cpu_codename(const struct match_entry_t* matchtable, int count,
|
||||||
|
struct cpu_id_t* data, int brand_code, int model_code)
|
||||||
|
{
|
||||||
|
int bestscore = -1;
|
||||||
|
int bestindex = 0;
|
||||||
|
int i, t;
|
||||||
|
|
||||||
|
debugf(3, "Matching cpu f:%d, m:%d, s:%d, xf:%d, xm:%d, ncore:%d, l2:%d, bcode:%d, code:%d\n",
|
||||||
|
data->family, data->model, data->stepping, data->ext_family,
|
||||||
|
data->ext_model, data->num_cores, data->l2_cache, brand_code, model_code);
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
t = score(&matchtable[i], data, brand_code, model_code);
|
||||||
|
debugf(3, "Entry %d, `%s', score %d\n", i, matchtable[i].name, t);
|
||||||
|
if (t > bestscore) {
|
||||||
|
debugf(2, "Entry `%s' selected - best score so far (%d)\n", matchtable[i].name, t);
|
||||||
|
bestscore = t;
|
||||||
|
bestindex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strcpy(data->cpu_codename, matchtable[bestindex].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generic_get_cpu_list(const struct match_entry_t* matchtable, int count,
|
||||||
|
struct cpu_list_t* list)
|
||||||
|
{
|
||||||
|
int i, j, n, good;
|
||||||
|
n = 0;
|
||||||
|
list->names = (char**) malloc(sizeof(char*) * count);
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (strstr(matchtable[i].name, "Unknown")) continue;
|
||||||
|
good = 1;
|
||||||
|
for (j = n - 1; j >= 0; j--)
|
||||||
|
if (!strcmp(list->names[j], matchtable[i].name)) {
|
||||||
|
good = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!good) continue;
|
||||||
|
list->names[n++] = strdup(matchtable[i].name);
|
||||||
|
}
|
||||||
|
list->num_entries = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xmatch_entry(char c, const char* p)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
if (c == 0) return -1;
|
||||||
|
if (c == p[0]) return 1;
|
||||||
|
if (p[0] == '.') return 1;
|
||||||
|
if (p[0] == '#' && isdigit(c)) return 1;
|
||||||
|
if (p[0] == '[') {
|
||||||
|
j = 1;
|
||||||
|
while (p[j] && p[j] != ']') j++;
|
||||||
|
if (!p[j]) return -1;
|
||||||
|
for (i = 1; i < j; i++)
|
||||||
|
if (p[i] == c) return j + 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int match_pattern(const char* s, const char* p)
|
||||||
|
{
|
||||||
|
int i, j, dj, k, n, m;
|
||||||
|
n = (int) strlen(s);
|
||||||
|
m = (int) strlen(p);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (xmatch_entry(s[i], p) != -1) {
|
||||||
|
j = 0;
|
||||||
|
k = 0;
|
||||||
|
while (j < m && ((dj = xmatch_entry(s[i + k], p + j)) != -1)) {
|
||||||
|
k++;
|
||||||
|
j += dj;
|
||||||
|
}
|
||||||
|
if (j == m) return i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cpu_id_t* get_cached_cpuid(void)
|
||||||
|
{
|
||||||
|
static int initialized = 0;
|
||||||
|
static struct cpu_id_t id;
|
||||||
|
if (initialized) return &id;
|
||||||
|
if (cpu_identify(NULL, &id))
|
||||||
|
memset(&id, 0, sizeof(id));
|
||||||
|
initialized = 1;
|
||||||
|
return &id;
|
||||||
|
}
|
87
contrib/libcpuid/include/cpuid/libcpuid_util.h
Normal file
87
contrib/libcpuid/include/cpuid/libcpuid_util.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __LIBCPUID_UTIL_H__
|
||||||
|
#define __LIBCPUID_UTIL_H__
|
||||||
|
|
||||||
|
#define COUNT_OF(array) (sizeof(array) / sizeof(array[0]))
|
||||||
|
|
||||||
|
struct feature_map_t {
|
||||||
|
unsigned bit;
|
||||||
|
cpu_feature_t feature;
|
||||||
|
};
|
||||||
|
|
||||||
|
void match_features(const struct feature_map_t* matchtable, int count,
|
||||||
|
uint32_t reg, struct cpu_id_t* data);
|
||||||
|
|
||||||
|
struct match_entry_t {
|
||||||
|
int family, model, stepping, ext_family, ext_model;
|
||||||
|
int ncores, l2cache, l3cache, brand_code, model_code;
|
||||||
|
char name[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
void match_cpu_codename(const struct match_entry_t* matchtable, int count,
|
||||||
|
struct cpu_id_t* data, int brand_code, int model_code);
|
||||||
|
|
||||||
|
void warnf(const char* format, ...)
|
||||||
|
#ifdef __GNUC__
|
||||||
|
__attribute__((format(printf, 1, 2)))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
void debugf(int verboselevel, const char* format, ...)
|
||||||
|
#ifdef __GNUC__
|
||||||
|
__attribute__((format(printf, 2, 3)))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
void generic_get_cpu_list(const struct match_entry_t* matchtable, int count,
|
||||||
|
struct cpu_list_t* list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Seek for a pattern in `haystack'.
|
||||||
|
* Pattern may be an fixed string, or contain the special metacharacters
|
||||||
|
* '.' - match any single character
|
||||||
|
* '#' - match any digit
|
||||||
|
* '[<chars>] - match any of the given chars (regex-like ranges are not
|
||||||
|
* supported)
|
||||||
|
* Return val: 0 if the pattern is not found. Nonzero if it is found (actually,
|
||||||
|
* x + 1 where x is the index where the match is found).
|
||||||
|
*/
|
||||||
|
int match_pattern(const char* haystack, const char* pattern);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets an initialized cpu_id_t. It is cached, so that internal libcpuid
|
||||||
|
* machinery doesn't need to issue cpu_identify more than once.
|
||||||
|
*/
|
||||||
|
struct cpu_id_t* get_cached_cpuid(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the current errno
|
||||||
|
*/
|
||||||
|
int set_error(cpu_error_t err);
|
||||||
|
|
||||||
|
extern libcpuid_warn_fn_t _warn_fun;
|
||||||
|
extern int _current_verboselevel;
|
||||||
|
|
||||||
|
#endif /* __LIBCPUID_UTIL_H__ */
|
295
contrib/libcpuid/include/cpuid/rdtsc.c
Normal file
295
contrib/libcpuid/include/cpuid/rdtsc.c
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "libcpuid.h"
|
||||||
|
#include "libcpuid_util.h"
|
||||||
|
#include "asm-bits.h"
|
||||||
|
#include "rdtsc.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
void sys_precise_clock(uint64_t *result)
|
||||||
|
{
|
||||||
|
double c, f;
|
||||||
|
LARGE_INTEGER freq, counter;
|
||||||
|
QueryPerformanceCounter(&counter);
|
||||||
|
QueryPerformanceFrequency(&freq);
|
||||||
|
c = (double) counter.QuadPart;
|
||||||
|
f = (double) freq.QuadPart;
|
||||||
|
*result = (uint64_t) ( c * 1000000.0 / f );
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* assuming Linux, Mac OS or other POSIX */
|
||||||
|
#include <sys/time.h>
|
||||||
|
void sys_precise_clock(uint64_t *result)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
*result = (uint64_t) tv.tv_sec * (uint64_t) 1000000 +
|
||||||
|
(uint64_t) tv.tv_usec;
|
||||||
|
}
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
/* out = a - b */
|
||||||
|
static void mark_t_subtract(struct cpu_mark_t* a, struct cpu_mark_t* b, struct cpu_mark_t *out)
|
||||||
|
{
|
||||||
|
out->tsc = a->tsc - b->tsc;
|
||||||
|
out->sys_clock = a->sys_clock - b->sys_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_tsc_mark(struct cpu_mark_t* mark)
|
||||||
|
{
|
||||||
|
cpu_rdtsc(&mark->tsc);
|
||||||
|
sys_precise_clock(&mark->sys_clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_tsc_unmark(struct cpu_mark_t* mark)
|
||||||
|
{
|
||||||
|
struct cpu_mark_t temp;
|
||||||
|
cpu_tsc_mark(&temp);
|
||||||
|
mark_t_subtract(&temp, mark, mark);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int cpu_clock_by_mark(struct cpu_mark_t* mark)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
|
||||||
|
/* Check if some subtraction resulted in a negative number: */
|
||||||
|
if ((mark->tsc >> 63) != 0 || (mark->sys_clock >> 63) != 0) return -1;
|
||||||
|
|
||||||
|
/* Divide-by-zero check: */
|
||||||
|
if (mark->sys_clock == 0) return -1;
|
||||||
|
|
||||||
|
/* Check if the result fits in 32bits */
|
||||||
|
result = mark->tsc / mark->sys_clock;
|
||||||
|
if (result > (uint64_t) 0x7fffffff) return -1;
|
||||||
|
return (int) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
int cpu_clock_by_os(void)
|
||||||
|
{
|
||||||
|
HKEY key;
|
||||||
|
DWORD result;
|
||||||
|
DWORD size = 4;
|
||||||
|
|
||||||
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), 0, KEY_READ, &key) != ERROR_SUCCESS)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (RegQueryValueEx(key, TEXT("~MHz"), NULL, NULL, (LPBYTE) &result, (LPDWORD) &size) != ERROR_SUCCESS) {
|
||||||
|
RegCloseKey(key);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
RegCloseKey(key);
|
||||||
|
|
||||||
|
return (int)result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
/* Assuming Mac OS X with hw.cpufrequency sysctl */
|
||||||
|
int cpu_clock_by_os(void)
|
||||||
|
{
|
||||||
|
long long result = -1;
|
||||||
|
size_t size = sizeof(result);
|
||||||
|
if (sysctlbyname("hw.cpufrequency", &result, &size, NULL, 0))
|
||||||
|
return -1;
|
||||||
|
return (int) (result / (long long) 1000000);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Assuming Linux with /proc/cpuinfo */
|
||||||
|
int cpu_clock_by_os(void)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char line[1024], *s;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
f = fopen("/proc/cpuinfo", "rt");
|
||||||
|
if (!f) return -1;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), f)) {
|
||||||
|
if (!strncmp(line, "cpu MHz", 7)) {
|
||||||
|
s = strchr(line, ':');
|
||||||
|
if (s && 1 == sscanf(s, ":%d.", &result)) {
|
||||||
|
fclose(f);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
/* Emulate doing useful CPU intensive work */
|
||||||
|
static int busy_loop(int amount)
|
||||||
|
{
|
||||||
|
int i, j, k, s = 0;
|
||||||
|
static volatile int data[42] = {32, 12, -1, 5, 23, 0 };
|
||||||
|
for (i = 0; i < amount; i++)
|
||||||
|
for (j = 0; j < 65536; j++)
|
||||||
|
for (k = 0; k < 42; k++)
|
||||||
|
s += data[k];
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int busy_loop_delay(int milliseconds)
|
||||||
|
{
|
||||||
|
int cycles = 0, r = 0, first = 1;
|
||||||
|
uint64_t a, b, c;
|
||||||
|
sys_precise_clock(&a);
|
||||||
|
while (1) {
|
||||||
|
sys_precise_clock(&c);
|
||||||
|
if ((c - a) / 1000 > milliseconds) return r;
|
||||||
|
r += busy_loop(cycles);
|
||||||
|
if (first) {
|
||||||
|
first = 0;
|
||||||
|
} else {
|
||||||
|
if (c - b < 1000) cycles *= 2;
|
||||||
|
if (c - b > 10000) cycles /= 2;
|
||||||
|
}
|
||||||
|
b = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpu_clock_measure(int millis, int quad_check)
|
||||||
|
{
|
||||||
|
struct cpu_mark_t begin[4], end[4], temp, temp2;
|
||||||
|
int results[4], cycles, n, k, i, j, bi, bj, mdiff, diff, _zero = 0;
|
||||||
|
uint64_t tl;
|
||||||
|
|
||||||
|
if (millis < 1) return -1;
|
||||||
|
tl = millis * (uint64_t) 1000;
|
||||||
|
if (quad_check)
|
||||||
|
tl /= 4;
|
||||||
|
n = quad_check ? 4 : 1;
|
||||||
|
cycles = 1;
|
||||||
|
for (k = 0; k < n; k++) {
|
||||||
|
cpu_tsc_mark(&begin[k]);
|
||||||
|
end[k] = begin[k];
|
||||||
|
do {
|
||||||
|
/* Run busy loop, and fool the compiler that we USE the garbishy
|
||||||
|
value it calculates */
|
||||||
|
_zero |= (1 & busy_loop(cycles));
|
||||||
|
cpu_tsc_mark(&temp);
|
||||||
|
mark_t_subtract(&temp, &end[k], &temp2);
|
||||||
|
/* If busy loop is too short, increase it */
|
||||||
|
if (temp2.sys_clock < tl / 8)
|
||||||
|
cycles *= 2;
|
||||||
|
end[k] = temp;
|
||||||
|
} while (end[k].sys_clock - begin[k].sys_clock < tl);
|
||||||
|
mark_t_subtract(&end[k], &begin[k], &temp);
|
||||||
|
results[k] = cpu_clock_by_mark(&temp);
|
||||||
|
}
|
||||||
|
if (n == 1) return results[0];
|
||||||
|
mdiff = 0x7fffffff;
|
||||||
|
bi = bj = -1;
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
for (j = i + 1; j < 4; j++) {
|
||||||
|
diff = results[i] - results[j];
|
||||||
|
if (diff < 0) diff = -diff;
|
||||||
|
if (diff < mdiff) {
|
||||||
|
mdiff = diff;
|
||||||
|
bi = i;
|
||||||
|
bj = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (results[bi] == -1) return -1;
|
||||||
|
return (results[bi] + results[bj] + _zero) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpu_clock_by_ic(int millis, int runs)
|
||||||
|
{
|
||||||
|
int max_value = 0, cur_value, i, ri, cycles_inner, cycles_outer, c;
|
||||||
|
struct cpu_id_t* id;
|
||||||
|
uint64_t t0, t1, tl, hz;
|
||||||
|
int multiplier_numerator = 1, multiplier_denom = 1;
|
||||||
|
if (millis <= 0 || runs <= 0) return -2;
|
||||||
|
id = get_cached_cpuid();
|
||||||
|
// if there aren't SSE instructions - we can't run the test at all
|
||||||
|
if (!id || !id->flags[CPU_FEATURE_SSE]) return -1;
|
||||||
|
//
|
||||||
|
if (id->sse_size < 128) {
|
||||||
|
debugf(1, "SSE execution path is 64-bit\n");
|
||||||
|
// on a CPU with half SSE unit length, SSE instructions execute at 0.5 IPC;
|
||||||
|
// the resulting value must be multiplied by 2:
|
||||||
|
multiplier_numerator = 2;
|
||||||
|
} else {
|
||||||
|
debugf(1, "SSE execution path is 128-bit\n");
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// on a Bulldozer or later CPU, SSE instructions execute at 1.4 IPC, handle that as well:
|
||||||
|
if (id->vendor == VENDOR_AMD && id->ext_family >= 21) {
|
||||||
|
debugf(1, "cpu_clock_by_ic: Bulldozer (or later) detected, dividing result by 1.4\n");
|
||||||
|
multiplier_numerator = 5;
|
||||||
|
multiplier_denom = 7; // multiply by 5/7, to divide by 1.4
|
||||||
|
}
|
||||||
|
//
|
||||||
|
tl = millis * 125; // (*1000 / 8)
|
||||||
|
cycles_inner = 128;
|
||||||
|
cycles_outer = 1;
|
||||||
|
do {
|
||||||
|
if (cycles_inner < 1000000000) cycles_inner *= 2;
|
||||||
|
else cycles_outer *= 2;
|
||||||
|
sys_precise_clock(&t0);
|
||||||
|
for (i = 0; i < cycles_outer; i++)
|
||||||
|
busy_sse_loop(cycles_inner);
|
||||||
|
sys_precise_clock(&t1);
|
||||||
|
} while (t1 - t0 < tl);
|
||||||
|
debugf(2, "inner: %d, outer: %d\n", cycles_inner, cycles_outer);
|
||||||
|
for (ri = 0; ri < runs; ri++) {
|
||||||
|
sys_precise_clock(&t0);
|
||||||
|
c = 0;
|
||||||
|
do {
|
||||||
|
c++;
|
||||||
|
for (i = 0; i < cycles_outer; i++)
|
||||||
|
busy_sse_loop(cycles_inner);
|
||||||
|
sys_precise_clock(&t1);
|
||||||
|
} while (t1 - t0 < tl * (uint64_t) 8);
|
||||||
|
// cpu_Hz = cycles_inner * cycles_outer * 256 / (t1 - t0) * 1000000
|
||||||
|
debugf(2, "c = %d, td = %d\n", c, (int) (t1 - t0));
|
||||||
|
hz = ((uint64_t) cycles_inner * (uint64_t) 256 + 12) *
|
||||||
|
(uint64_t) cycles_outer * (uint64_t) multiplier_numerator * (uint64_t) c * (uint64_t) 1000000
|
||||||
|
/ ((t1 - t0) * (uint64_t) multiplier_denom);
|
||||||
|
cur_value = (int) (hz / 1000000);
|
||||||
|
if (cur_value > max_value) max_value = cur_value;
|
||||||
|
}
|
||||||
|
return max_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpu_clock(void)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
result = cpu_clock_by_os();
|
||||||
|
if (result <= 0)
|
||||||
|
result = cpu_clock_measure(200, 1);
|
||||||
|
return result;
|
||||||
|
}
|
33
contrib/libcpuid/include/cpuid/rdtsc.h
Normal file
33
contrib/libcpuid/include/cpuid/rdtsc.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __RDTSC_H__
|
||||||
|
#define __RDTSC_H__
|
||||||
|
|
||||||
|
void sys_precise_clock(uint64_t *result);
|
||||||
|
int busy_loop_delay(int milliseconds);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* __RDTSC_H__ */
|
469
contrib/libcpuid/include/cpuid/recog_amd.c
Normal file
469
contrib/libcpuid/include/cpuid/recog_amd.c
Normal file
@ -0,0 +1,469 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "libcpuid.h"
|
||||||
|
#include "recog_amd.h"
|
||||||
|
#include "libcpuid_util.h"
|
||||||
|
|
||||||
|
enum _amd_code_t {
|
||||||
|
NA,
|
||||||
|
NO_CODE,
|
||||||
|
OPTERON_GENERIC,
|
||||||
|
OPTERON_800,
|
||||||
|
ATHLON_XP,
|
||||||
|
ATHLON_XP_M,
|
||||||
|
ATHLON_XP_M_LV,
|
||||||
|
ATHLON,
|
||||||
|
ATHLON_MP,
|
||||||
|
MOBILE_ATHLON64,
|
||||||
|
ATHLON_FX,
|
||||||
|
DURON,
|
||||||
|
DURON_MP,
|
||||||
|
MOBILE_DURON,
|
||||||
|
MOBILE_SEMPRON,
|
||||||
|
OPTERON_SINGLE,
|
||||||
|
OPTERON_DUALCORE,
|
||||||
|
OPTERON_800_DUALCORE,
|
||||||
|
MOBILE_TURION,
|
||||||
|
ATHLON_64,
|
||||||
|
ATHLON_64_FX,
|
||||||
|
TURION_64,
|
||||||
|
TURION_X2,
|
||||||
|
SEMPRON,
|
||||||
|
M_SEMPRON,
|
||||||
|
SEMPRON_DUALCORE,
|
||||||
|
PHENOM,
|
||||||
|
PHENOM2,
|
||||||
|
ATHLON_64_X2,
|
||||||
|
ATHLON_64_X3,
|
||||||
|
ATHLON_64_X4,
|
||||||
|
FUSION_C,
|
||||||
|
FUSION_E,
|
||||||
|
FUSION_EA,
|
||||||
|
FUSION_Z,
|
||||||
|
};
|
||||||
|
typedef enum _amd_code_t amd_code_t;
|
||||||
|
|
||||||
|
const struct match_entry_t cpudb_amd[] = {
|
||||||
|
{ -1, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown AMD CPU" },
|
||||||
|
|
||||||
|
/* 486 and the likes */
|
||||||
|
{ 4, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown AMD 486" },
|
||||||
|
{ 4, 3, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "AMD 486DX2" },
|
||||||
|
{ 4, 7, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "AMD 486DX2WB" },
|
||||||
|
{ 4, 8, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "AMD 486DX4" },
|
||||||
|
{ 4, 9, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "AMD 486DX4WB" },
|
||||||
|
|
||||||
|
/* Pentia clones */
|
||||||
|
{ 5, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown AMD 586" },
|
||||||
|
{ 5, 0, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K5" },
|
||||||
|
{ 5, 1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K5" },
|
||||||
|
{ 5, 2, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K5" },
|
||||||
|
{ 5, 3, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K5" },
|
||||||
|
|
||||||
|
/* The K6 */
|
||||||
|
{ 5, 6, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K6" },
|
||||||
|
{ 5, 7, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K6" },
|
||||||
|
|
||||||
|
{ 5, 8, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K6-2" },
|
||||||
|
{ 5, 9, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K6-III" },
|
||||||
|
{ 5, 10, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown K6" },
|
||||||
|
{ 5, 11, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown K6" },
|
||||||
|
{ 5, 12, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown K6" },
|
||||||
|
{ 5, 13, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "K6-2+" },
|
||||||
|
|
||||||
|
/* Athlon et al. */
|
||||||
|
{ 6, 1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Athlon (Slot-A)" },
|
||||||
|
{ 6, 2, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Athlon (Slot-A)" },
|
||||||
|
{ 6, 3, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Duron (Spitfire)" },
|
||||||
|
{ 6, 4, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Athlon (ThunderBird)" },
|
||||||
|
|
||||||
|
{ 6, 6, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown Athlon" },
|
||||||
|
{ 6, 6, -1, -1, -1, 1, -1, -1, ATHLON , 0, "Athlon (Palomino)" },
|
||||||
|
{ 6, 6, -1, -1, -1, 1, -1, -1, ATHLON_MP , 0, "Athlon MP (Palomino)" },
|
||||||
|
{ 6, 6, -1, -1, -1, 1, -1, -1, DURON , 0, "Duron (Palomino)" },
|
||||||
|
{ 6, 6, -1, -1, -1, 1, -1, -1, ATHLON_XP , 0, "Athlon XP" },
|
||||||
|
|
||||||
|
{ 6, 7, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown Athlon XP" },
|
||||||
|
{ 6, 7, -1, -1, -1, 1, -1, -1, DURON , 0, "Duron (Morgan)" },
|
||||||
|
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Athlon XP" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, ATHLON , 0, "Athlon XP (Thoroughbred)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, ATHLON_XP , 0, "Athlon XP (Thoroughbred)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, DURON , 0, "Duron (Applebred)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, SEMPRON , 0, "Sempron (Thoroughbred)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, 128, -1, SEMPRON , 0, "Sempron (Thoroughbred)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, 256, -1, SEMPRON , 0, "Sempron (Thoroughbred)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, ATHLON_MP , 0, "Athlon MP (Thoroughbred)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, ATHLON_XP_M , 0, "Mobile Athlon (T-Bred)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, ATHLON_XP_M_LV , 0, "Mobile Athlon (T-Bred)" },
|
||||||
|
|
||||||
|
{ 6, 10, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Athlon XP (Barton)" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, 512, -1, ATHLON_XP , 0, "Athlon XP (Barton)" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, 512, -1, SEMPRON , 0, "Sempron (Barton)" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, 256, -1, SEMPRON , 0, "Sempron (Thorton)" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, 256, -1, ATHLON_XP , 0, "Athlon XP (Thorton)" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, -1, -1, ATHLON_MP , 0, "Athlon MP (Barton)" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, -1, -1, ATHLON_XP_M , 0, "Mobile Athlon (Barton)" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, -1, -1, ATHLON_XP_M_LV , 0, "Mobile Athlon (Barton)" },
|
||||||
|
|
||||||
|
/* K8 Architecture */
|
||||||
|
{ 15, -1, -1, 15, -1, 1, -1, -1, NO_CODE , 0, "Unknown K8" },
|
||||||
|
{ 15, -1, -1, 16, -1, 1, -1, -1, NO_CODE , 0, "Unknown K9" },
|
||||||
|
|
||||||
|
{ 15, -1, -1, 15, -1, 1, -1, -1, NO_CODE , 0, "Unknown A64" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, -1, -1, OPTERON_SINGLE , 0, "Opteron" },
|
||||||
|
{ 15, -1, -1, 15, -1, 2, -1, -1, OPTERON_DUALCORE , 0, "Opteron (Dual Core)" },
|
||||||
|
{ 15, 3, -1, 15, -1, 1, -1, -1, OPTERON_SINGLE , 0, "Opteron" },
|
||||||
|
{ 15, 3, -1, 15, -1, 2, -1, -1, OPTERON_DUALCORE , 0, "Opteron (Dual Core)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (512K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, 1024, -1, ATHLON_64 , 0, "Athlon 64 (1024K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, -1, -1, ATHLON_FX , 0, "Athlon FX" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, -1, -1, ATHLON_64_FX , 0, "Athlon 64 FX" },
|
||||||
|
{ 15, -1, -1, 15, -1, 2, 512, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (512K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 2, 1024, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (1024K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, 512, -1, TURION_64 , 0, "Turion 64 (512K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, 1024, -1, TURION_64 , 0, "Turion 64 (1024K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 2, 512, -1, TURION_X2 , 0, "Turion 64 X2 (512K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 2, 1024, -1, TURION_X2 , 0, "Turion 64 X2 (1024K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, 128, -1, SEMPRON , 0, "A64 Sempron (128K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, 256, -1, SEMPRON , 0, "A64 Sempron (256K)" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, 512, -1, SEMPRON , 0, "A64 Sempron (512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x4f, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (Orleans/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x5f, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (Orleans/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x2f, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (Venice/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x2c, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (Venice/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x1f, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (Winchester/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x0c, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (Newcastle/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x27, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (San Diego/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x37, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (San Diego/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x04, 1, 512, -1, ATHLON_64 , 0, "Athlon 64 (ClawHammer/512K)" },
|
||||||
|
|
||||||
|
{ 15, -1, -1, 15, 0x5f, 1, 1024, -1, ATHLON_64 , 0, "Athlon 64 (Orleans/1024K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x27, 1, 1024, -1, ATHLON_64 , 0, "Athlon 64 (San Diego/1024K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x04, 1, 1024, -1, ATHLON_64 , 0, "Athlon 64 (ClawHammer/1024K)" },
|
||||||
|
|
||||||
|
{ 15, -1, -1, 15, 0x4b, 2, 256, -1, SEMPRON_DUALCORE , 0, "Athlon 64 X2 (Windsor/256K)" },
|
||||||
|
|
||||||
|
{ 15, -1, -1, 15, 0x23, 2, 512, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (Toledo/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x4b, 2, 512, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (Windsor/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x43, 2, 512, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (Windsor/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x6b, 2, 512, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (Brisbane/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x2b, 2, 512, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (Manchester/512K)"},
|
||||||
|
|
||||||
|
{ 15, -1, -1, 15, 0x23, 2, 1024, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (Toledo/1024K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x43, 2, 1024, -1, ATHLON_64_X2 , 0, "Athlon 64 X2 (Windsor/1024K)" },
|
||||||
|
|
||||||
|
{ 15, -1, -1, 15, 0x08, 1, 128, -1, M_SEMPRON , 0, "Mobile Sempron 64 (Dublin/128K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x08, 1, 256, -1, M_SEMPRON , 0, "Mobile Sempron 64 (Dublin/256K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x0c, 1, 256, -1, SEMPRON , 0, "Sempron 64 (Paris)" },
|
||||||
|
{ 15, -1, -1, 15, 0x1c, 1, 128, -1, SEMPRON , 0, "Sempron 64 (Palermo/128K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x1c, 1, 256, -1, SEMPRON , 0, "Sempron 64 (Palermo/256K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x1c, 1, 128, -1, M_SEMPRON , 0, "Mobile Sempron 64 (Sonora/128K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x1c, 1, 256, -1, M_SEMPRON , 0, "Mobile Sempron 64 (Sonora/256K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x2c, 1, 128, -1, SEMPRON , 0, "Sempron 64 (Palermo/128K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x2c, 1, 256, -1, SEMPRON , 0, "Sempron 64 (Palermo/256K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x2c, 1, 128, -1, M_SEMPRON , 0, "Mobile Sempron 64 (Albany/128K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x2c, 1, 256, -1, M_SEMPRON , 0, "Mobile Sempron 64 (Albany/256K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x2f, 1, 128, -1, SEMPRON , 0, "Sempron 64 (Palermo/128K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x2f, 1, 256, -1, SEMPRON , 0, "Sempron 64 (Palermo/256K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x4f, 1, 128, -1, SEMPRON , 0, "Sempron 64 (Manila/128K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x4f, 1, 256, -1, SEMPRON , 0, "Sempron 64 (Manila/256K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x5f, 1, 128, -1, SEMPRON , 0, "Sempron 64 (Manila/128K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x5f, 1, 256, -1, SEMPRON , 0, "Sempron 64 (Manila/256K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x6b, 2, 256, -1, SEMPRON , 0, "Sempron 64 Dual (Sherman/256K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x6b, 2, 512, -1, SEMPRON , 0, "Sempron 64 Dual (Sherman/512K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x7f, 1, 256, -1, SEMPRON , 0, "Sempron 64 (Sparta/256K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x7f, 1, 512, -1, SEMPRON , 0, "Sempron 64 (Sparta/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x4c, 1, 256, -1, M_SEMPRON , 0, "Mobile Sempron 64 (Keene/256K)"},
|
||||||
|
{ 15, -1, -1, 15, 0x4c, 1, 512, -1, M_SEMPRON , 0, "Mobile Sempron 64 (Keene/512K)"},
|
||||||
|
{ 15, -1, -1, 15, -1, 2, -1, -1, SEMPRON_DUALCORE , 0, "Sempron Dual Core" },
|
||||||
|
|
||||||
|
{ 15, -1, -1, 15, 0x24, 1, 512, -1, TURION_64 , 0, "Turion 64 (Lancaster/512K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x24, 1, 1024, -1, TURION_64 , 0, "Turion 64 (Lancaster/1024K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x48, 2, 256, -1, TURION_X2 , 0, "Turion X2 (Taylor)" },
|
||||||
|
{ 15, -1, -1, 15, 0x48, 2, 512, -1, TURION_X2 , 0, "Turion X2 (Trinidad)" },
|
||||||
|
{ 15, -1, -1, 15, 0x4c, 1, 512, -1, TURION_64 , 0, "Turion 64 (Richmond)" },
|
||||||
|
{ 15, -1, -1, 15, 0x68, 2, 256, -1, TURION_X2 , 0, "Turion X2 (Tyler/256K)" },
|
||||||
|
{ 15, -1, -1, 15, 0x68, 2, 512, -1, TURION_X2 , 0, "Turion X2 (Tyler/512K)" },
|
||||||
|
{ 15, -1, -1, 17, 3, 2, 512, -1, TURION_X2 , 0, "Turion X2 (Griffin/512K)" },
|
||||||
|
{ 15, -1, -1, 17, 3, 2, 1024, -1, TURION_X2 , 0, "Turion X2 (Griffin/1024K)" },
|
||||||
|
|
||||||
|
/* K9 Architecture */
|
||||||
|
{ 15, -1, -1, 16, -1, 1, -1, -1, PHENOM , 0, "Unknown AMD Phenom" },
|
||||||
|
{ 15, 2, -1, 16, -1, 1, -1, -1, PHENOM , 0, "Phenom" },
|
||||||
|
{ 15, 2, -1, 16, -1, 3, -1, -1, PHENOM , 0, "Phenom X3 (Toliman)" },
|
||||||
|
{ 15, 2, -1, 16, -1, 4, -1, -1, PHENOM , 0, "Phenom X4 (Agena)" },
|
||||||
|
{ 15, 2, -1, 16, -1, 3, 512, -1, PHENOM , 0, "Phenom X3 (Toliman/256K)" },
|
||||||
|
{ 15, 2, -1, 16, -1, 3, 512, -1, PHENOM , 0, "Phenom X3 (Toliman/512K)" },
|
||||||
|
{ 15, 2, -1, 16, -1, 4, 128, -1, PHENOM , 0, "Phenom X4 (Agena/128K)" },
|
||||||
|
{ 15, 2, -1, 16, -1, 4, 256, -1, PHENOM , 0, "Phenom X4 (Agena/256K)" },
|
||||||
|
{ 15, 2, -1, 16, -1, 4, 512, -1, PHENOM , 0, "Phenom X4 (Agena/512K)" },
|
||||||
|
{ 15, 2, -1, 16, -1, 2, 512, -1, ATHLON_64_X2 , 0, "Athlon X2 (Kuma)" },
|
||||||
|
/* Phenom II derivates: */
|
||||||
|
{ 15, 4, -1, 16, -1, 4, -1, -1, NO_CODE , 0, "Phenom (Deneb-based)" },
|
||||||
|
{ 15, 4, -1, 16, -1, 1, 1024, -1, SEMPRON , 0, "Sempron (Sargas)" },
|
||||||
|
{ 15, 4, -1, 16, -1, 2, 512, -1, PHENOM2 , 0, "Phenom II X2 (Callisto)" },
|
||||||
|
{ 15, 4, -1, 16, -1, 3, 512, -1, PHENOM2 , 0, "Phenom II X3 (Heka)" },
|
||||||
|
{ 15, 4, -1, 16, -1, 4, 512, -1, PHENOM2 , 0, "Phenom II X4" },
|
||||||
|
{ 15, 4, -1, 16, 4, 4, 512, -1, PHENOM2 , 0, "Phenom II X4 (Deneb)" },
|
||||||
|
{ 15, 5, -1, 16, 5, 4, 512, -1, PHENOM2 , 0, "Phenom II X4 (Deneb)" },
|
||||||
|
{ 15, 4, -1, 16, 10, 4, 512, -1, PHENOM2 , 0, "Phenom II X4 (Zosma)" },
|
||||||
|
{ 15, 4, -1, 16, 10, 6, 512, -1, PHENOM2 , 0, "Phenom II X6 (Thuban)" },
|
||||||
|
|
||||||
|
{ 15, 4, -1, 16, -1, 2, 1024, -1, ATHLON_64_X2 , 0, "Athlon II X2 (Regor)" },
|
||||||
|
{ 15, 4, -1, 16, -1, 2, 512, -1, ATHLON_64_X2 , 0, "Athlon II X2 (Regor)" },
|
||||||
|
{ 15, 5, -1, 16, 5, 3, 512, -1, ATHLON_64_X3 , 0, "Athlon II X3 (Rana)" },
|
||||||
|
{ 15, 5, -1, 16, 5, 4, 512, -1, ATHLON_64_X4 , 0, "Athlon II X4 (Propus)" },
|
||||||
|
/* 2011 CPUs with AMD fusion: */
|
||||||
|
{ 15, -1, -1, 20, 1, 1, 512, -1, FUSION_C , 0, "Brazos Ontario" },
|
||||||
|
{ 15, -1, -1, 20, 1, 2, 512, -1, FUSION_C , 0, "Brazos Ontario (Dual-core)" },
|
||||||
|
{ 15, -1, -1, 20, 1, 1, 512, -1, FUSION_E , 0, "Brazos Zacate" },
|
||||||
|
{ 15, -1, -1, 20, 1, 2, 512, -1, FUSION_E , 0, "Brazos Zacate (Dual-core)" },
|
||||||
|
{ 15, -1, -1, 20, 1, 1, 512, -1, FUSION_Z , 0, "Brazos Desna" },
|
||||||
|
{ 15, -1, -1, 18, 1, 2, 512, -1, FUSION_EA , 0, "Llano X2" },
|
||||||
|
{ 15, -1, -1, 18, 1, 2, 1024, -1, FUSION_EA , 0, "Llano X2" },
|
||||||
|
{ 15, -1, -1, 18, 1, 3, 1024, -1, FUSION_EA , 0, "Llano X3" },
|
||||||
|
{ 15, -1, -1, 18, 1, 4, 1024, -1, FUSION_EA , 0, "Llano X4" },
|
||||||
|
|
||||||
|
/* Newer Opterons: */
|
||||||
|
{ 15, 9, -1, 16, 9, 8, -1, -1, OPTERON_GENERIC , 0, "Magny-Cours Opteron" },
|
||||||
|
|
||||||
|
/* Bulldozer CPUs: */
|
||||||
|
{ 15, -1, -1, 21, 1, 4, 2048, -1, NO_CODE , 0, "Bulldozer X2" },
|
||||||
|
{ 15, -1, -1, 21, 1, 6, 2048, -1, NO_CODE , 0, "Bulldozer X3" },
|
||||||
|
{ 15, -1, -1, 21, 1, 8, 2048, -1, NO_CODE , 0, "Bulldozer X4" },
|
||||||
|
{ 15, -1, -1, 21, 2, 4, 2048, -1, NO_CODE , 0, "Vishera X2" },
|
||||||
|
{ 15, -1, -1, 21, 2, 6, 2048, -1, NO_CODE , 0, "Vishera X3" },
|
||||||
|
{ 15, -1, -1, 21, 2, 8, 2048, -1, NO_CODE , 0, "Vishera X4" },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void load_amd_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
const struct feature_map_t matchtable_edx81[] = {
|
||||||
|
{ 20, CPU_FEATURE_NX },
|
||||||
|
{ 22, CPU_FEATURE_MMXEXT },
|
||||||
|
{ 25, CPU_FEATURE_FXSR_OPT },
|
||||||
|
{ 30, CPU_FEATURE_3DNOWEXT },
|
||||||
|
{ 31, CPU_FEATURE_3DNOW },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_ecx81[] = {
|
||||||
|
{ 1, CPU_FEATURE_CMP_LEGACY },
|
||||||
|
{ 2, CPU_FEATURE_SVM },
|
||||||
|
{ 5, CPU_FEATURE_ABM },
|
||||||
|
{ 6, CPU_FEATURE_SSE4A },
|
||||||
|
{ 7, CPU_FEATURE_MISALIGNSSE },
|
||||||
|
{ 8, CPU_FEATURE_3DNOWPREFETCH },
|
||||||
|
{ 9, CPU_FEATURE_OSVW },
|
||||||
|
{ 10, CPU_FEATURE_IBS },
|
||||||
|
{ 11, CPU_FEATURE_XOP },
|
||||||
|
{ 12, CPU_FEATURE_SKINIT },
|
||||||
|
{ 13, CPU_FEATURE_WDT },
|
||||||
|
{ 16, CPU_FEATURE_FMA4 },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_edx87[] = {
|
||||||
|
{ 0, CPU_FEATURE_TS },
|
||||||
|
{ 1, CPU_FEATURE_FID },
|
||||||
|
{ 2, CPU_FEATURE_VID },
|
||||||
|
{ 3, CPU_FEATURE_TTP },
|
||||||
|
{ 4, CPU_FEATURE_TM_AMD },
|
||||||
|
{ 5, CPU_FEATURE_STC },
|
||||||
|
{ 6, CPU_FEATURE_100MHZSTEPS },
|
||||||
|
{ 7, CPU_FEATURE_HWPSTATE },
|
||||||
|
/* id 8 is handled in common */
|
||||||
|
{ 9, CPU_FEATURE_CPB },
|
||||||
|
{ 10, CPU_FEATURE_APERFMPERF },
|
||||||
|
{ 11, CPU_FEATURE_PFI },
|
||||||
|
{ 12, CPU_FEATURE_PA },
|
||||||
|
};
|
||||||
|
if (raw->ext_cpuid[0][0] >= 0x80000001) {
|
||||||
|
match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data);
|
||||||
|
match_features(matchtable_ecx81, COUNT_OF(matchtable_ecx81), raw->ext_cpuid[1][2], data);
|
||||||
|
}
|
||||||
|
if (raw->ext_cpuid[0][0] >= 0x80000001)
|
||||||
|
match_features(matchtable_edx87, COUNT_OF(matchtable_edx87), raw->ext_cpuid[7][3], data);
|
||||||
|
if (raw->ext_cpuid[0][0] >= 0x8000001a) {
|
||||||
|
/* We have the extended info about SSE unit size */
|
||||||
|
data->detection_hints[CPU_HINT_SSE_SIZE_AUTH] = 1;
|
||||||
|
data->sse_size = (raw->ext_cpuid[0x1a][0] & 1) ? 128 : 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decode_amd_cache_info(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int l3_result;
|
||||||
|
const int assoc_table[16] = {
|
||||||
|
0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 92, 128, 255
|
||||||
|
};
|
||||||
|
unsigned n = raw->ext_cpuid[0][0];
|
||||||
|
|
||||||
|
if (n >= 0x80000005) {
|
||||||
|
data->l1_data_cache = (raw->ext_cpuid[5][2] >> 24) & 0xff;
|
||||||
|
data->l1_assoc = (raw->ext_cpuid[5][2] >> 16) & 0xff;
|
||||||
|
data->l1_cacheline = (raw->ext_cpuid[5][2]) & 0xff;
|
||||||
|
data->l1_instruction_cache = (raw->ext_cpuid[5][3] >> 24) & 0xff;
|
||||||
|
}
|
||||||
|
if (n >= 0x80000006) {
|
||||||
|
data->l2_cache = (raw->ext_cpuid[6][2] >> 16) & 0xffff;
|
||||||
|
data->l2_assoc = assoc_table[(raw->ext_cpuid[6][2] >> 12) & 0xf];
|
||||||
|
data->l2_cacheline = (raw->ext_cpuid[6][2]) & 0xff;
|
||||||
|
|
||||||
|
l3_result = (raw->ext_cpuid[6][3] >> 18);
|
||||||
|
if (l3_result > 0) {
|
||||||
|
l3_result = 512 * l3_result; /* AMD spec says it's a range,
|
||||||
|
but we take the lower bound */
|
||||||
|
data->l3_cache = l3_result;
|
||||||
|
data->l3_assoc = assoc_table[(raw->ext_cpuid[6][3] >> 12) & 0xf];
|
||||||
|
data->l3_cacheline = (raw->ext_cpuid[6][3]) & 0xff;
|
||||||
|
} else {
|
||||||
|
data->l3_cache = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decode_amd_number_of_cores(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int logical_cpus = -1, num_cores = -1;
|
||||||
|
|
||||||
|
if (raw->basic_cpuid[0][0] >= 1) {
|
||||||
|
logical_cpus = (raw->basic_cpuid[1][1] >> 16) & 0xff;
|
||||||
|
if (raw->ext_cpuid[0][0] >= 8) {
|
||||||
|
num_cores = 1 + (raw->ext_cpuid[8][2] & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data->flags[CPU_FEATURE_HT]) {
|
||||||
|
if (num_cores > 1) {
|
||||||
|
data->num_cores = num_cores;
|
||||||
|
data->num_logical_cpus = logical_cpus;
|
||||||
|
} else {
|
||||||
|
data->num_cores = 1;
|
||||||
|
data->num_logical_cpus = (logical_cpus >= 2 ? logical_cpus : 2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data->num_cores = data->num_logical_cpus = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_has_turion_modelname(const char *bs)
|
||||||
|
{
|
||||||
|
/* We search for something like TL-60. Ahh, I miss regexes...*/
|
||||||
|
int i, l, k;
|
||||||
|
char code[3] = {0};
|
||||||
|
const char* codes[] = { "ML", "MT", "MK", "TK", "TL", "RM", "ZM", "" };
|
||||||
|
l = (int) strlen(bs);
|
||||||
|
for (i = 3; i < l - 2; i++) {
|
||||||
|
if (bs[i] == '-' &&
|
||||||
|
isupper(bs[i-1]) && isupper(bs[i-2]) && !isupper(bs[i-3]) &&
|
||||||
|
isdigit(bs[i+1]) && isdigit(bs[i+2]) && !isdigit(bs[i+3]))
|
||||||
|
{
|
||||||
|
code[0] = bs[i-2];
|
||||||
|
code[1] = bs[i-1];
|
||||||
|
for (k = 0; codes[k][0]; k++)
|
||||||
|
if (!strcmp(codes[k], code)) return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static amd_code_t decode_amd_codename_part1(const char *bs)
|
||||||
|
{
|
||||||
|
int is_dual = 0, is_quad = 0, is_tri = 0;
|
||||||
|
if (strstr(bs, "Dual Core") ||
|
||||||
|
strstr(bs, "Dual-Core") ||
|
||||||
|
strstr(bs, " X2 "))
|
||||||
|
is_dual = 1;
|
||||||
|
if (strstr(bs, " X4 ")) is_quad = 1;
|
||||||
|
if (strstr(bs, " X3 ")) is_tri = 1;
|
||||||
|
if (strstr(bs, "Opteron")) {
|
||||||
|
return is_dual ? OPTERON_DUALCORE : OPTERON_SINGLE;
|
||||||
|
}
|
||||||
|
if (strstr(bs, "Phenom")) {
|
||||||
|
if (strstr(bs, "II")) return PHENOM2;
|
||||||
|
else return PHENOM;
|
||||||
|
}
|
||||||
|
if (amd_has_turion_modelname(bs)) {
|
||||||
|
return is_dual ? TURION_X2 : TURION_64;
|
||||||
|
}
|
||||||
|
if (strstr(bs, "Athlon(tm) 64 FX")) return ATHLON_64_FX;
|
||||||
|
if (strstr(bs, "Athlon(tm) FX")) return ATHLON_FX;
|
||||||
|
if (strstr(bs, "Athlon(tm) 64") || strstr(bs, "Athlon(tm) II X") || match_pattern(bs, "Athlon(tm) X#")) {
|
||||||
|
if (is_quad) return ATHLON_64_X4;
|
||||||
|
if (is_dual) return ATHLON_64_X2;
|
||||||
|
if (is_tri) return ATHLON_64_X3;
|
||||||
|
return ATHLON_64;
|
||||||
|
}
|
||||||
|
if (strstr(bs, "Turion")) {
|
||||||
|
return is_dual ? TURION_X2 : TURION_64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strstr(bs, "mobile") || strstr(bs, "Mobile")) {
|
||||||
|
if (strstr(bs, "Athlon(tm) XP-M (LV)")) return ATHLON_XP_M_LV;
|
||||||
|
if (strstr(bs, "Athlon(tm) XP")) return ATHLON_XP_M;
|
||||||
|
if (strstr(bs, "Sempron(tm)")) return M_SEMPRON;
|
||||||
|
if (strstr(bs, "Athlon")) return MOBILE_ATHLON64;
|
||||||
|
if (strstr(bs, "Duron")) return MOBILE_DURON;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (strstr(bs, "Athlon(tm) XP")) return ATHLON_XP;
|
||||||
|
if (strstr(bs, "Athlon(tm) MP")) return ATHLON_MP;
|
||||||
|
if (strstr(bs, "Sempron(tm)")) return SEMPRON;
|
||||||
|
if (strstr(bs, "Duron")) return DURON;
|
||||||
|
if (strstr(bs, "Athlon")) return ATHLON;
|
||||||
|
}
|
||||||
|
if (match_pattern(bs, "C-##")) return FUSION_C;
|
||||||
|
if (match_pattern(bs, "E-###")) return FUSION_E;
|
||||||
|
if (match_pattern(bs, "Z-##")) return FUSION_Z;
|
||||||
|
if (match_pattern(bs, "E#-####") || match_pattern(bs, "A#-####")) return FUSION_EA;
|
||||||
|
|
||||||
|
return NO_CODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decode_amd_codename(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
amd_code_t code = decode_amd_codename_part1(data->brand_str);
|
||||||
|
|
||||||
|
if (code == ATHLON_64_X2 && data->l2_cache < 512)
|
||||||
|
code = SEMPRON_DUALCORE;
|
||||||
|
match_cpu_codename(cpudb_amd, COUNT_OF(cpudb_amd), data, code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpuid_identify_amd(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
load_amd_features(raw, data);
|
||||||
|
decode_amd_cache_info(raw, data);
|
||||||
|
decode_amd_number_of_cores(raw, data);
|
||||||
|
decode_amd_codename(raw, data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpuid_get_list_amd(struct cpu_list_t* list)
|
||||||
|
{
|
||||||
|
generic_get_cpu_list(cpudb_amd, COUNT_OF(cpudb_amd), list);
|
||||||
|
}
|
32
contrib/libcpuid/include/cpuid/recog_amd.h
Normal file
32
contrib/libcpuid/include/cpuid/recog_amd.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __RECOG_AMD_H__
|
||||||
|
#define __RECOG_AMD_H__
|
||||||
|
|
||||||
|
int cpuid_identify_amd(struct cpu_raw_data_t* raw, struct cpu_id_t* data);
|
||||||
|
void cpuid_get_list_amd(struct cpu_list_t* list);
|
||||||
|
|
||||||
|
#endif /* __RECOG_AMD_H__ */
|
791
contrib/libcpuid/include/cpuid/recog_intel.c
Normal file
791
contrib/libcpuid/include/cpuid/recog_intel.c
Normal file
@ -0,0 +1,791 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "libcpuid.h"
|
||||||
|
#include "recog_intel.h"
|
||||||
|
#include "libcpuid_util.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum _intel_code_t {
|
||||||
|
NA,
|
||||||
|
NO_CODE,
|
||||||
|
PENTIUM = 10,
|
||||||
|
MOBILE_PENTIUM,
|
||||||
|
|
||||||
|
XEON = 20,
|
||||||
|
XEON_IRWIN,
|
||||||
|
XEONMP,
|
||||||
|
XEON_POTOMAC,
|
||||||
|
XEON_I7,
|
||||||
|
XEON_GAINESTOWN,
|
||||||
|
XEON_WESTMERE,
|
||||||
|
|
||||||
|
MOBILE_PENTIUM_M = 30,
|
||||||
|
CELERON,
|
||||||
|
MOBILE_CELERON,
|
||||||
|
NOT_CELERON,
|
||||||
|
|
||||||
|
|
||||||
|
CORE_SOLO = 40,
|
||||||
|
MOBILE_CORE_SOLO,
|
||||||
|
CORE_DUO,
|
||||||
|
MOBILE_CORE_DUO,
|
||||||
|
|
||||||
|
WOLFDALE = 50,
|
||||||
|
MEROM,
|
||||||
|
PENRYN,
|
||||||
|
QUAD_CORE,
|
||||||
|
DUAL_CORE_HT,
|
||||||
|
QUAD_CORE_HT,
|
||||||
|
MORE_THAN_QUADCORE,
|
||||||
|
PENTIUM_D,
|
||||||
|
|
||||||
|
ATOM = 60,
|
||||||
|
ATOM_SILVERTHORNE,
|
||||||
|
ATOM_DIAMONDVILLE,
|
||||||
|
ATOM_PINEVIEW,
|
||||||
|
ATOM_CEDARVIEW,
|
||||||
|
|
||||||
|
CORE_I3 = 70,
|
||||||
|
CORE_I5,
|
||||||
|
CORE_I7,
|
||||||
|
CORE_IVY3, /* 22nm Core-iX */
|
||||||
|
CORE_IVY5,
|
||||||
|
CORE_IVY7,
|
||||||
|
CORE_HASWELL3, /* 22nm Core-iX, Haswell */
|
||||||
|
CORE_HASWELL5,
|
||||||
|
CORE_HASWELL7,
|
||||||
|
};
|
||||||
|
typedef enum _intel_code_t intel_code_t;
|
||||||
|
|
||||||
|
enum _intel_model_t {
|
||||||
|
UNKNOWN = -1,
|
||||||
|
_3000 = 100,
|
||||||
|
_3100,
|
||||||
|
_3200,
|
||||||
|
X3200,
|
||||||
|
_3300,
|
||||||
|
X3300,
|
||||||
|
_5100,
|
||||||
|
_5200,
|
||||||
|
_5300,
|
||||||
|
_5400,
|
||||||
|
_2xxx, /* Core i[357] 2xxx */
|
||||||
|
_3xxx, /* Core i[357] 3xxx */
|
||||||
|
};
|
||||||
|
typedef enum _intel_model_t intel_model_t;
|
||||||
|
|
||||||
|
const struct match_entry_t cpudb_intel[] = {
|
||||||
|
{ -1, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown Intel CPU" },
|
||||||
|
|
||||||
|
/* i486 */
|
||||||
|
{ 4, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown i486" },
|
||||||
|
{ 4, 0, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 DX-25/33" },
|
||||||
|
{ 4, 1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 DX-50" },
|
||||||
|
{ 4, 2, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 SX" },
|
||||||
|
{ 4, 3, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 DX2" },
|
||||||
|
{ 4, 4, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 SL" },
|
||||||
|
{ 4, 5, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 SX2" },
|
||||||
|
{ 4, 7, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 DX2 WriteBack" },
|
||||||
|
{ 4, 8, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 DX4" },
|
||||||
|
{ 4, 9, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "i486 DX4 WriteBack" },
|
||||||
|
|
||||||
|
/* All Pentia:
|
||||||
|
Pentium 1 */
|
||||||
|
{ 5, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown Pentium" },
|
||||||
|
{ 5, 0, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium A-Step" },
|
||||||
|
{ 5, 1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium 1 (0.8u)" },
|
||||||
|
{ 5, 2, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium 1 (0.35u)" },
|
||||||
|
{ 5, 3, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium OverDrive" },
|
||||||
|
{ 5, 4, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium 1 (0.35u)" },
|
||||||
|
{ 5, 7, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium 1 (0.35u)" },
|
||||||
|
{ 5, 8, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium MMX (0.25u)" },
|
||||||
|
|
||||||
|
/* Pentium 2 / 3 / M / Conroe / whatsnext - all P6 based. */
|
||||||
|
{ 6, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown P6" },
|
||||||
|
{ 6, 0, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium Pro" },
|
||||||
|
{ 6, 1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium Pro" },
|
||||||
|
{ 6, 3, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium II (Klamath)" },
|
||||||
|
{ 6, 5, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium II (Deschutes)" },
|
||||||
|
{ 6, 5, -1, -1, -1, 1, -1, -1, MOBILE_PENTIUM , 0, "Mobile Pentium II (Tonga)"},
|
||||||
|
{ 6, 6, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium II (Dixon)" },
|
||||||
|
|
||||||
|
{ 6, 3, -1, -1, -1, 1, -1, -1, XEON , 0, "P-II Xeon" },
|
||||||
|
{ 6, 5, -1, -1, -1, 1, -1, -1, XEON , 0, "P-II Xeon" },
|
||||||
|
{ 6, 6, -1, -1, -1, 1, -1, -1, XEON , 0, "P-II Xeon" },
|
||||||
|
|
||||||
|
{ 6, 5, -1, -1, -1, 1, -1, -1, CELERON , 0, "P-II Celeron (no L2)" },
|
||||||
|
{ 6, 6, -1, -1, -1, 1, -1, -1, CELERON , 0, "P-II Celeron (128K)" },
|
||||||
|
|
||||||
|
/* -------------------------------------------------- */
|
||||||
|
|
||||||
|
{ 6, 7, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium III (Katmai)" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium III (Coppermine)"},
|
||||||
|
{ 6, 10, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium III (Coppermine)"},
|
||||||
|
{ 6, 11, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Pentium III (Tualatin)" },
|
||||||
|
|
||||||
|
{ 6, 7, -1, -1, -1, 1, -1, -1, XEON , 0, "P-III Xeon" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, XEON , 0, "P-III Xeon" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, -1, -1, XEON , 0, "P-III Xeon" },
|
||||||
|
{ 6, 11, -1, -1, -1, 1, -1, -1, XEON , 0, "P-III Xeon" },
|
||||||
|
|
||||||
|
{ 6, 7, -1, -1, -1, 1, -1, -1, CELERON , 0, "P-III Celeron" },
|
||||||
|
{ 6, 8, -1, -1, -1, 1, -1, -1, CELERON , 0, "P-III Celeron" },
|
||||||
|
{ 6, 10, -1, -1, -1, 1, -1, -1, CELERON , 0, "P-III Celeron" },
|
||||||
|
{ 6, 11, -1, -1, -1, 1, -1, -1, CELERON , 0, "P-III Celeron" },
|
||||||
|
|
||||||
|
/* Netburst based (Pentium 4 and later)
|
||||||
|
classic P4s */
|
||||||
|
{ 15, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown Pentium 4" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, -1, -1, CELERON , 0, "Unknown P-4 Celeron" },
|
||||||
|
{ 15, -1, -1, 15, -1, 1, -1, -1, XEON , 0, "Unknown Xeon" },
|
||||||
|
|
||||||
|
{ 15, 0, -1, 15, -1, 1, -1, -1, NO_CODE , 0, "Pentium 4 (Willamette)" },
|
||||||
|
{ 15, 1, -1, 15, -1, 1, -1, -1, NO_CODE , 0, "Pentium 4 (Willamette)" },
|
||||||
|
{ 15, 2, -1, 15, -1, 1, -1, -1, NO_CODE , 0, "Pentium 4 (Northwood)" },
|
||||||
|
{ 15, 3, -1, 15, -1, 1, -1, -1, NO_CODE , 0, "Pentium 4 (Prescott)" },
|
||||||
|
{ 15, 4, -1, 15, -1, 1, -1, -1, NO_CODE , 0, "Pentium 4 (Prescott)" },
|
||||||
|
{ 15, 6, -1, 15, -1, 1, -1, -1, NO_CODE , 0, "Pentium 4 (Cedar Mill)" },
|
||||||
|
{ 15, 0, -1, 15, -1, 1, -1, -1, MOBILE_PENTIUM , 0, "Mobile P-4 (Willamette)" },
|
||||||
|
{ 15, 1, -1, 15, -1, 1, -1, -1, MOBILE_PENTIUM , 0, "Mobile P-4 (Willamette)" },
|
||||||
|
{ 15, 2, -1, 15, -1, 1, -1, -1, MOBILE_PENTIUM , 0, "Mobile P-4 (Northwood)" },
|
||||||
|
{ 15, 3, -1, 15, -1, 1, -1, -1, MOBILE_PENTIUM , 0, "Mobile P-4 (Prescott)" },
|
||||||
|
{ 15, 4, -1, 15, -1, 1, -1, -1, MOBILE_PENTIUM , 0, "Mobile P-4 (Prescott)" },
|
||||||
|
{ 15, 6, -1, 15, -1, 1, -1, -1, MOBILE_PENTIUM , 0, "Mobile P-4 (Cedar Mill)" },
|
||||||
|
|
||||||
|
/* server CPUs */
|
||||||
|
{ 15, 0, -1, 15, -1, 1, -1, -1, XEON , 0, "Xeon (Foster)" },
|
||||||
|
{ 15, 1, -1, 15, -1, 1, -1, -1, XEON , 0, "Xeon (Foster)" },
|
||||||
|
{ 15, 2, -1, 15, -1, 1, -1, -1, XEON , 0, "Xeon (Prestonia)" },
|
||||||
|
{ 15, 2, -1, 15, -1, 1, -1, -1, XEONMP , 0, "Xeon (Gallatin)" },
|
||||||
|
{ 15, 3, -1, 15, -1, 1, -1, -1, XEON , 0, "Xeon (Nocona)" },
|
||||||
|
{ 15, 4, -1, 15, -1, 1, -1, -1, XEON , 0, "Xeon (Nocona)" },
|
||||||
|
{ 15, 4, -1, 15, -1, 1, -1, -1, XEON_IRWIN , 0, "Xeon (Irwindale)" },
|
||||||
|
{ 15, 4, -1, 15, -1, 1, -1, -1, XEONMP , 0, "Xeon (Cranford)" },
|
||||||
|
{ 15, 4, -1, 15, -1, 1, -1, -1, XEON_POTOMAC , 0, "Xeon (Potomac)" },
|
||||||
|
{ 15, 6, -1, 15, -1, 1, -1, -1, XEON , 0, "Xeon (Dempsey)" },
|
||||||
|
|
||||||
|
/* Pentium Ds */
|
||||||
|
{ 15, 4, 4, 15, -1, 1, -1, -1, NO_CODE , 0, "Pentium D" },
|
||||||
|
{ 15, 4, -1, 15, -1, 1, -1, -1, PENTIUM_D , 0, "Pentium D" },
|
||||||
|
{ 15, 4, 7, 15, -1, 1, -1, -1, NO_CODE , 0, "Pentium D" },
|
||||||
|
{ 15, 6, -1, 15, -1, 1, -1, -1, PENTIUM_D , 0, "Pentium D" },
|
||||||
|
|
||||||
|
/* Celeron and Celeron Ds */
|
||||||
|
{ 15, 1, -1, 15, -1, 1, -1, -1, CELERON , 0, "P-4 Celeron (128K)" },
|
||||||
|
{ 15, 2, -1, 15, -1, 1, -1, -1, CELERON , 0, "P-4 Celeron (128K)" },
|
||||||
|
{ 15, 3, -1, 15, -1, 1, -1, -1, CELERON , 0, "Celeron D" },
|
||||||
|
{ 15, 4, -1, 15, -1, 1, -1, -1, CELERON , 0, "Celeron D" },
|
||||||
|
{ 15, 6, -1, 15, -1, 1, -1, -1, CELERON , 0, "Celeron D" },
|
||||||
|
|
||||||
|
/* -------------------------------------------------- */
|
||||||
|
/* Intel Core microarchitecture - P6-based */
|
||||||
|
|
||||||
|
{ 6, 9, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown Pentium M" },
|
||||||
|
{ 6, 9, -1, -1, -1, 1, -1, -1, MOBILE_PENTIUM_M , 0, "Unknown Pentium M" },
|
||||||
|
{ 6, 9, -1, -1, -1, 1, -1, -1, PENTIUM , 0, "Pentium M (Banias)" },
|
||||||
|
{ 6, 9, -1, -1, -1, 1, -1, -1, MOBILE_PENTIUM_M , 0, "Pentium M (Banias)" },
|
||||||
|
{ 6, 9, -1, -1, -1, 1, -1, -1, CELERON , 0, "Celeron M" },
|
||||||
|
{ 6, 13, -1, -1, -1, 1, -1, -1, PENTIUM , 0, "Pentium M (Dothan)" },
|
||||||
|
{ 6, 13, -1, -1, -1, 1, -1, -1, MOBILE_PENTIUM_M , 0, "Pentium M (Dothan)" },
|
||||||
|
{ 6, 13, -1, -1, -1, 1, -1, -1, CELERON , 0, "Celeron M" },
|
||||||
|
|
||||||
|
{ 6, 12, -1, -1, -1, -1, -1, -1, ATOM , 0, "Unknown Atom" },
|
||||||
|
{ 6, 12, -1, -1, -1, -1, -1, -1, ATOM_DIAMONDVILLE , 0, "Atom (Diamondville)" },
|
||||||
|
{ 6, 12, -1, -1, -1, -1, -1, -1, ATOM_SILVERTHORNE , 0, "Atom (Silverthorne)" },
|
||||||
|
{ 6, 12, -1, -1, -1, -1, -1, -1, ATOM_CEDARVIEW , 0, "Atom (Cedarview)" },
|
||||||
|
{ 6, 6, -1, -1, -1, -1, -1, -1, ATOM_CEDARVIEW , 0, "Atom (Cedarview)" },
|
||||||
|
{ 6, 12, -1, -1, -1, -1, -1, -1, ATOM_PINEVIEW , 0, "Atom (Pineview)" },
|
||||||
|
|
||||||
|
/* -------------------------------------------------- */
|
||||||
|
|
||||||
|
{ 6, 14, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown Yonah" },
|
||||||
|
{ 6, 14, -1, -1, -1, 1, -1, -1, CORE_SOLO , 0, "Yonah (Core Solo)" },
|
||||||
|
{ 6, 14, -1, -1, -1, 2, -1, -1, CORE_DUO , 0, "Yonah (Core Duo)" },
|
||||||
|
{ 6, 14, -1, -1, -1, 1, -1, -1, MOBILE_CORE_SOLO , 0, "Yonah (Core Solo)" },
|
||||||
|
{ 6, 14, -1, -1, -1, 2, -1, -1, MOBILE_CORE_DUO , 0, "Yonah (Core Duo)" },
|
||||||
|
{ 6, 14, -1, -1, -1, 1, -1, -1, CORE_SOLO , 0, "Yonah (Core Solo)" },
|
||||||
|
|
||||||
|
{ 6, 15, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Unknown Core 2" },
|
||||||
|
{ 6, 15, -1, -1, -1, 2, 4096, -1, CORE_DUO , 0, "Conroe (Core 2 Duo)" },
|
||||||
|
{ 6, 15, -1, -1, -1, 2, 1024, -1, CORE_DUO , 0, "Conroe (Core 2 Duo) 1024K" },
|
||||||
|
{ 6, 15, -1, -1, -1, 2, 512, -1, CORE_DUO , 0, "Conroe (Core 2 Duo) 512K" },
|
||||||
|
{ 6, 15, -1, -1, -1, 4, -1, -1, QUAD_CORE , 0, "Kentsfield (Core 2 Quad)" },
|
||||||
|
{ 6, 15, -1, -1, -1, 4, 4096, -1, QUAD_CORE , 0, "Kentsfield (Core 2 Quad)" },
|
||||||
|
{ 6, 15, -1, -1, -1, 400, -1, -1, MORE_THAN_QUADCORE, 0, "More than quad-core" },
|
||||||
|
{ 6, 15, -1, -1, -1, 2, 2048, -1, CORE_DUO , 0, "Allendale (Core 2 Duo)" },
|
||||||
|
{ 6, 15, -1, -1, -1, 2, -1, -1, MOBILE_CORE_DUO , 0, "Merom (Core 2 Duo)" },
|
||||||
|
{ 6, 15, -1, -1, -1, 2, 2048, -1, MEROM , 0, "Merom (Core 2 Duo) 2048K" },
|
||||||
|
{ 6, 15, -1, -1, -1, 2, 4096, -1, MEROM , 0, "Merom (Core 2 Duo) 4096K" },
|
||||||
|
|
||||||
|
{ 6, 15, -1, -1, 15, 1, -1, -1, CELERON , 0, "Conroe-L (Celeron)" },
|
||||||
|
{ 6, 6, -1, -1, 22, 1, -1, -1, CELERON , 0, "Conroe-L (Celeron)" },
|
||||||
|
{ 6, 15, -1, -1, 15, 2, -1, -1, CELERON , 0, "Conroe-L (Allendale)" },
|
||||||
|
{ 6, 6, -1, -1, 22, 2, -1, -1, CELERON , 0, "Conroe-L (Allendale)" },
|
||||||
|
|
||||||
|
|
||||||
|
{ 6, 6, -1, -1, 22, 1, -1, -1, NO_CODE , 0, "Unknown Core ?" },
|
||||||
|
{ 6, 7, -1, -1, 23, 1, -1, -1, NO_CODE , 0, "Unknown Core ?" },
|
||||||
|
{ 6, 6, -1, -1, 22, 400, -1, -1, MORE_THAN_QUADCORE, 0, "More than quad-core" },
|
||||||
|
{ 6, 7, -1, -1, 23, 400, -1, -1, MORE_THAN_QUADCORE, 0, "More than quad-core" },
|
||||||
|
|
||||||
|
{ 6, 7, -1, -1, 23, 1, -1, -1, CORE_SOLO , 0, "Unknown Core 45nm" },
|
||||||
|
{ 6, 7, -1, -1, 23, 1, -1, -1, CORE_DUO , 0, "Unknown Core 45nm" },
|
||||||
|
{ 6, 7, -1, -1, 23, 2, 1024, -1, WOLFDALE , 0, "Celeron Wolfdale 1M" },
|
||||||
|
{ 6, 7, -1, -1, 23, 2, 2048, -1, WOLFDALE , 0, "Wolfdale (Core 2 Duo) 2M" },
|
||||||
|
{ 6, 7, -1, -1, 23, 2, 3072, -1, WOLFDALE , 0, "Wolfdale (Core 2 Duo) 3M" },
|
||||||
|
{ 6, 7, -1, -1, 23, 2, 6144, -1, WOLFDALE , 0, "Wolfdale (Core 2 Duo) 6M" },
|
||||||
|
{ 6, 7, -1, -1, 23, 1, -1, -1, MOBILE_CORE_DUO , 0, "Penryn (Core 2 Duo)" },
|
||||||
|
{ 6, 7, -1, -1, 23, 2, 3072, -1, PENRYN , 0, "Penryn (Core 2 Duo) 3M" },
|
||||||
|
{ 6, 7, -1, -1, 23, 2, 6144, -1, PENRYN , 0, "Penryn (Core 2 Duo) 6M" },
|
||||||
|
{ 6, 7, -1, -1, 23, 4, 2048, -1, QUAD_CORE , 0, "Yorkfield (Core 2 Quad) 2M"},
|
||||||
|
{ 6, 7, -1, -1, 23, 4, 3072, -1, QUAD_CORE , 0, "Yorkfield (Core 2 Quad) 3M"},
|
||||||
|
{ 6, 7, -1, -1, 23, 4, 6144, -1, QUAD_CORE , 0, "Yorkfield (Core 2 Quad) 6M"},
|
||||||
|
|
||||||
|
{ 6, 5, -1, -1, 37, 2, -1, -1, NO_CODE , 0, "Unknown Core i3/i5 CPU" },
|
||||||
|
{ 6, 5, -1, -1, 37, 2, -1, 4096, CORE_I7 , 0, "Arrandale (Core i7)" },
|
||||||
|
{ 6, 5, -1, -1, 37, 2, -1, 3072, CORE_I5 , 0, "Arrandale (Core i5)" },
|
||||||
|
{ 6, 5, -1, -1, 37, 2, -1, 4096, CORE_I5 , 0, "Clarkdale (Core i5)" },
|
||||||
|
{ 6, 5, -1, -1, 37, 4, -1, 8192, CORE_I5 , 0, "Lynnfield (Core i5)" },
|
||||||
|
{ 6, 5, -1, -1, 37, 2, -1, 3072, CORE_I3 , 0, "Arrandale (Core i3)" },
|
||||||
|
{ 6, 5, -1, -1, 37, 2, -1, 4096, CORE_I3 , 0, "Clarkdale (Core i3)" },
|
||||||
|
|
||||||
|
{ 6, 10, -1, -1, 42, -1, -1, -1, NO_CODE , 0, "Unknown Sandy Bridge" },
|
||||||
|
{ 6, 10, -1, -1, 42, -1, -1, -1, CORE_I7 , 0, "Sandy Bridge i7" },
|
||||||
|
{ 6, 10, -1, -1, 42, 4, -1, -1, CORE_I7 , 0, "Sandy Bridge (Core i7)" },
|
||||||
|
{ 6, 10, -1, -1, 42, 4, -1, -1, CORE_I5 , 0, "Sandy Bridge (Core i5)" },
|
||||||
|
{ 6, 10, -1, -1, 42, 2, -1, -1, CORE_I3 , 0, "Sandy Bridge (Core i3)" },
|
||||||
|
{ 6, 10, -1, -1, 42, 1, -1, -1, CELERON , 0, "Celeron (Sandy Bridge)" },
|
||||||
|
{ 6, 10, -1, -1, 42, 2, -1, -1, CELERON , 0, "Celeron (Sandy Bridge)" },
|
||||||
|
{ 6, 10, -1, -1, 42, 2, -1, -1, PENTIUM , 0, "Pentium (Sandy Bridge)" },
|
||||||
|
|
||||||
|
{ 6, 10, -1, -1, 26, 1, -1, -1, CORE_I7 , 0, "Intel Core i7" },
|
||||||
|
{ 6, 10, -1, -1, 26, 4, -1, -1, CORE_I7 , 0, "Bloomfield (Core i7)" },
|
||||||
|
{ 6, 10, -1, -1, 30, 4, -1, -1, CORE_I7 , 0, "Lynnfield (Core i7)" },
|
||||||
|
{ 6, 10, -1, -1, 26, 4, -1, -1, XEON_I7 , 0, "Xeon (Bloomfield)" },
|
||||||
|
|
||||||
|
{ 6, 10, -1, -1, 26, 4, -1, -1, XEON_GAINESTOWN , 0, "Xeon (Gainestown)" },
|
||||||
|
{ 6, 10, -1, -1, 26, 4, -1, 4096, XEON_GAINESTOWN , 0, "Xeon (Gainestown) 4M" },
|
||||||
|
{ 6, 10, -1, -1, 26, 4, -1, 8192, XEON_GAINESTOWN , 0, "Xeon (Gainestown) 8M" },
|
||||||
|
|
||||||
|
{ 6, 12, -1, -1, 44, -1, -1, -1, XEON_WESTMERE , 0, "Xeon (Westmere-based)" },
|
||||||
|
{ 6, 12, -1, -1, 44, 4, -1, 12288, CORE_I7 , 0, "Gulftown (Core i7)" },
|
||||||
|
{ 6, 12, -1, -1, 44, -1, -1, 12288, XEON_WESTMERE , 0, "Xeon (Gulftown)" },
|
||||||
|
|
||||||
|
{ 6, 13, -1, -1, 45, -1, -1, -1, XEON , 0, "Xeon (Sandy Bridge)" },
|
||||||
|
|
||||||
|
{ 6, 13, -1, -1, 45, -1, -1, -1, CORE_I7 , 0, "Sandy Bridge-E (Core i7)" },
|
||||||
|
{ 6, 13, -1, -1, 45, -1, -1, -1, CORE_I5 , 0, "Sandy Bridge-E (Core i5)" },
|
||||||
|
{ 6, 13, -1, -1, 45, -1, -1, -1, CORE_I3 , 0, "Sandy Bridge-E (Core i3)" },
|
||||||
|
|
||||||
|
{ 6, 10, -1, -1, 58, 4, -1, -1, CORE_IVY7 , 0, "Ivy Bridge (Core i7)" },
|
||||||
|
{ 6, 10, -1, -1, 58, 4, -1, -1, CORE_IVY5 , 0, "Ivy Bridge (Core i5)" },
|
||||||
|
{ 6, 10, -1, -1, 58, 2, -1, -1, CORE_IVY3 , 0, "Ivy Bridge (Core i3)" },
|
||||||
|
|
||||||
|
{ 6, 12, -1, -1, 60, 4, -1, -1, CORE_HASWELL7 , 0, "Haswell (Core i7)" },
|
||||||
|
{ 6, 12, -1, -1, 60, 4, -1, -1, CORE_HASWELL5 , 0, "Haswell (Core i5)" },
|
||||||
|
{ 6, 12, -1, -1, 60, 2, -1, -1, CORE_HASWELL3 , 0, "Haswell (Core i3)" },
|
||||||
|
|
||||||
|
|
||||||
|
/* Core microarchitecture-based Xeons: */
|
||||||
|
{ 6, 14, -1, -1, 14, 1, -1, -1, XEON , 0, "Xeon LV" },
|
||||||
|
{ 6, 15, -1, -1, 15, 2, 4096, -1, XEON , _5100, "Xeon (Woodcrest)" },
|
||||||
|
{ 6, 15, -1, -1, 15, 2, 2048, -1, XEON , _3000, "Xeon (Conroe/2M)" },
|
||||||
|
{ 6, 15, -1, -1, 15, 2, 4096, -1, XEON , _3000, "Xeon (Conroe/4M)" },
|
||||||
|
{ 6, 15, -1, -1, 15, 4, 4096, -1, XEON , X3200, "Xeon (Kentsfield)" },
|
||||||
|
{ 6, 15, -1, -1, 15, 4, 4096, -1, XEON , _5300, "Xeon (Clovertown)" },
|
||||||
|
{ 6, 7, -1, -1, 23, 2, 6144, -1, XEON , _3100, "Xeon (Wolfdale)" },
|
||||||
|
{ 6, 7, -1, -1, 23, 2, 6144, -1, XEON , _5200, "Xeon (Wolfdale DP)" },
|
||||||
|
{ 6, 7, -1, -1, 23, 4, 6144, -1, XEON , _5400, "Xeon (Harpertown)" },
|
||||||
|
{ 6, 7, -1, -1, 23, 4, 3072, -1, XEON , X3300, "Xeon (Yorkfield/3M)" },
|
||||||
|
{ 6, 7, -1, -1, 23, 4, 6144, -1, XEON , X3300, "Xeon (Yorkfield/6M)" },
|
||||||
|
|
||||||
|
/* Itaniums */
|
||||||
|
{ 7, -1, -1, -1, -1, 1, -1, -1, NO_CODE , 0, "Itanium" },
|
||||||
|
{ 15, -1, -1, 16, -1, 1, -1, -1, NO_CODE , 0, "Itanium 2" },
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void load_intel_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
const struct feature_map_t matchtable_edx1[] = {
|
||||||
|
{ 18, CPU_FEATURE_PN },
|
||||||
|
{ 21, CPU_FEATURE_DTS },
|
||||||
|
{ 22, CPU_FEATURE_ACPI },
|
||||||
|
{ 27, CPU_FEATURE_SS },
|
||||||
|
{ 29, CPU_FEATURE_TM },
|
||||||
|
{ 30, CPU_FEATURE_IA64 },
|
||||||
|
{ 31, CPU_FEATURE_PBE },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_ecx1[] = {
|
||||||
|
{ 1, CPU_FEATURE_PCLMUL },
|
||||||
|
{ 2, CPU_FEATURE_DTS64 },
|
||||||
|
{ 4, CPU_FEATURE_DS_CPL },
|
||||||
|
{ 5, CPU_FEATURE_VMX },
|
||||||
|
{ 6, CPU_FEATURE_SMX },
|
||||||
|
{ 7, CPU_FEATURE_EST },
|
||||||
|
{ 8, CPU_FEATURE_TM2 },
|
||||||
|
{ 10, CPU_FEATURE_CID },
|
||||||
|
{ 14, CPU_FEATURE_XTPR },
|
||||||
|
{ 15, CPU_FEATURE_PDCM },
|
||||||
|
{ 18, CPU_FEATURE_DCA },
|
||||||
|
{ 20, CPU_FEATURE_SSE4_2 },
|
||||||
|
{ 22, CPU_FEATURE_MOVBE },
|
||||||
|
{ 25, CPU_FEATURE_AES },
|
||||||
|
{ 26, CPU_FEATURE_XSAVE },
|
||||||
|
{ 27, CPU_FEATURE_OSXSAVE },
|
||||||
|
{ 28, CPU_FEATURE_AVX },
|
||||||
|
{ 30, CPU_FEATURE_RDRAND },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_ebx7[] = {
|
||||||
|
{ 5, CPU_FEATURE_AVX2 },
|
||||||
|
};
|
||||||
|
const struct feature_map_t matchtable_edx81[] = {
|
||||||
|
{ 20, CPU_FEATURE_XD },
|
||||||
|
};
|
||||||
|
if (raw->basic_cpuid[0][0] >= 1) {
|
||||||
|
match_features(matchtable_edx1, COUNT_OF(matchtable_edx1), raw->basic_cpuid[1][3], data);
|
||||||
|
match_features(matchtable_ecx1, COUNT_OF(matchtable_ecx1), raw->basic_cpuid[1][2], data);
|
||||||
|
}
|
||||||
|
if (raw->basic_cpuid[0][0] >= 7) {
|
||||||
|
match_features(matchtable_ebx7, COUNT_OF(matchtable_ebx7), raw->basic_cpuid[7][1], data);
|
||||||
|
}
|
||||||
|
if (raw->ext_cpuid[0][0] >= 1) {
|
||||||
|
match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _cache_type_t {
|
||||||
|
L1I,
|
||||||
|
L1D,
|
||||||
|
L2,
|
||||||
|
L3
|
||||||
|
};
|
||||||
|
typedef enum _cache_type_t cache_type_t;
|
||||||
|
|
||||||
|
static void check_case(uint8_t on, cache_type_t cache, int size, int assoc, int linesize, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
if (!on) return;
|
||||||
|
switch (cache) {
|
||||||
|
case L1I:
|
||||||
|
data->l1_instruction_cache = size;
|
||||||
|
break;
|
||||||
|
case L1D:
|
||||||
|
data->l1_data_cache = size;
|
||||||
|
data->l1_assoc = assoc;
|
||||||
|
data->l1_cacheline = linesize;
|
||||||
|
break;
|
||||||
|
case L2:
|
||||||
|
data->l2_cache = size;
|
||||||
|
data->l2_assoc = assoc;
|
||||||
|
data->l2_cacheline = linesize;
|
||||||
|
break;
|
||||||
|
case L3:
|
||||||
|
data->l3_cache = size;
|
||||||
|
data->l3_assoc = assoc;
|
||||||
|
data->l3_cacheline = linesize;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decode_intel_oldstyle_cache_info(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
uint8_t f[256] = {0};
|
||||||
|
int reg, off;
|
||||||
|
uint32_t x;
|
||||||
|
for (reg = 0; reg < 4; reg++) {
|
||||||
|
x = raw->basic_cpuid[2][reg];
|
||||||
|
if (x & 0x80000000) continue;
|
||||||
|
for (off = 0; off < 4; off++) {
|
||||||
|
f[x & 0xff] = 1;
|
||||||
|
x >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_case(f[0x06], L1I, 8, 4, 32, data);
|
||||||
|
check_case(f[0x08], L1I, 16, 4, 32, data);
|
||||||
|
check_case(f[0x0A], L1D, 8, 2, 32, data);
|
||||||
|
check_case(f[0x0C], L1D, 16, 4, 32, data);
|
||||||
|
check_case(f[0x22], L3, 512, 4, 64, data);
|
||||||
|
check_case(f[0x23], L3, 1024, 8, 64, data);
|
||||||
|
check_case(f[0x25], L3, 2048, 8, 64, data);
|
||||||
|
check_case(f[0x29], L3, 4096, 8, 64, data);
|
||||||
|
check_case(f[0x2C], L1D, 32, 8, 64, data);
|
||||||
|
check_case(f[0x30], L1I, 32, 8, 64, data);
|
||||||
|
check_case(f[0x39], L2, 128, 4, 64, data);
|
||||||
|
check_case(f[0x3A], L2, 192, 6, 64, data);
|
||||||
|
check_case(f[0x3B], L2, 128, 2, 64, data);
|
||||||
|
check_case(f[0x3C], L2, 256, 4, 64, data);
|
||||||
|
check_case(f[0x3D], L2, 384, 6, 64, data);
|
||||||
|
check_case(f[0x3E], L2, 512, 4, 64, data);
|
||||||
|
check_case(f[0x41], L2, 128, 4, 32, data);
|
||||||
|
check_case(f[0x42], L2, 256, 4, 32, data);
|
||||||
|
check_case(f[0x43], L2, 512, 4, 32, data);
|
||||||
|
check_case(f[0x44], L2, 1024, 4, 32, data);
|
||||||
|
check_case(f[0x45], L2, 2048, 4, 32, data);
|
||||||
|
check_case(f[0x46], L3, 4096, 4, 64, data);
|
||||||
|
check_case(f[0x47], L3, 8192, 8, 64, data);
|
||||||
|
check_case(f[0x4A], L3, 6144, 12, 64, data);
|
||||||
|
check_case(f[0x4B], L3, 8192, 16, 64, data);
|
||||||
|
check_case(f[0x4C], L3, 12288, 12, 64, data);
|
||||||
|
check_case(f[0x4D], L3, 16384, 16, 64, data);
|
||||||
|
check_case(f[0x4E], L2, 6144, 24, 64, data);
|
||||||
|
check_case(f[0x60], L1D, 16, 8, 64, data);
|
||||||
|
check_case(f[0x66], L1D, 8, 4, 64, data);
|
||||||
|
check_case(f[0x67], L1D, 16, 4, 64, data);
|
||||||
|
check_case(f[0x68], L1D, 32, 4, 64, data);
|
||||||
|
/* The following four entries are trace cache. Intel does not
|
||||||
|
* specify a cache-line size, so we use -1 instead
|
||||||
|
*/
|
||||||
|
check_case(f[0x70], L1I, 12, 8, -1, data);
|
||||||
|
check_case(f[0x71], L1I, 16, 8, -1, data);
|
||||||
|
check_case(f[0x72], L1I, 32, 8, -1, data);
|
||||||
|
check_case(f[0x73], L1I, 64, 8, -1, data);
|
||||||
|
|
||||||
|
check_case(f[0x78], L2, 1024, 4, 64, data);
|
||||||
|
check_case(f[0x79], L2, 128, 8, 64, data);
|
||||||
|
check_case(f[0x7A], L2, 256, 8, 64, data);
|
||||||
|
check_case(f[0x7B], L2, 512, 8, 64, data);
|
||||||
|
check_case(f[0x7C], L2, 1024, 8, 64, data);
|
||||||
|
check_case(f[0x7D], L2, 2048, 8, 64, data);
|
||||||
|
check_case(f[0x7F], L2, 512, 2, 64, data);
|
||||||
|
check_case(f[0x82], L2, 256, 8, 32, data);
|
||||||
|
check_case(f[0x83], L2, 512, 8, 32, data);
|
||||||
|
check_case(f[0x84], L2, 1024, 8, 32, data);
|
||||||
|
check_case(f[0x85], L2, 2048, 8, 32, data);
|
||||||
|
check_case(f[0x86], L2, 512, 4, 64, data);
|
||||||
|
check_case(f[0x87], L2, 1024, 8, 64, data);
|
||||||
|
|
||||||
|
if (f[0x49]) {
|
||||||
|
/* This flag is overloaded with two meanings. On Xeon MP
|
||||||
|
* (family 0xf, model 0x6) this means L3 cache. On all other
|
||||||
|
* CPUs (notably Conroe et al), this is L2 cache. In both cases
|
||||||
|
* it means 4MB, 16-way associative, 64-byte line size.
|
||||||
|
*/
|
||||||
|
if (data->family == 0xf && data->model == 0x6) {
|
||||||
|
data->l3_cache = 4096;
|
||||||
|
data->l3_assoc = 16;
|
||||||
|
data->l3_cacheline = 64;
|
||||||
|
} else {
|
||||||
|
data->l2_cache = 4096;
|
||||||
|
data->l2_assoc = 16;
|
||||||
|
data->l2_cacheline = 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f[0x40]) {
|
||||||
|
/* Again, a special flag. It means:
|
||||||
|
* 1) If no L2 is specified, then CPU is w/o L2 (0 KB)
|
||||||
|
* 2) If L2 is specified by other flags, then, CPU is w/o L3.
|
||||||
|
*/
|
||||||
|
if (data->l2_cache == -1) {
|
||||||
|
data->l2_cache = 0;
|
||||||
|
} else {
|
||||||
|
data->l3_cache = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decode_intel_deterministic_cache_info(struct cpu_raw_data_t* raw,
|
||||||
|
struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int ecx;
|
||||||
|
int ways, partitions, linesize, sets, size, level, typenumber;
|
||||||
|
cache_type_t type;
|
||||||
|
for (ecx = 0; ecx < MAX_INTELFN4_LEVEL; ecx++) {
|
||||||
|
typenumber = raw->intel_fn4[ecx][0] & 0x1f;
|
||||||
|
if (typenumber == 0) break;
|
||||||
|
level = (raw->intel_fn4[ecx][0] >> 5) & 0x7;
|
||||||
|
if (level == 1 && typenumber == 1)
|
||||||
|
type = L1D;
|
||||||
|
else if (level == 1 && typenumber == 2)
|
||||||
|
type = L1I;
|
||||||
|
else if (level == 2 && typenumber == 3)
|
||||||
|
type = L2;
|
||||||
|
else if (level == 3 && typenumber == 3)
|
||||||
|
type = L3;
|
||||||
|
else {
|
||||||
|
warnf("deterministic_cache: unknown level/typenumber combo (%d/%d), cannot\n", level, typenumber);
|
||||||
|
warnf("deterministic_cache: recognize cache type\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ways = ((raw->intel_fn4[ecx][1] >> 22) & 0x3ff) + 1;
|
||||||
|
partitions = ((raw->intel_fn4[ecx][1] >> 12) & 0x3ff) + 1;
|
||||||
|
linesize = (raw->intel_fn4[ecx][1] & 0xfff) + 1;
|
||||||
|
sets = raw->intel_fn4[ecx][2] + 1;
|
||||||
|
size = ways * partitions * linesize * sets / 1024;
|
||||||
|
check_case(1, type, size, ways, linesize, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int decode_intel_extended_topology(struct cpu_raw_data_t* raw,
|
||||||
|
struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int i, level_type, num_smt = -1, num_core = -1;
|
||||||
|
for (i = 0; i < MAX_INTELFN11_LEVEL; i++) {
|
||||||
|
level_type = (raw->intel_fn11[i][2] & 0xff00) >> 8;
|
||||||
|
switch (level_type) {
|
||||||
|
case 0x01:
|
||||||
|
num_smt = raw->intel_fn11[i][1] & 0xffff;
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
num_core = raw->intel_fn11[i][1] & 0xffff;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num_smt == -1 || num_core == -1) return 0;
|
||||||
|
data->num_cores = num_core / num_smt;
|
||||||
|
data->num_logical_cpus = num_core;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decode_intel_number_of_cores(struct cpu_raw_data_t* raw,
|
||||||
|
struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int logical_cpus = -1, num_cores = -1;
|
||||||
|
|
||||||
|
if (raw->basic_cpuid[0][0] >= 11) {
|
||||||
|
if (decode_intel_extended_topology(raw, data)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw->basic_cpuid[0][0] >= 1) {
|
||||||
|
logical_cpus = (raw->basic_cpuid[1][1] >> 16) & 0xff;
|
||||||
|
if (raw->basic_cpuid[0][0] >= 4) {
|
||||||
|
num_cores = 1 + ((raw->basic_cpuid[4][0] >> 26) & 0x3f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data->flags[CPU_FEATURE_HT]) {
|
||||||
|
if (num_cores > 1) {
|
||||||
|
data->num_cores = num_cores;
|
||||||
|
data->num_logical_cpus = logical_cpus;
|
||||||
|
} else {
|
||||||
|
data->num_cores = 1;
|
||||||
|
data->num_logical_cpus = (logical_cpus >= 2 ? logical_cpus : 2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data->num_cores = data->num_logical_cpus = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static intel_code_t get_brand_code(struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
intel_code_t code = NO_CODE;
|
||||||
|
int i, need_matchtable = 1, core_ix_base = 0;
|
||||||
|
const char* bs = data->brand_str;
|
||||||
|
const char* s;
|
||||||
|
const struct { intel_code_t c; const char *search; } matchtable[] = {
|
||||||
|
{ XEONMP, "Xeon MP" },
|
||||||
|
{ XEONMP, "Xeon(TM) MP" },
|
||||||
|
{ XEON, "Xeon" },
|
||||||
|
{ CELERON, "Celeron" },
|
||||||
|
{ MOBILE_PENTIUM_M, "Pentium(R) M" },
|
||||||
|
{ CORE_SOLO, "Pentium(R) Dual CPU" },
|
||||||
|
{ PENTIUM_D, "Pentium(R) D" },
|
||||||
|
{ PENTIUM, "Pentium" },
|
||||||
|
{ CORE_SOLO, "Genuine Intel(R) CPU" },
|
||||||
|
{ CORE_SOLO, "Intel(R) Core(TM)" },
|
||||||
|
{ ATOM_DIAMONDVILLE, "Atom(TM) CPU [N ][23]## " },
|
||||||
|
{ ATOM_SILVERTHORNE, "Atom(TM) CPU Z" },
|
||||||
|
{ ATOM_PINEVIEW, "Atom(TM) CPU D" },
|
||||||
|
{ ATOM_CEDARVIEW, "Atom(TM) CPU N####" },
|
||||||
|
{ ATOM, "Atom(TM) CPU" },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (strstr(bs, "Mobile")) {
|
||||||
|
need_matchtable = 0;
|
||||||
|
if (strstr(bs, "Celeron"))
|
||||||
|
code = MOBILE_CELERON;
|
||||||
|
else if (strstr(bs, "Pentium"))
|
||||||
|
code = MOBILE_PENTIUM;
|
||||||
|
}
|
||||||
|
if ((i = match_pattern(bs, "Core(TM) i[357]")) != 0) {
|
||||||
|
/* Core i3, Core i5 or Core i7 */
|
||||||
|
need_matchtable = 0;
|
||||||
|
|
||||||
|
core_ix_base = CORE_I3;
|
||||||
|
|
||||||
|
/* if it has RdRand, then it is at least Ivy Bridge */
|
||||||
|
if (data->flags[CPU_FEATURE_RDRAND])
|
||||||
|
core_ix_base = CORE_IVY3;
|
||||||
|
/* if it has FMA, then it is at least Haswell */
|
||||||
|
if (data->flags[CPU_FEATURE_FMA3])
|
||||||
|
core_ix_base = CORE_HASWELL3;
|
||||||
|
|
||||||
|
switch (bs[i + 9]) {
|
||||||
|
case '3': code = core_ix_base + 0; break;
|
||||||
|
case '5': code = core_ix_base + 1; break;
|
||||||
|
case '7': code = core_ix_base + 2; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (need_matchtable) {
|
||||||
|
for (i = 0; i < COUNT_OF(matchtable); i++)
|
||||||
|
if (match_pattern(bs, matchtable[i].search)) {
|
||||||
|
code = matchtable[i].c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
debugf(2, "intel matchtable result is %d\n", code);
|
||||||
|
}
|
||||||
|
if (code == XEON) {
|
||||||
|
if (match_pattern(bs, "W35##") || match_pattern(bs, "[ELXW]75##"))
|
||||||
|
code = XEON_I7;
|
||||||
|
else if (match_pattern(bs, "[ELXW]55##"))
|
||||||
|
code = XEON_GAINESTOWN;
|
||||||
|
else if (match_pattern(bs, "[ELXW]56##"))
|
||||||
|
code = XEON_WESTMERE;
|
||||||
|
else if (data->l3_cache > 0)
|
||||||
|
code = XEON_IRWIN;
|
||||||
|
}
|
||||||
|
if (code == XEONMP && data->l3_cache > 0)
|
||||||
|
code = XEON_POTOMAC;
|
||||||
|
if (code == CORE_SOLO) {
|
||||||
|
s = strstr(bs, "CPU");
|
||||||
|
if (s) {
|
||||||
|
s += 3;
|
||||||
|
while (*s == ' ') s++;
|
||||||
|
if (*s == 'T')
|
||||||
|
code = (data->num_cores == 1) ? MOBILE_CORE_SOLO : MOBILE_CORE_DUO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (code == CORE_SOLO) {
|
||||||
|
switch (data->num_cores) {
|
||||||
|
case 1: break;
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
code = CORE_DUO;
|
||||||
|
if (data->num_logical_cpus > 2)
|
||||||
|
code = DUAL_CORE_HT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
code = QUAD_CORE;
|
||||||
|
if (data->num_logical_cpus > 4)
|
||||||
|
code = QUAD_CORE_HT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
code = MORE_THAN_QUADCORE; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == CORE_DUO && data->ext_model >= 23) {
|
||||||
|
code = WOLFDALE;
|
||||||
|
}
|
||||||
|
if (code == PENTIUM_D && data->ext_model >= 23) {
|
||||||
|
code = WOLFDALE;
|
||||||
|
}
|
||||||
|
if (code == MOBILE_CORE_DUO && data->model != 14) {
|
||||||
|
if (data->ext_model < 23) {
|
||||||
|
code = MEROM;
|
||||||
|
} else {
|
||||||
|
code = PENRYN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
static intel_model_t get_model_code(struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int l = (int) strlen(data->brand_str);
|
||||||
|
const char *bs = data->brand_str;
|
||||||
|
int mod_flags = 0, model_no = 0, ndigs = 0;
|
||||||
|
/* If the CPU is a Core ix, then just return the model number generation: */
|
||||||
|
if ((i = match_pattern(bs, "Core(TM) i[357]")) != 0) {
|
||||||
|
i += 11;
|
||||||
|
if (i + 4 >= l) return UNKNOWN;
|
||||||
|
if (bs[i] == '2') return _2xxx;
|
||||||
|
if (bs[i] == '3') return _3xxx;
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Core2-based Xeons: */
|
||||||
|
while (i < l - 3) {
|
||||||
|
if (bs[i] == 'C' && bs[i+1] == 'P' && bs[i+2] == 'U')
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= l - 3) return UNKNOWN;
|
||||||
|
i += 3;
|
||||||
|
while (i < l - 4 && bs[i] == ' ') i++;
|
||||||
|
if (i >= l - 4) return UNKNOWN;
|
||||||
|
while (i < l - 4 && !isdigit(bs[i])) {
|
||||||
|
if (bs[i] >= 'A' && bs[i] <= 'Z')
|
||||||
|
mod_flags |= (1 << (bs[i] - 'A'));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= l - 4) return UNKNOWN;
|
||||||
|
while (isdigit(bs[i])) {
|
||||||
|
ndigs++;
|
||||||
|
model_no = model_no * 10 + (int) (bs[i] - '0');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (ndigs != 4) return UNKNOWN;
|
||||||
|
#define HAVE(ch, flags) ((flags & (1 << ((int)(ch-'A')))) != 0)
|
||||||
|
switch (model_no / 100) {
|
||||||
|
case 30: return _3000;
|
||||||
|
case 31: return _3100;
|
||||||
|
case 32:
|
||||||
|
{
|
||||||
|
return (HAVE('X', mod_flags)) ? X3200 : _3200;
|
||||||
|
}
|
||||||
|
case 33:
|
||||||
|
{
|
||||||
|
return (HAVE('X', mod_flags)) ? X3300 : _3300;
|
||||||
|
}
|
||||||
|
case 51: return _5100;
|
||||||
|
case 52: return _5200;
|
||||||
|
case 53: return _5300;
|
||||||
|
case 54: return _5400;
|
||||||
|
default:
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
#undef HAVE
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data)
|
||||||
|
{
|
||||||
|
load_intel_features(raw, data);
|
||||||
|
if (raw->basic_cpuid[0][0] >= 4) {
|
||||||
|
/* Deterministic way is preferred, being more generic */
|
||||||
|
decode_intel_deterministic_cache_info(raw, data);
|
||||||
|
} else if (raw->basic_cpuid[0][0] >= 2) {
|
||||||
|
decode_intel_oldstyle_cache_info(raw, data);
|
||||||
|
}
|
||||||
|
decode_intel_number_of_cores(raw, data);
|
||||||
|
match_cpu_codename(cpudb_intel, COUNT_OF(cpudb_intel), data,
|
||||||
|
get_brand_code(data), get_model_code(data));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpuid_get_list_intel(struct cpu_list_t* list)
|
||||||
|
{
|
||||||
|
generic_get_cpu_list(cpudb_intel, COUNT_OF(cpudb_intel), list);
|
||||||
|
}
|
32
contrib/libcpuid/include/cpuid/recog_intel.h
Normal file
32
contrib/libcpuid/include/cpuid/recog_intel.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 Veselin Georgiev,
|
||||||
|
* anrieffNOSPAM @ mgail_DOT.com (convert to gmail)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#ifndef __RECOG_INTEL_H__
|
||||||
|
#define __RECOG_INTEL_H__
|
||||||
|
|
||||||
|
int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data);
|
||||||
|
void cpuid_get_list_intel(struct cpu_list_t* list);
|
||||||
|
|
||||||
|
#endif /*__RECOG_INTEL_H__*/
|
20
contrib/libdivide/LICENSE.txt
Normal file
20
contrib/libdivide/LICENSE.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
libdivide
|
||||||
|
Copyright (C) 2010 ridiculous_fish
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
libdivide@ridiculousfish.com
|
2
contrib/libdivide/README.txt
Normal file
2
contrib/libdivide/README.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
https://github.com/ridiculousfish/libdivide
|
||||||
|
http://libdivide.com/
|
1342
contrib/libdivide/libdivide.h
Normal file
1342
contrib/libdivide/libdivide.h
Normal file
File diff suppressed because it is too large
Load Diff
20
contrib/libdouble-conversion/CMakeLists.txt
Normal file
20
contrib/libdouble-conversion/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
add_library (double-conversion
|
||||||
|
double-conversion/bignum.cc
|
||||||
|
double-conversion/bignum-dtoa.cc
|
||||||
|
double-conversion/bignum-dtoa.h
|
||||||
|
double-conversion/bignum.h
|
||||||
|
double-conversion/cached-powers.cc
|
||||||
|
double-conversion/cached-powers.h
|
||||||
|
double-conversion/diy-fp.cc
|
||||||
|
double-conversion/diy-fp.h
|
||||||
|
double-conversion/double-conversion.cc
|
||||||
|
double-conversion/double-conversion.h
|
||||||
|
double-conversion/fast-dtoa.cc
|
||||||
|
double-conversion/fast-dtoa.h
|
||||||
|
double-conversion/fixed-dtoa.cc
|
||||||
|
double-conversion/fixed-dtoa.h
|
||||||
|
double-conversion/ieee.h
|
||||||
|
double-conversion/strtod.cc
|
||||||
|
double-conversion/strtod.h
|
||||||
|
double-conversion/utils.h
|
||||||
|
)
|
26
contrib/libdouble-conversion/COPYING
Normal file
26
contrib/libdouble-conversion/COPYING
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Copyright 2006-2011, the V8 project authors. All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
1
contrib/libdouble-conversion/README
Normal file
1
contrib/libdouble-conversion/README
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://github.com/google/double-conversion/tree/b2e9a94943dc9f64acc2967eebb241bf2fc10d5b
|
641
contrib/libdouble-conversion/double-conversion/bignum-dtoa.cc
Normal file
641
contrib/libdouble-conversion/double-conversion/bignum-dtoa.cc
Normal file
@ -0,0 +1,641 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "bignum-dtoa.h"
|
||||||
|
|
||||||
|
#include "bignum.h"
|
||||||
|
#include "ieee.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
static int NormalizedExponent(uint64_t significand, int exponent) {
|
||||||
|
ASSERT(significand != 0);
|
||||||
|
while ((significand & Double::kHiddenBit) == 0) {
|
||||||
|
significand = significand << 1;
|
||||||
|
exponent = exponent - 1;
|
||||||
|
}
|
||||||
|
return exponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Forward declarations:
|
||||||
|
// Returns an estimation of k such that 10^(k-1) <= v < 10^k.
|
||||||
|
static int EstimatePower(int exponent);
|
||||||
|
// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
|
||||||
|
// and denominator.
|
||||||
|
static void InitialScaledStartValues(uint64_t significand,
|
||||||
|
int exponent,
|
||||||
|
bool lower_boundary_is_closer,
|
||||||
|
int estimated_power,
|
||||||
|
bool need_boundary_deltas,
|
||||||
|
Bignum* numerator,
|
||||||
|
Bignum* denominator,
|
||||||
|
Bignum* delta_minus,
|
||||||
|
Bignum* delta_plus);
|
||||||
|
// Multiplies numerator/denominator so that its values lies in the range 1-10.
|
||||||
|
// Returns decimal_point s.t.
|
||||||
|
// v = numerator'/denominator' * 10^(decimal_point-1)
|
||||||
|
// where numerator' and denominator' are the values of numerator and
|
||||||
|
// denominator after the call to this function.
|
||||||
|
static void FixupMultiply10(int estimated_power, bool is_even,
|
||||||
|
int* decimal_point,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Bignum* delta_minus, Bignum* delta_plus);
|
||||||
|
// Generates digits from the left to the right and stops when the generated
|
||||||
|
// digits yield the shortest decimal representation of v.
|
||||||
|
static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
|
||||||
|
Bignum* delta_minus, Bignum* delta_plus,
|
||||||
|
bool is_even,
|
||||||
|
Vector<char> buffer, int* length);
|
||||||
|
// Generates 'requested_digits' after the decimal point.
|
||||||
|
static void BignumToFixed(int requested_digits, int* decimal_point,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Vector<char>(buffer), int* length);
|
||||||
|
// Generates 'count' digits of numerator/denominator.
|
||||||
|
// Once 'count' digits have been produced rounds the result depending on the
|
||||||
|
// remainder (remainders of exactly .5 round upwards). Might update the
|
||||||
|
// decimal_point when rounding up (for example for 0.9999).
|
||||||
|
static void GenerateCountedDigits(int count, int* decimal_point,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Vector<char>(buffer), int* length);
|
||||||
|
|
||||||
|
|
||||||
|
void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
|
||||||
|
Vector<char> buffer, int* length, int* decimal_point) {
|
||||||
|
ASSERT(v > 0);
|
||||||
|
ASSERT(!Double(v).IsSpecial());
|
||||||
|
uint64_t significand;
|
||||||
|
int exponent;
|
||||||
|
bool lower_boundary_is_closer;
|
||||||
|
if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) {
|
||||||
|
float f = static_cast<float>(v);
|
||||||
|
ASSERT(f == v);
|
||||||
|
significand = Single(f).Significand();
|
||||||
|
exponent = Single(f).Exponent();
|
||||||
|
lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser();
|
||||||
|
} else {
|
||||||
|
significand = Double(v).Significand();
|
||||||
|
exponent = Double(v).Exponent();
|
||||||
|
lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser();
|
||||||
|
}
|
||||||
|
bool need_boundary_deltas =
|
||||||
|
(mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE);
|
||||||
|
|
||||||
|
bool is_even = (significand & 1) == 0;
|
||||||
|
int normalized_exponent = NormalizedExponent(significand, exponent);
|
||||||
|
// estimated_power might be too low by 1.
|
||||||
|
int estimated_power = EstimatePower(normalized_exponent);
|
||||||
|
|
||||||
|
// Shortcut for Fixed.
|
||||||
|
// The requested digits correspond to the digits after the point. If the
|
||||||
|
// number is much too small, then there is no need in trying to get any
|
||||||
|
// digits.
|
||||||
|
if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) {
|
||||||
|
buffer[0] = '\0';
|
||||||
|
*length = 0;
|
||||||
|
// Set decimal-point to -requested_digits. This is what Gay does.
|
||||||
|
// Note that it should not have any effect anyways since the string is
|
||||||
|
// empty.
|
||||||
|
*decimal_point = -requested_digits;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bignum numerator;
|
||||||
|
Bignum denominator;
|
||||||
|
Bignum delta_minus;
|
||||||
|
Bignum delta_plus;
|
||||||
|
// Make sure the bignum can grow large enough. The smallest double equals
|
||||||
|
// 4e-324. In this case the denominator needs fewer than 324*4 binary digits.
|
||||||
|
// The maximum double is 1.7976931348623157e308 which needs fewer than
|
||||||
|
// 308*4 binary digits.
|
||||||
|
ASSERT(Bignum::kMaxSignificantBits >= 324*4);
|
||||||
|
InitialScaledStartValues(significand, exponent, lower_boundary_is_closer,
|
||||||
|
estimated_power, need_boundary_deltas,
|
||||||
|
&numerator, &denominator,
|
||||||
|
&delta_minus, &delta_plus);
|
||||||
|
// We now have v = (numerator / denominator) * 10^estimated_power.
|
||||||
|
FixupMultiply10(estimated_power, is_even, decimal_point,
|
||||||
|
&numerator, &denominator,
|
||||||
|
&delta_minus, &delta_plus);
|
||||||
|
// We now have v = (numerator / denominator) * 10^(decimal_point-1), and
|
||||||
|
// 1 <= (numerator + delta_plus) / denominator < 10
|
||||||
|
switch (mode) {
|
||||||
|
case BIGNUM_DTOA_SHORTEST:
|
||||||
|
case BIGNUM_DTOA_SHORTEST_SINGLE:
|
||||||
|
GenerateShortestDigits(&numerator, &denominator,
|
||||||
|
&delta_minus, &delta_plus,
|
||||||
|
is_even, buffer, length);
|
||||||
|
break;
|
||||||
|
case BIGNUM_DTOA_FIXED:
|
||||||
|
BignumToFixed(requested_digits, decimal_point,
|
||||||
|
&numerator, &denominator,
|
||||||
|
buffer, length);
|
||||||
|
break;
|
||||||
|
case BIGNUM_DTOA_PRECISION:
|
||||||
|
GenerateCountedDigits(requested_digits, decimal_point,
|
||||||
|
&numerator, &denominator,
|
||||||
|
buffer, length);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
buffer[*length] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The procedure starts generating digits from the left to the right and stops
|
||||||
|
// when the generated digits yield the shortest decimal representation of v. A
|
||||||
|
// decimal representation of v is a number lying closer to v than to any other
|
||||||
|
// double, so it converts to v when read.
|
||||||
|
//
|
||||||
|
// This is true if d, the decimal representation, is between m- and m+, the
|
||||||
|
// upper and lower boundaries. d must be strictly between them if !is_even.
|
||||||
|
// m- := (numerator - delta_minus) / denominator
|
||||||
|
// m+ := (numerator + delta_plus) / denominator
|
||||||
|
//
|
||||||
|
// Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
|
||||||
|
// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
|
||||||
|
// will be produced. This should be the standard precondition.
|
||||||
|
static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
|
||||||
|
Bignum* delta_minus, Bignum* delta_plus,
|
||||||
|
bool is_even,
|
||||||
|
Vector<char> buffer, int* length) {
|
||||||
|
// Small optimization: if delta_minus and delta_plus are the same just reuse
|
||||||
|
// one of the two bignums.
|
||||||
|
if (Bignum::Equal(*delta_minus, *delta_plus)) {
|
||||||
|
delta_plus = delta_minus;
|
||||||
|
}
|
||||||
|
*length = 0;
|
||||||
|
for (;;) {
|
||||||
|
uint16_t digit;
|
||||||
|
digit = numerator->DivideModuloIntBignum(*denominator);
|
||||||
|
ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive.
|
||||||
|
// digit = numerator / denominator (integer division).
|
||||||
|
// numerator = numerator % denominator.
|
||||||
|
buffer[(*length)++] = static_cast<char>(digit + '0');
|
||||||
|
|
||||||
|
// Can we stop already?
|
||||||
|
// If the remainder of the division is less than the distance to the lower
|
||||||
|
// boundary we can stop. In this case we simply round down (discarding the
|
||||||
|
// remainder).
|
||||||
|
// Similarly we test if we can round up (using the upper boundary).
|
||||||
|
bool in_delta_room_minus;
|
||||||
|
bool in_delta_room_plus;
|
||||||
|
if (is_even) {
|
||||||
|
in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus);
|
||||||
|
} else {
|
||||||
|
in_delta_room_minus = Bignum::Less(*numerator, *delta_minus);
|
||||||
|
}
|
||||||
|
if (is_even) {
|
||||||
|
in_delta_room_plus =
|
||||||
|
Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
|
||||||
|
} else {
|
||||||
|
in_delta_room_plus =
|
||||||
|
Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
|
||||||
|
}
|
||||||
|
if (!in_delta_room_minus && !in_delta_room_plus) {
|
||||||
|
// Prepare for next iteration.
|
||||||
|
numerator->Times10();
|
||||||
|
delta_minus->Times10();
|
||||||
|
// We optimized delta_plus to be equal to delta_minus (if they share the
|
||||||
|
// same value). So don't multiply delta_plus if they point to the same
|
||||||
|
// object.
|
||||||
|
if (delta_minus != delta_plus) {
|
||||||
|
delta_plus->Times10();
|
||||||
|
}
|
||||||
|
} else if (in_delta_room_minus && in_delta_room_plus) {
|
||||||
|
// Let's see if 2*numerator < denominator.
|
||||||
|
// If yes, then the next digit would be < 5 and we can round down.
|
||||||
|
int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator);
|
||||||
|
if (compare < 0) {
|
||||||
|
// Remaining digits are less than .5. -> Round down (== do nothing).
|
||||||
|
} else if (compare > 0) {
|
||||||
|
// Remaining digits are more than .5 of denominator. -> Round up.
|
||||||
|
// Note that the last digit could not be a '9' as otherwise the whole
|
||||||
|
// loop would have stopped earlier.
|
||||||
|
// We still have an assert here in case the preconditions were not
|
||||||
|
// satisfied.
|
||||||
|
ASSERT(buffer[(*length) - 1] != '9');
|
||||||
|
buffer[(*length) - 1]++;
|
||||||
|
} else {
|
||||||
|
// Halfway case.
|
||||||
|
// TODO(floitsch): need a way to solve half-way cases.
|
||||||
|
// For now let's round towards even (since this is what Gay seems to
|
||||||
|
// do).
|
||||||
|
|
||||||
|
if ((buffer[(*length) - 1] - '0') % 2 == 0) {
|
||||||
|
// Round down => Do nothing.
|
||||||
|
} else {
|
||||||
|
ASSERT(buffer[(*length) - 1] != '9');
|
||||||
|
buffer[(*length) - 1]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (in_delta_room_minus) {
|
||||||
|
// Round down (== do nothing).
|
||||||
|
return;
|
||||||
|
} else { // in_delta_room_plus
|
||||||
|
// Round up.
|
||||||
|
// Note again that the last digit could not be '9' since this would have
|
||||||
|
// stopped the loop earlier.
|
||||||
|
// We still have an ASSERT here, in case the preconditions were not
|
||||||
|
// satisfied.
|
||||||
|
ASSERT(buffer[(*length) -1] != '9');
|
||||||
|
buffer[(*length) - 1]++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Let v = numerator / denominator < 10.
|
||||||
|
// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
|
||||||
|
// from left to right. Once 'count' digits have been produced we decide wether
|
||||||
|
// to round up or down. Remainders of exactly .5 round upwards. Numbers such
|
||||||
|
// as 9.999999 propagate a carry all the way, and change the
|
||||||
|
// exponent (decimal_point), when rounding upwards.
|
||||||
|
static void GenerateCountedDigits(int count, int* decimal_point,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Vector<char> buffer, int* length) {
|
||||||
|
ASSERT(count >= 0);
|
||||||
|
for (int i = 0; i < count - 1; ++i) {
|
||||||
|
uint16_t digit;
|
||||||
|
digit = numerator->DivideModuloIntBignum(*denominator);
|
||||||
|
ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive.
|
||||||
|
// digit = numerator / denominator (integer division).
|
||||||
|
// numerator = numerator % denominator.
|
||||||
|
buffer[i] = static_cast<char>(digit + '0');
|
||||||
|
// Prepare for next iteration.
|
||||||
|
numerator->Times10();
|
||||||
|
}
|
||||||
|
// Generate the last digit.
|
||||||
|
uint16_t digit;
|
||||||
|
digit = numerator->DivideModuloIntBignum(*denominator);
|
||||||
|
if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
|
||||||
|
digit++;
|
||||||
|
}
|
||||||
|
ASSERT(digit <= 10);
|
||||||
|
buffer[count - 1] = static_cast<char>(digit + '0');
|
||||||
|
// Correct bad digits (in case we had a sequence of '9's). Propagate the
|
||||||
|
// carry until we hat a non-'9' or til we reach the first digit.
|
||||||
|
for (int i = count - 1; i > 0; --i) {
|
||||||
|
if (buffer[i] != '0' + 10) break;
|
||||||
|
buffer[i] = '0';
|
||||||
|
buffer[i - 1]++;
|
||||||
|
}
|
||||||
|
if (buffer[0] == '0' + 10) {
|
||||||
|
// Propagate a carry past the top place.
|
||||||
|
buffer[0] = '1';
|
||||||
|
(*decimal_point)++;
|
||||||
|
}
|
||||||
|
*length = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Generates 'requested_digits' after the decimal point. It might omit
|
||||||
|
// trailing '0's. If the input number is too small then no digits at all are
|
||||||
|
// generated (ex.: 2 fixed digits for 0.00001).
|
||||||
|
//
|
||||||
|
// Input verifies: 1 <= (numerator + delta) / denominator < 10.
|
||||||
|
static void BignumToFixed(int requested_digits, int* decimal_point,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Vector<char>(buffer), int* length) {
|
||||||
|
// Note that we have to look at more than just the requested_digits, since
|
||||||
|
// a number could be rounded up. Example: v=0.5 with requested_digits=0.
|
||||||
|
// Even though the power of v equals 0 we can't just stop here.
|
||||||
|
if (-(*decimal_point) > requested_digits) {
|
||||||
|
// The number is definitively too small.
|
||||||
|
// Ex: 0.001 with requested_digits == 1.
|
||||||
|
// Set decimal-point to -requested_digits. This is what Gay does.
|
||||||
|
// Note that it should not have any effect anyways since the string is
|
||||||
|
// empty.
|
||||||
|
*decimal_point = -requested_digits;
|
||||||
|
*length = 0;
|
||||||
|
return;
|
||||||
|
} else if (-(*decimal_point) == requested_digits) {
|
||||||
|
// We only need to verify if the number rounds down or up.
|
||||||
|
// Ex: 0.04 and 0.06 with requested_digits == 1.
|
||||||
|
ASSERT(*decimal_point == -requested_digits);
|
||||||
|
// Initially the fraction lies in range (1, 10]. Multiply the denominator
|
||||||
|
// by 10 so that we can compare more easily.
|
||||||
|
denominator->Times10();
|
||||||
|
if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
|
||||||
|
// If the fraction is >= 0.5 then we have to include the rounded
|
||||||
|
// digit.
|
||||||
|
buffer[0] = '1';
|
||||||
|
*length = 1;
|
||||||
|
(*decimal_point)++;
|
||||||
|
} else {
|
||||||
|
// Note that we caught most of similar cases earlier.
|
||||||
|
*length = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// The requested digits correspond to the digits after the point.
|
||||||
|
// The variable 'needed_digits' includes the digits before the point.
|
||||||
|
int needed_digits = (*decimal_point) + requested_digits;
|
||||||
|
GenerateCountedDigits(needed_digits, decimal_point,
|
||||||
|
numerator, denominator,
|
||||||
|
buffer, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns an estimation of k such that 10^(k-1) <= v < 10^k where
|
||||||
|
// v = f * 2^exponent and 2^52 <= f < 2^53.
|
||||||
|
// v is hence a normalized double with the given exponent. The output is an
|
||||||
|
// approximation for the exponent of the decimal approimation .digits * 10^k.
|
||||||
|
//
|
||||||
|
// The result might undershoot by 1 in which case 10^k <= v < 10^k+1.
|
||||||
|
// Note: this property holds for v's upper boundary m+ too.
|
||||||
|
// 10^k <= m+ < 10^k+1.
|
||||||
|
// (see explanation below).
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// EstimatePower(0) => 16
|
||||||
|
// EstimatePower(-52) => 0
|
||||||
|
//
|
||||||
|
// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0.
|
||||||
|
static int EstimatePower(int exponent) {
|
||||||
|
// This function estimates log10 of v where v = f*2^e (with e == exponent).
|
||||||
|
// Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)).
|
||||||
|
// Note that f is bounded by its container size. Let p = 53 (the double's
|
||||||
|
// significand size). Then 2^(p-1) <= f < 2^p.
|
||||||
|
//
|
||||||
|
// Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close
|
||||||
|
// to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)).
|
||||||
|
// The computed number undershoots by less than 0.631 (when we compute log3
|
||||||
|
// and not log10).
|
||||||
|
//
|
||||||
|
// Optimization: since we only need an approximated result this computation
|
||||||
|
// can be performed on 64 bit integers. On x86/x64 architecture the speedup is
|
||||||
|
// not really measurable, though.
|
||||||
|
//
|
||||||
|
// Since we want to avoid overshooting we decrement by 1e10 so that
|
||||||
|
// floating-point imprecisions don't affect us.
|
||||||
|
//
|
||||||
|
// Explanation for v's boundary m+: the computation takes advantage of
|
||||||
|
// the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement
|
||||||
|
// (even for denormals where the delta can be much more important).
|
||||||
|
|
||||||
|
const double k1Log10 = 0.30102999566398114; // 1/lg(10)
|
||||||
|
|
||||||
|
// For doubles len(f) == 53 (don't forget the hidden bit).
|
||||||
|
const int kSignificandSize = Double::kSignificandSize;
|
||||||
|
double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
|
||||||
|
return static_cast<int>(estimate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// See comments for InitialScaledStartValues.
|
||||||
|
static void InitialScaledStartValuesPositiveExponent(
|
||||||
|
uint64_t significand, int exponent,
|
||||||
|
int estimated_power, bool need_boundary_deltas,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Bignum* delta_minus, Bignum* delta_plus) {
|
||||||
|
// A positive exponent implies a positive power.
|
||||||
|
ASSERT(estimated_power >= 0);
|
||||||
|
// Since the estimated_power is positive we simply multiply the denominator
|
||||||
|
// by 10^estimated_power.
|
||||||
|
|
||||||
|
// numerator = v.
|
||||||
|
numerator->AssignUInt64(significand);
|
||||||
|
numerator->ShiftLeft(exponent);
|
||||||
|
// denominator = 10^estimated_power.
|
||||||
|
denominator->AssignPowerUInt16(10, estimated_power);
|
||||||
|
|
||||||
|
if (need_boundary_deltas) {
|
||||||
|
// Introduce a common denominator so that the deltas to the boundaries are
|
||||||
|
// integers.
|
||||||
|
denominator->ShiftLeft(1);
|
||||||
|
numerator->ShiftLeft(1);
|
||||||
|
// Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
|
||||||
|
// denominator (of 2) delta_plus equals 2^e.
|
||||||
|
delta_plus->AssignUInt16(1);
|
||||||
|
delta_plus->ShiftLeft(exponent);
|
||||||
|
// Same for delta_minus. The adjustments if f == 2^p-1 are done later.
|
||||||
|
delta_minus->AssignUInt16(1);
|
||||||
|
delta_minus->ShiftLeft(exponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// See comments for InitialScaledStartValues
|
||||||
|
static void InitialScaledStartValuesNegativeExponentPositivePower(
|
||||||
|
uint64_t significand, int exponent,
|
||||||
|
int estimated_power, bool need_boundary_deltas,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Bignum* delta_minus, Bignum* delta_plus) {
|
||||||
|
// v = f * 2^e with e < 0, and with estimated_power >= 0.
|
||||||
|
// This means that e is close to 0 (have a look at how estimated_power is
|
||||||
|
// computed).
|
||||||
|
|
||||||
|
// numerator = significand
|
||||||
|
// since v = significand * 2^exponent this is equivalent to
|
||||||
|
// numerator = v * / 2^-exponent
|
||||||
|
numerator->AssignUInt64(significand);
|
||||||
|
// denominator = 10^estimated_power * 2^-exponent (with exponent < 0)
|
||||||
|
denominator->AssignPowerUInt16(10, estimated_power);
|
||||||
|
denominator->ShiftLeft(-exponent);
|
||||||
|
|
||||||
|
if (need_boundary_deltas) {
|
||||||
|
// Introduce a common denominator so that the deltas to the boundaries are
|
||||||
|
// integers.
|
||||||
|
denominator->ShiftLeft(1);
|
||||||
|
numerator->ShiftLeft(1);
|
||||||
|
// Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
|
||||||
|
// denominator (of 2) delta_plus equals 2^e.
|
||||||
|
// Given that the denominator already includes v's exponent the distance
|
||||||
|
// to the boundaries is simply 1.
|
||||||
|
delta_plus->AssignUInt16(1);
|
||||||
|
// Same for delta_minus. The adjustments if f == 2^p-1 are done later.
|
||||||
|
delta_minus->AssignUInt16(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// See comments for InitialScaledStartValues
|
||||||
|
static void InitialScaledStartValuesNegativeExponentNegativePower(
|
||||||
|
uint64_t significand, int exponent,
|
||||||
|
int estimated_power, bool need_boundary_deltas,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Bignum* delta_minus, Bignum* delta_plus) {
|
||||||
|
// Instead of multiplying the denominator with 10^estimated_power we
|
||||||
|
// multiply all values (numerator and deltas) by 10^-estimated_power.
|
||||||
|
|
||||||
|
// Use numerator as temporary container for power_ten.
|
||||||
|
Bignum* power_ten = numerator;
|
||||||
|
power_ten->AssignPowerUInt16(10, -estimated_power);
|
||||||
|
|
||||||
|
if (need_boundary_deltas) {
|
||||||
|
// Since power_ten == numerator we must make a copy of 10^estimated_power
|
||||||
|
// before we complete the computation of the numerator.
|
||||||
|
// delta_plus = delta_minus = 10^estimated_power
|
||||||
|
delta_plus->AssignBignum(*power_ten);
|
||||||
|
delta_minus->AssignBignum(*power_ten);
|
||||||
|
}
|
||||||
|
|
||||||
|
// numerator = significand * 2 * 10^-estimated_power
|
||||||
|
// since v = significand * 2^exponent this is equivalent to
|
||||||
|
// numerator = v * 10^-estimated_power * 2 * 2^-exponent.
|
||||||
|
// Remember: numerator has been abused as power_ten. So no need to assign it
|
||||||
|
// to itself.
|
||||||
|
ASSERT(numerator == power_ten);
|
||||||
|
numerator->MultiplyByUInt64(significand);
|
||||||
|
|
||||||
|
// denominator = 2 * 2^-exponent with exponent < 0.
|
||||||
|
denominator->AssignUInt16(1);
|
||||||
|
denominator->ShiftLeft(-exponent);
|
||||||
|
|
||||||
|
if (need_boundary_deltas) {
|
||||||
|
// Introduce a common denominator so that the deltas to the boundaries are
|
||||||
|
// integers.
|
||||||
|
numerator->ShiftLeft(1);
|
||||||
|
denominator->ShiftLeft(1);
|
||||||
|
// With this shift the boundaries have their correct value, since
|
||||||
|
// delta_plus = 10^-estimated_power, and
|
||||||
|
// delta_minus = 10^-estimated_power.
|
||||||
|
// These assignments have been done earlier.
|
||||||
|
// The adjustments if f == 2^p-1 (lower boundary is closer) are done later.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Let v = significand * 2^exponent.
|
||||||
|
// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
|
||||||
|
// and denominator. The functions GenerateShortestDigits and
|
||||||
|
// GenerateCountedDigits will then convert this ratio to its decimal
|
||||||
|
// representation d, with the required accuracy.
|
||||||
|
// Then d * 10^estimated_power is the representation of v.
|
||||||
|
// (Note: the fraction and the estimated_power might get adjusted before
|
||||||
|
// generating the decimal representation.)
|
||||||
|
//
|
||||||
|
// The initial start values consist of:
|
||||||
|
// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power.
|
||||||
|
// - a scaled (common) denominator.
|
||||||
|
// optionally (used by GenerateShortestDigits to decide if it has the shortest
|
||||||
|
// decimal converting back to v):
|
||||||
|
// - v - m-: the distance to the lower boundary.
|
||||||
|
// - m+ - v: the distance to the upper boundary.
|
||||||
|
//
|
||||||
|
// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator.
|
||||||
|
//
|
||||||
|
// Let ep == estimated_power, then the returned values will satisfy:
|
||||||
|
// v / 10^ep = numerator / denominator.
|
||||||
|
// v's boundarys m- and m+:
|
||||||
|
// m- / 10^ep == v / 10^ep - delta_minus / denominator
|
||||||
|
// m+ / 10^ep == v / 10^ep + delta_plus / denominator
|
||||||
|
// Or in other words:
|
||||||
|
// m- == v - delta_minus * 10^ep / denominator;
|
||||||
|
// m+ == v + delta_plus * 10^ep / denominator;
|
||||||
|
//
|
||||||
|
// Since 10^(k-1) <= v < 10^k (with k == estimated_power)
|
||||||
|
// or 10^k <= v < 10^(k+1)
|
||||||
|
// we then have 0.1 <= numerator/denominator < 1
|
||||||
|
// or 1 <= numerator/denominator < 10
|
||||||
|
//
|
||||||
|
// It is then easy to kickstart the digit-generation routine.
|
||||||
|
//
|
||||||
|
// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST
|
||||||
|
// or BIGNUM_DTOA_SHORTEST_SINGLE.
|
||||||
|
|
||||||
|
static void InitialScaledStartValues(uint64_t significand,
|
||||||
|
int exponent,
|
||||||
|
bool lower_boundary_is_closer,
|
||||||
|
int estimated_power,
|
||||||
|
bool need_boundary_deltas,
|
||||||
|
Bignum* numerator,
|
||||||
|
Bignum* denominator,
|
||||||
|
Bignum* delta_minus,
|
||||||
|
Bignum* delta_plus) {
|
||||||
|
if (exponent >= 0) {
|
||||||
|
InitialScaledStartValuesPositiveExponent(
|
||||||
|
significand, exponent, estimated_power, need_boundary_deltas,
|
||||||
|
numerator, denominator, delta_minus, delta_plus);
|
||||||
|
} else if (estimated_power >= 0) {
|
||||||
|
InitialScaledStartValuesNegativeExponentPositivePower(
|
||||||
|
significand, exponent, estimated_power, need_boundary_deltas,
|
||||||
|
numerator, denominator, delta_minus, delta_plus);
|
||||||
|
} else {
|
||||||
|
InitialScaledStartValuesNegativeExponentNegativePower(
|
||||||
|
significand, exponent, estimated_power, need_boundary_deltas,
|
||||||
|
numerator, denominator, delta_minus, delta_plus);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_boundary_deltas && lower_boundary_is_closer) {
|
||||||
|
// The lower boundary is closer at half the distance of "normal" numbers.
|
||||||
|
// Increase the common denominator and adapt all but the delta_minus.
|
||||||
|
denominator->ShiftLeft(1); // *2
|
||||||
|
numerator->ShiftLeft(1); // *2
|
||||||
|
delta_plus->ShiftLeft(1); // *2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This routine multiplies numerator/denominator so that its values lies in the
|
||||||
|
// range 1-10. That is after a call to this function we have:
|
||||||
|
// 1 <= (numerator + delta_plus) /denominator < 10.
|
||||||
|
// Let numerator the input before modification and numerator' the argument
|
||||||
|
// after modification, then the output-parameter decimal_point is such that
|
||||||
|
// numerator / denominator * 10^estimated_power ==
|
||||||
|
// numerator' / denominator' * 10^(decimal_point - 1)
|
||||||
|
// In some cases estimated_power was too low, and this is already the case. We
|
||||||
|
// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
|
||||||
|
// estimated_power) but do not touch the numerator or denominator.
|
||||||
|
// Otherwise the routine multiplies the numerator and the deltas by 10.
|
||||||
|
static void FixupMultiply10(int estimated_power, bool is_even,
|
||||||
|
int* decimal_point,
|
||||||
|
Bignum* numerator, Bignum* denominator,
|
||||||
|
Bignum* delta_minus, Bignum* delta_plus) {
|
||||||
|
bool in_range;
|
||||||
|
if (is_even) {
|
||||||
|
// For IEEE doubles half-way cases (in decimal system numbers ending with 5)
|
||||||
|
// are rounded to the closest floating-point number with even significand.
|
||||||
|
in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
|
||||||
|
} else {
|
||||||
|
in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
|
||||||
|
}
|
||||||
|
if (in_range) {
|
||||||
|
// Since numerator + delta_plus >= denominator we already have
|
||||||
|
// 1 <= numerator/denominator < 10. Simply update the estimated_power.
|
||||||
|
*decimal_point = estimated_power + 1;
|
||||||
|
} else {
|
||||||
|
*decimal_point = estimated_power;
|
||||||
|
numerator->Times10();
|
||||||
|
if (Bignum::Equal(*delta_minus, *delta_plus)) {
|
||||||
|
delta_minus->Times10();
|
||||||
|
delta_plus->AssignBignum(*delta_minus);
|
||||||
|
} else {
|
||||||
|
delta_minus->Times10();
|
||||||
|
delta_plus->Times10();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
84
contrib/libdouble-conversion/double-conversion/bignum-dtoa.h
Normal file
84
contrib/libdouble-conversion/double-conversion/bignum-dtoa.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_
|
||||||
|
#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
enum BignumDtoaMode {
|
||||||
|
// Return the shortest correct representation.
|
||||||
|
// For example the output of 0.299999999999999988897 is (the less accurate but
|
||||||
|
// correct) 0.3.
|
||||||
|
BIGNUM_DTOA_SHORTEST,
|
||||||
|
// Same as BIGNUM_DTOA_SHORTEST but for single-precision floats.
|
||||||
|
BIGNUM_DTOA_SHORTEST_SINGLE,
|
||||||
|
// Return a fixed number of digits after the decimal point.
|
||||||
|
// For instance fixed(0.1, 4) becomes 0.1000
|
||||||
|
// If the input number is big, the output will be big.
|
||||||
|
BIGNUM_DTOA_FIXED,
|
||||||
|
// Return a fixed number of digits, no matter what the exponent is.
|
||||||
|
BIGNUM_DTOA_PRECISION
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts the given double 'v' to ascii.
|
||||||
|
// The result should be interpreted as buffer * 10^(point-length).
|
||||||
|
// The buffer will be null-terminated.
|
||||||
|
//
|
||||||
|
// The input v must be > 0 and different from NaN, and Infinity.
|
||||||
|
//
|
||||||
|
// The output depends on the given mode:
|
||||||
|
// - SHORTEST: produce the least amount of digits for which the internal
|
||||||
|
// identity requirement is still satisfied. If the digits are printed
|
||||||
|
// (together with the correct exponent) then reading this number will give
|
||||||
|
// 'v' again. The buffer will choose the representation that is closest to
|
||||||
|
// 'v'. If there are two at the same distance, than the number is round up.
|
||||||
|
// In this mode the 'requested_digits' parameter is ignored.
|
||||||
|
// - FIXED: produces digits necessary to print a given number with
|
||||||
|
// 'requested_digits' digits after the decimal point. The produced digits
|
||||||
|
// might be too short in which case the caller has to fill the gaps with '0's.
|
||||||
|
// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
|
||||||
|
// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns
|
||||||
|
// buffer="2", point=0.
|
||||||
|
// Note: the length of the returned buffer has no meaning wrt the significance
|
||||||
|
// of its digits. That is, just because it contains '0's does not mean that
|
||||||
|
// any other digit would not satisfy the internal identity requirement.
|
||||||
|
// - PRECISION: produces 'requested_digits' where the first digit is not '0'.
|
||||||
|
// Even though the length of produced digits usually equals
|
||||||
|
// 'requested_digits', the function is allowed to return fewer digits, in
|
||||||
|
// which case the caller has to fill the missing digits with '0's.
|
||||||
|
// Halfway cases are again rounded up.
|
||||||
|
// 'BignumDtoa' expects the given buffer to be big enough to hold all digits
|
||||||
|
// and a terminating null-character.
|
||||||
|
void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
|
||||||
|
Vector<char> buffer, int* length, int* point);
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_
|
766
contrib/libdouble-conversion/double-conversion/bignum.cc
Normal file
766
contrib/libdouble-conversion/double-conversion/bignum.cc
Normal file
@ -0,0 +1,766 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "bignum.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
Bignum::Bignum()
|
||||||
|
: bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) {
|
||||||
|
for (int i = 0; i < kBigitCapacity; ++i) {
|
||||||
|
bigits_[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
static int BitSize(S value) {
|
||||||
|
(void) value; // Mark variable as used.
|
||||||
|
return 8 * sizeof(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guaranteed to lie in one Bigit.
|
||||||
|
void Bignum::AssignUInt16(uint16_t value) {
|
||||||
|
ASSERT(kBigitSize >= BitSize(value));
|
||||||
|
Zero();
|
||||||
|
if (value == 0) return;
|
||||||
|
|
||||||
|
EnsureCapacity(1);
|
||||||
|
bigits_[0] = value;
|
||||||
|
used_digits_ = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::AssignUInt64(uint64_t value) {
|
||||||
|
const int kUInt64Size = 64;
|
||||||
|
|
||||||
|
Zero();
|
||||||
|
if (value == 0) return;
|
||||||
|
|
||||||
|
int needed_bigits = kUInt64Size / kBigitSize + 1;
|
||||||
|
EnsureCapacity(needed_bigits);
|
||||||
|
for (int i = 0; i < needed_bigits; ++i) {
|
||||||
|
bigits_[i] = value & kBigitMask;
|
||||||
|
value = value >> kBigitSize;
|
||||||
|
}
|
||||||
|
used_digits_ = needed_bigits;
|
||||||
|
Clamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::AssignBignum(const Bignum& other) {
|
||||||
|
exponent_ = other.exponent_;
|
||||||
|
for (int i = 0; i < other.used_digits_; ++i) {
|
||||||
|
bigits_[i] = other.bigits_[i];
|
||||||
|
}
|
||||||
|
// Clear the excess digits (if there were any).
|
||||||
|
for (int i = other.used_digits_; i < used_digits_; ++i) {
|
||||||
|
bigits_[i] = 0;
|
||||||
|
}
|
||||||
|
used_digits_ = other.used_digits_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint64_t ReadUInt64(Vector<const char> buffer,
|
||||||
|
int from,
|
||||||
|
int digits_to_read) {
|
||||||
|
uint64_t result = 0;
|
||||||
|
for (int i = from; i < from + digits_to_read; ++i) {
|
||||||
|
int digit = buffer[i] - '0';
|
||||||
|
ASSERT(0 <= digit && digit <= 9);
|
||||||
|
result = result * 10 + digit;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::AssignDecimalString(Vector<const char> value) {
|
||||||
|
// 2^64 = 18446744073709551616 > 10^19
|
||||||
|
const int kMaxUint64DecimalDigits = 19;
|
||||||
|
Zero();
|
||||||
|
int length = value.length();
|
||||||
|
unsigned int pos = 0;
|
||||||
|
// Let's just say that each digit needs 4 bits.
|
||||||
|
while (length >= kMaxUint64DecimalDigits) {
|
||||||
|
uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits);
|
||||||
|
pos += kMaxUint64DecimalDigits;
|
||||||
|
length -= kMaxUint64DecimalDigits;
|
||||||
|
MultiplyByPowerOfTen(kMaxUint64DecimalDigits);
|
||||||
|
AddUInt64(digits);
|
||||||
|
}
|
||||||
|
uint64_t digits = ReadUInt64(value, pos, length);
|
||||||
|
MultiplyByPowerOfTen(length);
|
||||||
|
AddUInt64(digits);
|
||||||
|
Clamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int HexCharValue(char c) {
|
||||||
|
if ('0' <= c && c <= '9') return c - '0';
|
||||||
|
if ('a' <= c && c <= 'f') return 10 + c - 'a';
|
||||||
|
ASSERT('A' <= c && c <= 'F');
|
||||||
|
return 10 + c - 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::AssignHexString(Vector<const char> value) {
|
||||||
|
Zero();
|
||||||
|
int length = value.length();
|
||||||
|
|
||||||
|
int needed_bigits = length * 4 / kBigitSize + 1;
|
||||||
|
EnsureCapacity(needed_bigits);
|
||||||
|
int string_index = length - 1;
|
||||||
|
for (int i = 0; i < needed_bigits - 1; ++i) {
|
||||||
|
// These bigits are guaranteed to be "full".
|
||||||
|
Chunk current_bigit = 0;
|
||||||
|
for (int j = 0; j < kBigitSize / 4; j++) {
|
||||||
|
current_bigit += HexCharValue(value[string_index--]) << (j * 4);
|
||||||
|
}
|
||||||
|
bigits_[i] = current_bigit;
|
||||||
|
}
|
||||||
|
used_digits_ = needed_bigits - 1;
|
||||||
|
|
||||||
|
Chunk most_significant_bigit = 0; // Could be = 0;
|
||||||
|
for (int j = 0; j <= string_index; ++j) {
|
||||||
|
most_significant_bigit <<= 4;
|
||||||
|
most_significant_bigit += HexCharValue(value[j]);
|
||||||
|
}
|
||||||
|
if (most_significant_bigit != 0) {
|
||||||
|
bigits_[used_digits_] = most_significant_bigit;
|
||||||
|
used_digits_++;
|
||||||
|
}
|
||||||
|
Clamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::AddUInt64(uint64_t operand) {
|
||||||
|
if (operand == 0) return;
|
||||||
|
Bignum other;
|
||||||
|
other.AssignUInt64(operand);
|
||||||
|
AddBignum(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::AddBignum(const Bignum& other) {
|
||||||
|
ASSERT(IsClamped());
|
||||||
|
ASSERT(other.IsClamped());
|
||||||
|
|
||||||
|
// If this has a greater exponent than other append zero-bigits to this.
|
||||||
|
// After this call exponent_ <= other.exponent_.
|
||||||
|
Align(other);
|
||||||
|
|
||||||
|
// There are two possibilities:
|
||||||
|
// aaaaaaaaaaa 0000 (where the 0s represent a's exponent)
|
||||||
|
// bbbbb 00000000
|
||||||
|
// ----------------
|
||||||
|
// ccccccccccc 0000
|
||||||
|
// or
|
||||||
|
// aaaaaaaaaa 0000
|
||||||
|
// bbbbbbbbb 0000000
|
||||||
|
// -----------------
|
||||||
|
// cccccccccccc 0000
|
||||||
|
// In both cases we might need a carry bigit.
|
||||||
|
|
||||||
|
EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_);
|
||||||
|
Chunk carry = 0;
|
||||||
|
int bigit_pos = other.exponent_ - exponent_;
|
||||||
|
ASSERT(bigit_pos >= 0);
|
||||||
|
for (int i = 0; i < other.used_digits_; ++i) {
|
||||||
|
Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry;
|
||||||
|
bigits_[bigit_pos] = sum & kBigitMask;
|
||||||
|
carry = sum >> kBigitSize;
|
||||||
|
bigit_pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (carry != 0) {
|
||||||
|
Chunk sum = bigits_[bigit_pos] + carry;
|
||||||
|
bigits_[bigit_pos] = sum & kBigitMask;
|
||||||
|
carry = sum >> kBigitSize;
|
||||||
|
bigit_pos++;
|
||||||
|
}
|
||||||
|
used_digits_ = Max(bigit_pos, used_digits_);
|
||||||
|
ASSERT(IsClamped());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::SubtractBignum(const Bignum& other) {
|
||||||
|
ASSERT(IsClamped());
|
||||||
|
ASSERT(other.IsClamped());
|
||||||
|
// We require this to be bigger than other.
|
||||||
|
ASSERT(LessEqual(other, *this));
|
||||||
|
|
||||||
|
Align(other);
|
||||||
|
|
||||||
|
int offset = other.exponent_ - exponent_;
|
||||||
|
Chunk borrow = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < other.used_digits_; ++i) {
|
||||||
|
ASSERT((borrow == 0) || (borrow == 1));
|
||||||
|
Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow;
|
||||||
|
bigits_[i + offset] = difference & kBigitMask;
|
||||||
|
borrow = difference >> (kChunkSize - 1);
|
||||||
|
}
|
||||||
|
while (borrow != 0) {
|
||||||
|
Chunk difference = bigits_[i + offset] - borrow;
|
||||||
|
bigits_[i + offset] = difference & kBigitMask;
|
||||||
|
borrow = difference >> (kChunkSize - 1);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
Clamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::ShiftLeft(int shift_amount) {
|
||||||
|
if (used_digits_ == 0) return;
|
||||||
|
exponent_ += shift_amount / kBigitSize;
|
||||||
|
int local_shift = shift_amount % kBigitSize;
|
||||||
|
EnsureCapacity(used_digits_ + 1);
|
||||||
|
BigitsShiftLeft(local_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::MultiplyByUInt32(uint32_t factor) {
|
||||||
|
if (factor == 1) return;
|
||||||
|
if (factor == 0) {
|
||||||
|
Zero();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (used_digits_ == 0) return;
|
||||||
|
|
||||||
|
// The product of a bigit with the factor is of size kBigitSize + 32.
|
||||||
|
// Assert that this number + 1 (for the carry) fits into double chunk.
|
||||||
|
ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1);
|
||||||
|
DoubleChunk carry = 0;
|
||||||
|
for (int i = 0; i < used_digits_; ++i) {
|
||||||
|
DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry;
|
||||||
|
bigits_[i] = static_cast<Chunk>(product & kBigitMask);
|
||||||
|
carry = (product >> kBigitSize);
|
||||||
|
}
|
||||||
|
while (carry != 0) {
|
||||||
|
EnsureCapacity(used_digits_ + 1);
|
||||||
|
bigits_[used_digits_] = carry & kBigitMask;
|
||||||
|
used_digits_++;
|
||||||
|
carry >>= kBigitSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::MultiplyByUInt64(uint64_t factor) {
|
||||||
|
if (factor == 1) return;
|
||||||
|
if (factor == 0) {
|
||||||
|
Zero();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT(kBigitSize < 32);
|
||||||
|
uint64_t carry = 0;
|
||||||
|
uint64_t low = factor & 0xFFFFFFFF;
|
||||||
|
uint64_t high = factor >> 32;
|
||||||
|
for (int i = 0; i < used_digits_; ++i) {
|
||||||
|
uint64_t product_low = low * bigits_[i];
|
||||||
|
uint64_t product_high = high * bigits_[i];
|
||||||
|
uint64_t tmp = (carry & kBigitMask) + product_low;
|
||||||
|
bigits_[i] = tmp & kBigitMask;
|
||||||
|
carry = (carry >> kBigitSize) + (tmp >> kBigitSize) +
|
||||||
|
(product_high << (32 - kBigitSize));
|
||||||
|
}
|
||||||
|
while (carry != 0) {
|
||||||
|
EnsureCapacity(used_digits_ + 1);
|
||||||
|
bigits_[used_digits_] = carry & kBigitMask;
|
||||||
|
used_digits_++;
|
||||||
|
carry >>= kBigitSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::MultiplyByPowerOfTen(int exponent) {
|
||||||
|
const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d);
|
||||||
|
const uint16_t kFive1 = 5;
|
||||||
|
const uint16_t kFive2 = kFive1 * 5;
|
||||||
|
const uint16_t kFive3 = kFive2 * 5;
|
||||||
|
const uint16_t kFive4 = kFive3 * 5;
|
||||||
|
const uint16_t kFive5 = kFive4 * 5;
|
||||||
|
const uint16_t kFive6 = kFive5 * 5;
|
||||||
|
const uint32_t kFive7 = kFive6 * 5;
|
||||||
|
const uint32_t kFive8 = kFive7 * 5;
|
||||||
|
const uint32_t kFive9 = kFive8 * 5;
|
||||||
|
const uint32_t kFive10 = kFive9 * 5;
|
||||||
|
const uint32_t kFive11 = kFive10 * 5;
|
||||||
|
const uint32_t kFive12 = kFive11 * 5;
|
||||||
|
const uint32_t kFive13 = kFive12 * 5;
|
||||||
|
const uint32_t kFive1_to_12[] =
|
||||||
|
{ kFive1, kFive2, kFive3, kFive4, kFive5, kFive6,
|
||||||
|
kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 };
|
||||||
|
|
||||||
|
ASSERT(exponent >= 0);
|
||||||
|
if (exponent == 0) return;
|
||||||
|
if (used_digits_ == 0) return;
|
||||||
|
|
||||||
|
// We shift by exponent at the end just before returning.
|
||||||
|
int remaining_exponent = exponent;
|
||||||
|
while (remaining_exponent >= 27) {
|
||||||
|
MultiplyByUInt64(kFive27);
|
||||||
|
remaining_exponent -= 27;
|
||||||
|
}
|
||||||
|
while (remaining_exponent >= 13) {
|
||||||
|
MultiplyByUInt32(kFive13);
|
||||||
|
remaining_exponent -= 13;
|
||||||
|
}
|
||||||
|
if (remaining_exponent > 0) {
|
||||||
|
MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]);
|
||||||
|
}
|
||||||
|
ShiftLeft(exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::Square() {
|
||||||
|
ASSERT(IsClamped());
|
||||||
|
int product_length = 2 * used_digits_;
|
||||||
|
EnsureCapacity(product_length);
|
||||||
|
|
||||||
|
// Comba multiplication: compute each column separately.
|
||||||
|
// Example: r = a2a1a0 * b2b1b0.
|
||||||
|
// r = 1 * a0b0 +
|
||||||
|
// 10 * (a1b0 + a0b1) +
|
||||||
|
// 100 * (a2b0 + a1b1 + a0b2) +
|
||||||
|
// 1000 * (a2b1 + a1b2) +
|
||||||
|
// 10000 * a2b2
|
||||||
|
//
|
||||||
|
// In the worst case we have to accumulate nb-digits products of digit*digit.
|
||||||
|
//
|
||||||
|
// Assert that the additional number of bits in a DoubleChunk are enough to
|
||||||
|
// sum up used_digits of Bigit*Bigit.
|
||||||
|
if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
DoubleChunk accumulator = 0;
|
||||||
|
// First shift the digits so we don't overwrite them.
|
||||||
|
int copy_offset = used_digits_;
|
||||||
|
for (int i = 0; i < used_digits_; ++i) {
|
||||||
|
bigits_[copy_offset + i] = bigits_[i];
|
||||||
|
}
|
||||||
|
// We have two loops to avoid some 'if's in the loop.
|
||||||
|
for (int i = 0; i < used_digits_; ++i) {
|
||||||
|
// Process temporary digit i with power i.
|
||||||
|
// The sum of the two indices must be equal to i.
|
||||||
|
int bigit_index1 = i;
|
||||||
|
int bigit_index2 = 0;
|
||||||
|
// Sum all of the sub-products.
|
||||||
|
while (bigit_index1 >= 0) {
|
||||||
|
Chunk chunk1 = bigits_[copy_offset + bigit_index1];
|
||||||
|
Chunk chunk2 = bigits_[copy_offset + bigit_index2];
|
||||||
|
accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
|
||||||
|
bigit_index1--;
|
||||||
|
bigit_index2++;
|
||||||
|
}
|
||||||
|
bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
|
||||||
|
accumulator >>= kBigitSize;
|
||||||
|
}
|
||||||
|
for (int i = used_digits_; i < product_length; ++i) {
|
||||||
|
int bigit_index1 = used_digits_ - 1;
|
||||||
|
int bigit_index2 = i - bigit_index1;
|
||||||
|
// Invariant: sum of both indices is again equal to i.
|
||||||
|
// Inner loop runs 0 times on last iteration, emptying accumulator.
|
||||||
|
while (bigit_index2 < used_digits_) {
|
||||||
|
Chunk chunk1 = bigits_[copy_offset + bigit_index1];
|
||||||
|
Chunk chunk2 = bigits_[copy_offset + bigit_index2];
|
||||||
|
accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
|
||||||
|
bigit_index1--;
|
||||||
|
bigit_index2++;
|
||||||
|
}
|
||||||
|
// The overwritten bigits_[i] will never be read in further loop iterations,
|
||||||
|
// because bigit_index1 and bigit_index2 are always greater
|
||||||
|
// than i - used_digits_.
|
||||||
|
bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
|
||||||
|
accumulator >>= kBigitSize;
|
||||||
|
}
|
||||||
|
// Since the result was guaranteed to lie inside the number the
|
||||||
|
// accumulator must be 0 now.
|
||||||
|
ASSERT(accumulator == 0);
|
||||||
|
|
||||||
|
// Don't forget to update the used_digits and the exponent.
|
||||||
|
used_digits_ = product_length;
|
||||||
|
exponent_ *= 2;
|
||||||
|
Clamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) {
|
||||||
|
ASSERT(base != 0);
|
||||||
|
ASSERT(power_exponent >= 0);
|
||||||
|
if (power_exponent == 0) {
|
||||||
|
AssignUInt16(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Zero();
|
||||||
|
int shifts = 0;
|
||||||
|
// We expect base to be in range 2-32, and most often to be 10.
|
||||||
|
// It does not make much sense to implement different algorithms for counting
|
||||||
|
// the bits.
|
||||||
|
while ((base & 1) == 0) {
|
||||||
|
base >>= 1;
|
||||||
|
shifts++;
|
||||||
|
}
|
||||||
|
int bit_size = 0;
|
||||||
|
int tmp_base = base;
|
||||||
|
while (tmp_base != 0) {
|
||||||
|
tmp_base >>= 1;
|
||||||
|
bit_size++;
|
||||||
|
}
|
||||||
|
int final_size = bit_size * power_exponent;
|
||||||
|
// 1 extra bigit for the shifting, and one for rounded final_size.
|
||||||
|
EnsureCapacity(final_size / kBigitSize + 2);
|
||||||
|
|
||||||
|
// Left to Right exponentiation.
|
||||||
|
int mask = 1;
|
||||||
|
while (power_exponent >= mask) mask <<= 1;
|
||||||
|
|
||||||
|
// The mask is now pointing to the bit above the most significant 1-bit of
|
||||||
|
// power_exponent.
|
||||||
|
// Get rid of first 1-bit;
|
||||||
|
mask >>= 2;
|
||||||
|
uint64_t this_value = base;
|
||||||
|
|
||||||
|
bool delayed_multipliciation = false;
|
||||||
|
const uint64_t max_32bits = 0xFFFFFFFF;
|
||||||
|
while (mask != 0 && this_value <= max_32bits) {
|
||||||
|
this_value = this_value * this_value;
|
||||||
|
// Verify that there is enough space in this_value to perform the
|
||||||
|
// multiplication. The first bit_size bits must be 0.
|
||||||
|
if ((power_exponent & mask) != 0) {
|
||||||
|
uint64_t base_bits_mask =
|
||||||
|
~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1);
|
||||||
|
bool high_bits_zero = (this_value & base_bits_mask) == 0;
|
||||||
|
if (high_bits_zero) {
|
||||||
|
this_value *= base;
|
||||||
|
} else {
|
||||||
|
delayed_multipliciation = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
AssignUInt64(this_value);
|
||||||
|
if (delayed_multipliciation) {
|
||||||
|
MultiplyByUInt32(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now do the same thing as a bignum.
|
||||||
|
while (mask != 0) {
|
||||||
|
Square();
|
||||||
|
if ((power_exponent & mask) != 0) {
|
||||||
|
MultiplyByUInt32(base);
|
||||||
|
}
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally add the saved shifts.
|
||||||
|
ShiftLeft(shifts * power_exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Precondition: this/other < 16bit.
|
||||||
|
uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) {
|
||||||
|
ASSERT(IsClamped());
|
||||||
|
ASSERT(other.IsClamped());
|
||||||
|
ASSERT(other.used_digits_ > 0);
|
||||||
|
|
||||||
|
// Easy case: if we have less digits than the divisor than the result is 0.
|
||||||
|
// Note: this handles the case where this == 0, too.
|
||||||
|
if (BigitLength() < other.BigitLength()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Align(other);
|
||||||
|
|
||||||
|
uint16_t result = 0;
|
||||||
|
|
||||||
|
// Start by removing multiples of 'other' until both numbers have the same
|
||||||
|
// number of digits.
|
||||||
|
while (BigitLength() > other.BigitLength()) {
|
||||||
|
// This naive approach is extremely inefficient if `this` divided by other
|
||||||
|
// is big. This function is implemented for doubleToString where
|
||||||
|
// the result should be small (less than 10).
|
||||||
|
ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16));
|
||||||
|
ASSERT(bigits_[used_digits_ - 1] < 0x10000);
|
||||||
|
// Remove the multiples of the first digit.
|
||||||
|
// Example this = 23 and other equals 9. -> Remove 2 multiples.
|
||||||
|
result += static_cast<uint16_t>(bigits_[used_digits_ - 1]);
|
||||||
|
SubtractTimes(other, bigits_[used_digits_ - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(BigitLength() == other.BigitLength());
|
||||||
|
|
||||||
|
// Both bignums are at the same length now.
|
||||||
|
// Since other has more than 0 digits we know that the access to
|
||||||
|
// bigits_[used_digits_ - 1] is safe.
|
||||||
|
Chunk this_bigit = bigits_[used_digits_ - 1];
|
||||||
|
Chunk other_bigit = other.bigits_[other.used_digits_ - 1];
|
||||||
|
|
||||||
|
if (other.used_digits_ == 1) {
|
||||||
|
// Shortcut for easy (and common) case.
|
||||||
|
int quotient = this_bigit / other_bigit;
|
||||||
|
bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient;
|
||||||
|
ASSERT(quotient < 0x10000);
|
||||||
|
result += static_cast<uint16_t>(quotient);
|
||||||
|
Clamp();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int division_estimate = this_bigit / (other_bigit + 1);
|
||||||
|
ASSERT(division_estimate < 0x10000);
|
||||||
|
result += static_cast<uint16_t>(division_estimate);
|
||||||
|
SubtractTimes(other, division_estimate);
|
||||||
|
|
||||||
|
if (other_bigit * (division_estimate + 1) > this_bigit) {
|
||||||
|
// No need to even try to subtract. Even if other's remaining digits were 0
|
||||||
|
// another subtraction would be too much.
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (LessEqual(other, *this)) {
|
||||||
|
SubtractBignum(other);
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
static int SizeInHexChars(S number) {
|
||||||
|
ASSERT(number > 0);
|
||||||
|
int result = 0;
|
||||||
|
while (number != 0) {
|
||||||
|
number >>= 4;
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char HexCharOfValue(int value) {
|
||||||
|
ASSERT(0 <= value && value <= 16);
|
||||||
|
if (value < 10) return static_cast<char>(value + '0');
|
||||||
|
return static_cast<char>(value - 10 + 'A');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Bignum::ToHexString(char* buffer, int buffer_size) const {
|
||||||
|
ASSERT(IsClamped());
|
||||||
|
// Each bigit must be printable as separate hex-character.
|
||||||
|
ASSERT(kBigitSize % 4 == 0);
|
||||||
|
const int kHexCharsPerBigit = kBigitSize / 4;
|
||||||
|
|
||||||
|
if (used_digits_ == 0) {
|
||||||
|
if (buffer_size < 2) return false;
|
||||||
|
buffer[0] = '0';
|
||||||
|
buffer[1] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// We add 1 for the terminating '\0' character.
|
||||||
|
int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit +
|
||||||
|
SizeInHexChars(bigits_[used_digits_ - 1]) + 1;
|
||||||
|
if (needed_chars > buffer_size) return false;
|
||||||
|
int string_index = needed_chars - 1;
|
||||||
|
buffer[string_index--] = '\0';
|
||||||
|
for (int i = 0; i < exponent_; ++i) {
|
||||||
|
for (int j = 0; j < kHexCharsPerBigit; ++j) {
|
||||||
|
buffer[string_index--] = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < used_digits_ - 1; ++i) {
|
||||||
|
Chunk current_bigit = bigits_[i];
|
||||||
|
for (int j = 0; j < kHexCharsPerBigit; ++j) {
|
||||||
|
buffer[string_index--] = HexCharOfValue(current_bigit & 0xF);
|
||||||
|
current_bigit >>= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// And finally the last bigit.
|
||||||
|
Chunk most_significant_bigit = bigits_[used_digits_ - 1];
|
||||||
|
while (most_significant_bigit != 0) {
|
||||||
|
buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF);
|
||||||
|
most_significant_bigit >>= 4;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Bignum::Chunk Bignum::BigitAt(int index) const {
|
||||||
|
if (index >= BigitLength()) return 0;
|
||||||
|
if (index < exponent_) return 0;
|
||||||
|
return bigits_[index - exponent_];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Bignum::Compare(const Bignum& a, const Bignum& b) {
|
||||||
|
ASSERT(a.IsClamped());
|
||||||
|
ASSERT(b.IsClamped());
|
||||||
|
int bigit_length_a = a.BigitLength();
|
||||||
|
int bigit_length_b = b.BigitLength();
|
||||||
|
if (bigit_length_a < bigit_length_b) return -1;
|
||||||
|
if (bigit_length_a > bigit_length_b) return +1;
|
||||||
|
for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) {
|
||||||
|
Chunk bigit_a = a.BigitAt(i);
|
||||||
|
Chunk bigit_b = b.BigitAt(i);
|
||||||
|
if (bigit_a < bigit_b) return -1;
|
||||||
|
if (bigit_a > bigit_b) return +1;
|
||||||
|
// Otherwise they are equal up to this digit. Try the next digit.
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) {
|
||||||
|
ASSERT(a.IsClamped());
|
||||||
|
ASSERT(b.IsClamped());
|
||||||
|
ASSERT(c.IsClamped());
|
||||||
|
if (a.BigitLength() < b.BigitLength()) {
|
||||||
|
return PlusCompare(b, a, c);
|
||||||
|
}
|
||||||
|
if (a.BigitLength() + 1 < c.BigitLength()) return -1;
|
||||||
|
if (a.BigitLength() > c.BigitLength()) return +1;
|
||||||
|
// The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than
|
||||||
|
// 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one
|
||||||
|
// of 'a'.
|
||||||
|
if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk borrow = 0;
|
||||||
|
// Starting at min_exponent all digits are == 0. So no need to compare them.
|
||||||
|
int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_);
|
||||||
|
for (int i = c.BigitLength() - 1; i >= min_exponent; --i) {
|
||||||
|
Chunk chunk_a = a.BigitAt(i);
|
||||||
|
Chunk chunk_b = b.BigitAt(i);
|
||||||
|
Chunk chunk_c = c.BigitAt(i);
|
||||||
|
Chunk sum = chunk_a + chunk_b;
|
||||||
|
if (sum > chunk_c + borrow) {
|
||||||
|
return +1;
|
||||||
|
} else {
|
||||||
|
borrow = chunk_c + borrow - sum;
|
||||||
|
if (borrow > 1) return -1;
|
||||||
|
borrow <<= kBigitSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (borrow == 0) return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::Clamp() {
|
||||||
|
while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) {
|
||||||
|
used_digits_--;
|
||||||
|
}
|
||||||
|
if (used_digits_ == 0) {
|
||||||
|
// Zero.
|
||||||
|
exponent_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Bignum::IsClamped() const {
|
||||||
|
return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::Zero() {
|
||||||
|
for (int i = 0; i < used_digits_; ++i) {
|
||||||
|
bigits_[i] = 0;
|
||||||
|
}
|
||||||
|
used_digits_ = 0;
|
||||||
|
exponent_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::Align(const Bignum& other) {
|
||||||
|
if (exponent_ > other.exponent_) {
|
||||||
|
// If "X" represents a "hidden" digit (by the exponent) then we are in the
|
||||||
|
// following case (a == this, b == other):
|
||||||
|
// a: aaaaaaXXXX or a: aaaaaXXX
|
||||||
|
// b: bbbbbbX b: bbbbbbbbXX
|
||||||
|
// We replace some of the hidden digits (X) of a with 0 digits.
|
||||||
|
// a: aaaaaa000X or a: aaaaa0XX
|
||||||
|
int zero_digits = exponent_ - other.exponent_;
|
||||||
|
EnsureCapacity(used_digits_ + zero_digits);
|
||||||
|
for (int i = used_digits_ - 1; i >= 0; --i) {
|
||||||
|
bigits_[i + zero_digits] = bigits_[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < zero_digits; ++i) {
|
||||||
|
bigits_[i] = 0;
|
||||||
|
}
|
||||||
|
used_digits_ += zero_digits;
|
||||||
|
exponent_ -= zero_digits;
|
||||||
|
ASSERT(used_digits_ >= 0);
|
||||||
|
ASSERT(exponent_ >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::BigitsShiftLeft(int shift_amount) {
|
||||||
|
ASSERT(shift_amount < kBigitSize);
|
||||||
|
ASSERT(shift_amount >= 0);
|
||||||
|
Chunk carry = 0;
|
||||||
|
for (int i = 0; i < used_digits_; ++i) {
|
||||||
|
Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount);
|
||||||
|
bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask;
|
||||||
|
carry = new_carry;
|
||||||
|
}
|
||||||
|
if (carry != 0) {
|
||||||
|
bigits_[used_digits_] = carry;
|
||||||
|
used_digits_++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bignum::SubtractTimes(const Bignum& other, int factor) {
|
||||||
|
ASSERT(exponent_ <= other.exponent_);
|
||||||
|
if (factor < 3) {
|
||||||
|
for (int i = 0; i < factor; ++i) {
|
||||||
|
SubtractBignum(other);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Chunk borrow = 0;
|
||||||
|
int exponent_diff = other.exponent_ - exponent_;
|
||||||
|
for (int i = 0; i < other.used_digits_; ++i) {
|
||||||
|
DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i];
|
||||||
|
DoubleChunk remove = borrow + product;
|
||||||
|
Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask);
|
||||||
|
bigits_[i + exponent_diff] = difference & kBigitMask;
|
||||||
|
borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) +
|
||||||
|
(remove >> kBigitSize));
|
||||||
|
}
|
||||||
|
for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) {
|
||||||
|
if (borrow == 0) return;
|
||||||
|
Chunk difference = bigits_[i] - borrow;
|
||||||
|
bigits_[i] = difference & kBigitMask;
|
||||||
|
borrow = difference >> (kChunkSize - 1);
|
||||||
|
}
|
||||||
|
Clamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
144
contrib/libdouble-conversion/double-conversion/bignum.h
Normal file
144
contrib/libdouble-conversion/double-conversion/bignum.h
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_BIGNUM_H_
|
||||||
|
#define DOUBLE_CONVERSION_BIGNUM_H_
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
class Bignum {
|
||||||
|
public:
|
||||||
|
// 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
|
||||||
|
// This bignum can encode much bigger numbers, since it contains an
|
||||||
|
// exponent.
|
||||||
|
static const int kMaxSignificantBits = 3584;
|
||||||
|
|
||||||
|
Bignum();
|
||||||
|
void AssignUInt16(uint16_t value);
|
||||||
|
void AssignUInt64(uint64_t value);
|
||||||
|
void AssignBignum(const Bignum& other);
|
||||||
|
|
||||||
|
void AssignDecimalString(Vector<const char> value);
|
||||||
|
void AssignHexString(Vector<const char> value);
|
||||||
|
|
||||||
|
void AssignPowerUInt16(uint16_t base, int exponent);
|
||||||
|
|
||||||
|
void AddUInt64(uint64_t operand);
|
||||||
|
void AddBignum(const Bignum& other);
|
||||||
|
// Precondition: this >= other.
|
||||||
|
void SubtractBignum(const Bignum& other);
|
||||||
|
|
||||||
|
void Square();
|
||||||
|
void ShiftLeft(int shift_amount);
|
||||||
|
void MultiplyByUInt32(uint32_t factor);
|
||||||
|
void MultiplyByUInt64(uint64_t factor);
|
||||||
|
void MultiplyByPowerOfTen(int exponent);
|
||||||
|
void Times10() { return MultiplyByUInt32(10); }
|
||||||
|
// Pseudocode:
|
||||||
|
// int result = this / other;
|
||||||
|
// this = this % other;
|
||||||
|
// In the worst case this function is in O(this/other).
|
||||||
|
uint16_t DivideModuloIntBignum(const Bignum& other);
|
||||||
|
|
||||||
|
bool ToHexString(char* buffer, int buffer_size) const;
|
||||||
|
|
||||||
|
// Returns
|
||||||
|
// -1 if a < b,
|
||||||
|
// 0 if a == b, and
|
||||||
|
// +1 if a > b.
|
||||||
|
static int Compare(const Bignum& a, const Bignum& b);
|
||||||
|
static bool Equal(const Bignum& a, const Bignum& b) {
|
||||||
|
return Compare(a, b) == 0;
|
||||||
|
}
|
||||||
|
static bool LessEqual(const Bignum& a, const Bignum& b) {
|
||||||
|
return Compare(a, b) <= 0;
|
||||||
|
}
|
||||||
|
static bool Less(const Bignum& a, const Bignum& b) {
|
||||||
|
return Compare(a, b) < 0;
|
||||||
|
}
|
||||||
|
// Returns Compare(a + b, c);
|
||||||
|
static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c);
|
||||||
|
// Returns a + b == c
|
||||||
|
static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
|
||||||
|
return PlusCompare(a, b, c) == 0;
|
||||||
|
}
|
||||||
|
// Returns a + b <= c
|
||||||
|
static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
|
||||||
|
return PlusCompare(a, b, c) <= 0;
|
||||||
|
}
|
||||||
|
// Returns a + b < c
|
||||||
|
static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) {
|
||||||
|
return PlusCompare(a, b, c) < 0;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
typedef uint32_t Chunk;
|
||||||
|
typedef uint64_t DoubleChunk;
|
||||||
|
|
||||||
|
static const int kChunkSize = sizeof(Chunk) * 8;
|
||||||
|
static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8;
|
||||||
|
// With bigit size of 28 we loose some bits, but a double still fits easily
|
||||||
|
// into two chunks, and more importantly we can use the Comba multiplication.
|
||||||
|
static const int kBigitSize = 28;
|
||||||
|
static const Chunk kBigitMask = (1 << kBigitSize) - 1;
|
||||||
|
// Every instance allocates kBigitLength chunks on the stack. Bignums cannot
|
||||||
|
// grow. There are no checks if the stack-allocated space is sufficient.
|
||||||
|
static const int kBigitCapacity = kMaxSignificantBits / kBigitSize;
|
||||||
|
|
||||||
|
void EnsureCapacity(int size) {
|
||||||
|
if (size > kBigitCapacity) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Align(const Bignum& other);
|
||||||
|
void Clamp();
|
||||||
|
bool IsClamped() const;
|
||||||
|
void Zero();
|
||||||
|
// Requires this to have enough capacity (no tests done).
|
||||||
|
// Updates used_digits_ if necessary.
|
||||||
|
// shift_amount must be < kBigitSize.
|
||||||
|
void BigitsShiftLeft(int shift_amount);
|
||||||
|
// BigitLength includes the "hidden" digits encoded in the exponent.
|
||||||
|
int BigitLength() const { return used_digits_ + exponent_; }
|
||||||
|
Chunk BigitAt(int index) const;
|
||||||
|
void SubtractTimes(const Bignum& other, int factor);
|
||||||
|
|
||||||
|
Chunk bigits_buffer_[kBigitCapacity];
|
||||||
|
// A vector backed by bigits_buffer_. This way accesses to the array are
|
||||||
|
// checked for out-of-bounds errors.
|
||||||
|
Vector<Chunk> bigits_;
|
||||||
|
int used_digits_;
|
||||||
|
// The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
|
||||||
|
int exponent_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Bignum);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_BIGNUM_H_
|
175
contrib/libdouble-conversion/double-conversion/cached-powers.cc
Normal file
175
contrib/libdouble-conversion/double-conversion/cached-powers.cc
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include "cached-powers.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
struct CachedPower {
|
||||||
|
uint64_t significand;
|
||||||
|
int16_t binary_exponent;
|
||||||
|
int16_t decimal_exponent;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const CachedPower kCachedPowers[] = {
|
||||||
|
{UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348},
|
||||||
|
{UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340},
|
||||||
|
{UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332},
|
||||||
|
{UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324},
|
||||||
|
{UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316},
|
||||||
|
{UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308},
|
||||||
|
{UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300},
|
||||||
|
{UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292},
|
||||||
|
{UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284},
|
||||||
|
{UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276},
|
||||||
|
{UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268},
|
||||||
|
{UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260},
|
||||||
|
{UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252},
|
||||||
|
{UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244},
|
||||||
|
{UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236},
|
||||||
|
{UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228},
|
||||||
|
{UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220},
|
||||||
|
{UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212},
|
||||||
|
{UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204},
|
||||||
|
{UINT64_2PART_C(0xef340a98, 172aace5), -715, -196},
|
||||||
|
{UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188},
|
||||||
|
{UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180},
|
||||||
|
{UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172},
|
||||||
|
{UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164},
|
||||||
|
{UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156},
|
||||||
|
{UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148},
|
||||||
|
{UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140},
|
||||||
|
{UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132},
|
||||||
|
{UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124},
|
||||||
|
{UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116},
|
||||||
|
{UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108},
|
||||||
|
{UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100},
|
||||||
|
{UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92},
|
||||||
|
{UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84},
|
||||||
|
{UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76},
|
||||||
|
{UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68},
|
||||||
|
{UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60},
|
||||||
|
{UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52},
|
||||||
|
{UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44},
|
||||||
|
{UINT64_2PART_C(0xaa242499, 697392d3), -183, -36},
|
||||||
|
{UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28},
|
||||||
|
{UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20},
|
||||||
|
{UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12},
|
||||||
|
{UINT64_2PART_C(0xd1b71758, e219652c), -77, -4},
|
||||||
|
{UINT64_2PART_C(0x9c400000, 00000000), -50, 4},
|
||||||
|
{UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12},
|
||||||
|
{UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20},
|
||||||
|
{UINT64_2PART_C(0x813f3978, f8940984), 30, 28},
|
||||||
|
{UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36},
|
||||||
|
{UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44},
|
||||||
|
{UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52},
|
||||||
|
{UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60},
|
||||||
|
{UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68},
|
||||||
|
{UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76},
|
||||||
|
{UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84},
|
||||||
|
{UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92},
|
||||||
|
{UINT64_2PART_C(0x924d692c, a61be758), 269, 100},
|
||||||
|
{UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108},
|
||||||
|
{UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116},
|
||||||
|
{UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124},
|
||||||
|
{UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132},
|
||||||
|
{UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140},
|
||||||
|
{UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148},
|
||||||
|
{UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156},
|
||||||
|
{UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164},
|
||||||
|
{UINT64_2PART_C(0xa59bc234, db398c25), 508, 172},
|
||||||
|
{UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180},
|
||||||
|
{UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188},
|
||||||
|
{UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196},
|
||||||
|
{UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204},
|
||||||
|
{UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212},
|
||||||
|
{UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220},
|
||||||
|
{UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228},
|
||||||
|
{UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236},
|
||||||
|
{UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244},
|
||||||
|
{UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252},
|
||||||
|
{UINT64_2PART_C(0xd01fef10, a657842c), 800, 260},
|
||||||
|
{UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268},
|
||||||
|
{UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276},
|
||||||
|
{UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284},
|
||||||
|
{UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292},
|
||||||
|
{UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300},
|
||||||
|
{UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308},
|
||||||
|
{UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316},
|
||||||
|
{UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324},
|
||||||
|
{UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332},
|
||||||
|
{UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent.
|
||||||
|
static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
|
||||||
|
// Difference between the decimal exponents in the table above.
|
||||||
|
const int PowersOfTenCache::kDecimalExponentDistance = 8;
|
||||||
|
const int PowersOfTenCache::kMinDecimalExponent = -348;
|
||||||
|
const int PowersOfTenCache::kMaxDecimalExponent = 340;
|
||||||
|
|
||||||
|
void PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
|
||||||
|
int min_exponent,
|
||||||
|
int max_exponent,
|
||||||
|
DiyFp* power,
|
||||||
|
int* decimal_exponent) {
|
||||||
|
int kQ = DiyFp::kSignificandSize;
|
||||||
|
double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
|
||||||
|
int foo = kCachedPowersOffset;
|
||||||
|
int index =
|
||||||
|
(foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
|
||||||
|
ASSERT(0 <= index && index < static_cast<int>(ARRAY_SIZE(kCachedPowers)));
|
||||||
|
CachedPower cached_power = kCachedPowers[index];
|
||||||
|
ASSERT(min_exponent <= cached_power.binary_exponent);
|
||||||
|
(void) max_exponent; // Mark variable as used.
|
||||||
|
ASSERT(cached_power.binary_exponent <= max_exponent);
|
||||||
|
*decimal_exponent = cached_power.decimal_exponent;
|
||||||
|
*power = DiyFp(cached_power.significand, cached_power.binary_exponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent,
|
||||||
|
DiyFp* power,
|
||||||
|
int* found_exponent) {
|
||||||
|
ASSERT(kMinDecimalExponent <= requested_exponent);
|
||||||
|
ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
|
||||||
|
int index =
|
||||||
|
(requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
|
||||||
|
CachedPower cached_power = kCachedPowers[index];
|
||||||
|
*power = DiyFp(cached_power.significand, cached_power.binary_exponent);
|
||||||
|
*found_exponent = cached_power.decimal_exponent;
|
||||||
|
ASSERT(*found_exponent <= requested_exponent);
|
||||||
|
ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_
|
||||||
|
#define DOUBLE_CONVERSION_CACHED_POWERS_H_
|
||||||
|
|
||||||
|
#include "diy-fp.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
class PowersOfTenCache {
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Not all powers of ten are cached. The decimal exponent of two neighboring
|
||||||
|
// cached numbers will differ by kDecimalExponentDistance.
|
||||||
|
static const int kDecimalExponentDistance;
|
||||||
|
|
||||||
|
static const int kMinDecimalExponent;
|
||||||
|
static const int kMaxDecimalExponent;
|
||||||
|
|
||||||
|
// Returns a cached power-of-ten with a binary exponent in the range
|
||||||
|
// [min_exponent; max_exponent] (boundaries included).
|
||||||
|
static void GetCachedPowerForBinaryExponentRange(int min_exponent,
|
||||||
|
int max_exponent,
|
||||||
|
DiyFp* power,
|
||||||
|
int* decimal_exponent);
|
||||||
|
|
||||||
|
// Returns a cached power of ten x ~= 10^k such that
|
||||||
|
// k <= decimal_exponent < k + kCachedPowersDecimalDistance.
|
||||||
|
// The given decimal_exponent must satisfy
|
||||||
|
// kMinDecimalExponent <= requested_exponent, and
|
||||||
|
// requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance.
|
||||||
|
static void GetCachedPowerForDecimalExponent(int requested_exponent,
|
||||||
|
DiyFp* power,
|
||||||
|
int* found_exponent);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_
|
57
contrib/libdouble-conversion/double-conversion/diy-fp.cc
Normal file
57
contrib/libdouble-conversion/double-conversion/diy-fp.cc
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
#include "diy-fp.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
void DiyFp::Multiply(const DiyFp& other) {
|
||||||
|
// Simply "emulates" a 128 bit multiplication.
|
||||||
|
// However: the resulting number only contains 64 bits. The least
|
||||||
|
// significant 64 bits are only used for rounding the most significant 64
|
||||||
|
// bits.
|
||||||
|
const uint64_t kM32 = 0xFFFFFFFFU;
|
||||||
|
uint64_t a = f_ >> 32;
|
||||||
|
uint64_t b = f_ & kM32;
|
||||||
|
uint64_t c = other.f_ >> 32;
|
||||||
|
uint64_t d = other.f_ & kM32;
|
||||||
|
uint64_t ac = a * c;
|
||||||
|
uint64_t bc = b * c;
|
||||||
|
uint64_t ad = a * d;
|
||||||
|
uint64_t bd = b * d;
|
||||||
|
uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32);
|
||||||
|
// By adding 1U << 31 to tmp we round the final result.
|
||||||
|
// Halfway cases will be round up.
|
||||||
|
tmp += 1U << 31;
|
||||||
|
uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
|
||||||
|
e_ += other.e_ + 64;
|
||||||
|
f_ = result_f;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
118
contrib/libdouble-conversion/double-conversion/diy-fp.h
Normal file
118
contrib/libdouble-conversion/double-conversion/diy-fp.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_DIY_FP_H_
|
||||||
|
#define DOUBLE_CONVERSION_DIY_FP_H_
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
// This "Do It Yourself Floating Point" class implements a floating-point number
|
||||||
|
// with a uint64 significand and an int exponent. Normalized DiyFp numbers will
|
||||||
|
// have the most significant bit of the significand set.
|
||||||
|
// Multiplication and Subtraction do not normalize their results.
|
||||||
|
// DiyFp are not designed to contain special doubles (NaN and Infinity).
|
||||||
|
class DiyFp {
|
||||||
|
public:
|
||||||
|
static const int kSignificandSize = 64;
|
||||||
|
|
||||||
|
DiyFp() : f_(0), e_(0) {}
|
||||||
|
DiyFp(uint64_t significand, int exponent) : f_(significand), e_(exponent) {}
|
||||||
|
|
||||||
|
// this = this - other.
|
||||||
|
// The exponents of both numbers must be the same and the significand of this
|
||||||
|
// must be bigger than the significand of other.
|
||||||
|
// The result will not be normalized.
|
||||||
|
void Subtract(const DiyFp& other) {
|
||||||
|
ASSERT(e_ == other.e_);
|
||||||
|
ASSERT(f_ >= other.f_);
|
||||||
|
f_ -= other.f_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a - b.
|
||||||
|
// The exponents of both numbers must be the same and this must be bigger
|
||||||
|
// than other. The result will not be normalized.
|
||||||
|
static DiyFp Minus(const DiyFp& a, const DiyFp& b) {
|
||||||
|
DiyFp result = a;
|
||||||
|
result.Subtract(b);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// this = this * other.
|
||||||
|
void Multiply(const DiyFp& other);
|
||||||
|
|
||||||
|
// returns a * b;
|
||||||
|
static DiyFp Times(const DiyFp& a, const DiyFp& b) {
|
||||||
|
DiyFp result = a;
|
||||||
|
result.Multiply(b);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Normalize() {
|
||||||
|
ASSERT(f_ != 0);
|
||||||
|
uint64_t significand = f_;
|
||||||
|
int exponent = e_;
|
||||||
|
|
||||||
|
// This method is mainly called for normalizing boundaries. In general
|
||||||
|
// boundaries need to be shifted by 10 bits. We thus optimize for this case.
|
||||||
|
const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000);
|
||||||
|
while ((significand & k10MSBits) == 0) {
|
||||||
|
significand <<= 10;
|
||||||
|
exponent -= 10;
|
||||||
|
}
|
||||||
|
while ((significand & kUint64MSB) == 0) {
|
||||||
|
significand <<= 1;
|
||||||
|
exponent--;
|
||||||
|
}
|
||||||
|
f_ = significand;
|
||||||
|
e_ = exponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DiyFp Normalize(const DiyFp& a) {
|
||||||
|
DiyFp result = a;
|
||||||
|
result.Normalize();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t f() const { return f_; }
|
||||||
|
int e() const { return e_; }
|
||||||
|
|
||||||
|
void set_f(uint64_t new_value) { f_ = new_value; }
|
||||||
|
void set_e(int new_value) { e_ = new_value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000);
|
||||||
|
|
||||||
|
uint64_t f_;
|
||||||
|
int e_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_DIY_FP_H_
|
@ -0,0 +1,982 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "double-conversion.h"
|
||||||
|
|
||||||
|
#include "bignum-dtoa.h"
|
||||||
|
#include "fast-dtoa.h"
|
||||||
|
#include "fixed-dtoa.h"
|
||||||
|
#include "ieee.h"
|
||||||
|
#include "strtod.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() {
|
||||||
|
int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN;
|
||||||
|
static DoubleToStringConverter converter(flags,
|
||||||
|
"Infinity",
|
||||||
|
"NaN",
|
||||||
|
'e',
|
||||||
|
-6, 21,
|
||||||
|
6, 0);
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DoubleToStringConverter::HandleSpecialValues(
|
||||||
|
double value,
|
||||||
|
StringBuilder* result_builder) const {
|
||||||
|
Double double_inspect(value);
|
||||||
|
if (double_inspect.IsInfinite()) {
|
||||||
|
if (infinity_symbol_ == NULL) return false;
|
||||||
|
if (value < 0) {
|
||||||
|
result_builder->AddCharacter('-');
|
||||||
|
}
|
||||||
|
result_builder->AddString(infinity_symbol_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (double_inspect.IsNan()) {
|
||||||
|
if (nan_symbol_ == NULL) return false;
|
||||||
|
result_builder->AddString(nan_symbol_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DoubleToStringConverter::CreateExponentialRepresentation(
|
||||||
|
const char* decimal_digits,
|
||||||
|
int length,
|
||||||
|
int exponent,
|
||||||
|
StringBuilder* result_builder) const {
|
||||||
|
ASSERT(length != 0);
|
||||||
|
result_builder->AddCharacter(decimal_digits[0]);
|
||||||
|
if (length != 1) {
|
||||||
|
result_builder->AddCharacter('.');
|
||||||
|
result_builder->AddSubstring(&decimal_digits[1], length-1);
|
||||||
|
}
|
||||||
|
result_builder->AddCharacter(exponent_character_);
|
||||||
|
if (exponent < 0) {
|
||||||
|
result_builder->AddCharacter('-');
|
||||||
|
exponent = -exponent;
|
||||||
|
} else {
|
||||||
|
if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) {
|
||||||
|
result_builder->AddCharacter('+');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exponent == 0) {
|
||||||
|
result_builder->AddCharacter('0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT(exponent < 1e4);
|
||||||
|
const int kMaxExponentLength = 5;
|
||||||
|
char buffer[kMaxExponentLength + 1];
|
||||||
|
buffer[kMaxExponentLength] = '\0';
|
||||||
|
int first_char_pos = kMaxExponentLength;
|
||||||
|
while (exponent > 0) {
|
||||||
|
buffer[--first_char_pos] = '0' + (exponent % 10);
|
||||||
|
exponent /= 10;
|
||||||
|
}
|
||||||
|
result_builder->AddSubstring(&buffer[first_char_pos],
|
||||||
|
kMaxExponentLength - first_char_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DoubleToStringConverter::CreateDecimalRepresentation(
|
||||||
|
const char* decimal_digits,
|
||||||
|
int length,
|
||||||
|
int decimal_point,
|
||||||
|
int digits_after_point,
|
||||||
|
StringBuilder* result_builder) const {
|
||||||
|
// Create a representation that is padded with zeros if needed.
|
||||||
|
if (decimal_point <= 0) {
|
||||||
|
// "0.00000decimal_rep".
|
||||||
|
result_builder->AddCharacter('0');
|
||||||
|
if (digits_after_point > 0) {
|
||||||
|
result_builder->AddCharacter('.');
|
||||||
|
result_builder->AddPadding('0', -decimal_point);
|
||||||
|
ASSERT(length <= digits_after_point - (-decimal_point));
|
||||||
|
result_builder->AddSubstring(decimal_digits, length);
|
||||||
|
int remaining_digits = digits_after_point - (-decimal_point) - length;
|
||||||
|
result_builder->AddPadding('0', remaining_digits);
|
||||||
|
}
|
||||||
|
} else if (decimal_point >= length) {
|
||||||
|
// "decimal_rep0000.00000" or "decimal_rep.0000"
|
||||||
|
result_builder->AddSubstring(decimal_digits, length);
|
||||||
|
result_builder->AddPadding('0', decimal_point - length);
|
||||||
|
if (digits_after_point > 0) {
|
||||||
|
result_builder->AddCharacter('.');
|
||||||
|
result_builder->AddPadding('0', digits_after_point);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// "decima.l_rep000"
|
||||||
|
ASSERT(digits_after_point > 0);
|
||||||
|
result_builder->AddSubstring(decimal_digits, decimal_point);
|
||||||
|
result_builder->AddCharacter('.');
|
||||||
|
ASSERT(length - decimal_point <= digits_after_point);
|
||||||
|
result_builder->AddSubstring(&decimal_digits[decimal_point],
|
||||||
|
length - decimal_point);
|
||||||
|
int remaining_digits = digits_after_point - (length - decimal_point);
|
||||||
|
result_builder->AddPadding('0', remaining_digits);
|
||||||
|
}
|
||||||
|
if (digits_after_point == 0) {
|
||||||
|
if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) {
|
||||||
|
result_builder->AddCharacter('.');
|
||||||
|
}
|
||||||
|
if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) {
|
||||||
|
result_builder->AddCharacter('0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DoubleToStringConverter::ToShortestIeeeNumber(
|
||||||
|
double value,
|
||||||
|
StringBuilder* result_builder,
|
||||||
|
DoubleToStringConverter::DtoaMode mode) const {
|
||||||
|
ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE);
|
||||||
|
if (Double(value).IsSpecial()) {
|
||||||
|
return HandleSpecialValues(value, result_builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
int decimal_point;
|
||||||
|
bool sign;
|
||||||
|
const int kDecimalRepCapacity = kBase10MaximalLength + 1;
|
||||||
|
char decimal_rep[kDecimalRepCapacity];
|
||||||
|
int decimal_rep_length;
|
||||||
|
|
||||||
|
DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity,
|
||||||
|
&sign, &decimal_rep_length, &decimal_point);
|
||||||
|
|
||||||
|
bool unique_zero = (flags_ & UNIQUE_ZERO) != 0;
|
||||||
|
if (sign && (value != 0.0 || !unique_zero)) {
|
||||||
|
result_builder->AddCharacter('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
int exponent = decimal_point - 1;
|
||||||
|
if ((decimal_in_shortest_low_ <= exponent) &&
|
||||||
|
(exponent < decimal_in_shortest_high_)) {
|
||||||
|
CreateDecimalRepresentation(decimal_rep, decimal_rep_length,
|
||||||
|
decimal_point,
|
||||||
|
Max(0, decimal_rep_length - decimal_point),
|
||||||
|
result_builder);
|
||||||
|
} else {
|
||||||
|
CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
|
||||||
|
result_builder);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DoubleToStringConverter::ToFixed(double value,
|
||||||
|
int requested_digits,
|
||||||
|
StringBuilder* result_builder) const {
|
||||||
|
ASSERT(kMaxFixedDigitsBeforePoint == 60);
|
||||||
|
const double kFirstNonFixed = 1e60;
|
||||||
|
|
||||||
|
if (Double(value).IsSpecial()) {
|
||||||
|
return HandleSpecialValues(value, result_builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requested_digits > kMaxFixedDigitsAfterPoint) return false;
|
||||||
|
if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false;
|
||||||
|
|
||||||
|
// Find a sufficiently precise decimal representation of n.
|
||||||
|
int decimal_point;
|
||||||
|
bool sign;
|
||||||
|
// Add space for the '\0' byte.
|
||||||
|
const int kDecimalRepCapacity =
|
||||||
|
kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1;
|
||||||
|
char decimal_rep[kDecimalRepCapacity];
|
||||||
|
int decimal_rep_length;
|
||||||
|
DoubleToAscii(value, FIXED, requested_digits,
|
||||||
|
decimal_rep, kDecimalRepCapacity,
|
||||||
|
&sign, &decimal_rep_length, &decimal_point);
|
||||||
|
|
||||||
|
bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
|
||||||
|
if (sign && (value != 0.0 || !unique_zero)) {
|
||||||
|
result_builder->AddCharacter('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
|
||||||
|
requested_digits, result_builder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DoubleToStringConverter::ToExponential(
|
||||||
|
double value,
|
||||||
|
int requested_digits,
|
||||||
|
StringBuilder* result_builder) const {
|
||||||
|
if (Double(value).IsSpecial()) {
|
||||||
|
return HandleSpecialValues(value, result_builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requested_digits < -1) return false;
|
||||||
|
if (requested_digits > kMaxExponentialDigits) return false;
|
||||||
|
|
||||||
|
int decimal_point;
|
||||||
|
bool sign;
|
||||||
|
// Add space for digit before the decimal point and the '\0' character.
|
||||||
|
const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
|
||||||
|
ASSERT(kDecimalRepCapacity > kBase10MaximalLength);
|
||||||
|
char decimal_rep[kDecimalRepCapacity];
|
||||||
|
int decimal_rep_length;
|
||||||
|
|
||||||
|
if (requested_digits == -1) {
|
||||||
|
DoubleToAscii(value, SHORTEST, 0,
|
||||||
|
decimal_rep, kDecimalRepCapacity,
|
||||||
|
&sign, &decimal_rep_length, &decimal_point);
|
||||||
|
} else {
|
||||||
|
DoubleToAscii(value, PRECISION, requested_digits + 1,
|
||||||
|
decimal_rep, kDecimalRepCapacity,
|
||||||
|
&sign, &decimal_rep_length, &decimal_point);
|
||||||
|
ASSERT(decimal_rep_length <= requested_digits + 1);
|
||||||
|
|
||||||
|
for (int i = decimal_rep_length; i < requested_digits + 1; ++i) {
|
||||||
|
decimal_rep[i] = '0';
|
||||||
|
}
|
||||||
|
decimal_rep_length = requested_digits + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
|
||||||
|
if (sign && (value != 0.0 || !unique_zero)) {
|
||||||
|
result_builder->AddCharacter('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
int exponent = decimal_point - 1;
|
||||||
|
CreateExponentialRepresentation(decimal_rep,
|
||||||
|
decimal_rep_length,
|
||||||
|
exponent,
|
||||||
|
result_builder);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DoubleToStringConverter::ToPrecision(double value,
|
||||||
|
int precision,
|
||||||
|
StringBuilder* result_builder) const {
|
||||||
|
if (Double(value).IsSpecial()) {
|
||||||
|
return HandleSpecialValues(value, result_builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a sufficiently precise decimal representation of n.
|
||||||
|
int decimal_point;
|
||||||
|
bool sign;
|
||||||
|
// Add one for the terminating null character.
|
||||||
|
const int kDecimalRepCapacity = kMaxPrecisionDigits + 1;
|
||||||
|
char decimal_rep[kDecimalRepCapacity];
|
||||||
|
int decimal_rep_length;
|
||||||
|
|
||||||
|
DoubleToAscii(value, PRECISION, precision,
|
||||||
|
decimal_rep, kDecimalRepCapacity,
|
||||||
|
&sign, &decimal_rep_length, &decimal_point);
|
||||||
|
ASSERT(decimal_rep_length <= precision);
|
||||||
|
|
||||||
|
bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
|
||||||
|
if (sign && (value != 0.0 || !unique_zero)) {
|
||||||
|
result_builder->AddCharacter('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The exponent if we print the number as x.xxeyyy. That is with the
|
||||||
|
// decimal point after the first digit.
|
||||||
|
int exponent = decimal_point - 1;
|
||||||
|
|
||||||
|
int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
|
||||||
|
if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
|
||||||
|
(decimal_point - precision + extra_zero >
|
||||||
|
max_trailing_padding_zeroes_in_precision_mode_)) {
|
||||||
|
// Fill buffer to contain 'precision' digits.
|
||||||
|
// Usually the buffer is already at the correct length, but 'DoubleToAscii'
|
||||||
|
// is allowed to return less characters.
|
||||||
|
for (int i = decimal_rep_length; i < precision; ++i) {
|
||||||
|
decimal_rep[i] = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateExponentialRepresentation(decimal_rep,
|
||||||
|
precision,
|
||||||
|
exponent,
|
||||||
|
result_builder);
|
||||||
|
} else {
|
||||||
|
CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
|
||||||
|
Max(0, precision - decimal_point),
|
||||||
|
result_builder);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static BignumDtoaMode DtoaToBignumDtoaMode(
|
||||||
|
DoubleToStringConverter::DtoaMode dtoa_mode) {
|
||||||
|
switch (dtoa_mode) {
|
||||||
|
case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST;
|
||||||
|
case DoubleToStringConverter::SHORTEST_SINGLE:
|
||||||
|
return BIGNUM_DTOA_SHORTEST_SINGLE;
|
||||||
|
case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED;
|
||||||
|
case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DoubleToStringConverter::DoubleToAscii(double v,
|
||||||
|
DtoaMode mode,
|
||||||
|
int requested_digits,
|
||||||
|
char* buffer,
|
||||||
|
int buffer_length,
|
||||||
|
bool* sign,
|
||||||
|
int* length,
|
||||||
|
int* point) {
|
||||||
|
Vector<char> vector(buffer, buffer_length);
|
||||||
|
ASSERT(!Double(v).IsSpecial());
|
||||||
|
ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0);
|
||||||
|
|
||||||
|
if (Double(v).Sign() < 0) {
|
||||||
|
*sign = true;
|
||||||
|
v = -v;
|
||||||
|
} else {
|
||||||
|
*sign = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == PRECISION && requested_digits == 0) {
|
||||||
|
vector[0] = '\0';
|
||||||
|
*length = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v == 0) {
|
||||||
|
vector[0] = '0';
|
||||||
|
vector[1] = '\0';
|
||||||
|
*length = 1;
|
||||||
|
*point = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fast_worked;
|
||||||
|
switch (mode) {
|
||||||
|
case SHORTEST:
|
||||||
|
fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
|
||||||
|
break;
|
||||||
|
case SHORTEST_SINGLE:
|
||||||
|
fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0,
|
||||||
|
vector, length, point);
|
||||||
|
break;
|
||||||
|
case FIXED:
|
||||||
|
fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
|
||||||
|
break;
|
||||||
|
case PRECISION:
|
||||||
|
fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
|
||||||
|
vector, length, point);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fast_worked = false;
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
if (fast_worked) return;
|
||||||
|
|
||||||
|
// If the fast dtoa didn't succeed use the slower bignum version.
|
||||||
|
BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
|
||||||
|
BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
|
||||||
|
vector[*length] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Consumes the given substring from the iterator.
|
||||||
|
// Returns false, if the substring does not match.
|
||||||
|
template <class Iterator>
|
||||||
|
static bool ConsumeSubString(Iterator* current,
|
||||||
|
Iterator end,
|
||||||
|
const char* substring) {
|
||||||
|
ASSERT(**current == *substring);
|
||||||
|
for (substring++; *substring != '\0'; substring++) {
|
||||||
|
++*current;
|
||||||
|
if (*current == end || **current != *substring) return false;
|
||||||
|
}
|
||||||
|
++*current;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Maximum number of significant digits in decimal representation.
|
||||||
|
// The longest possible double in decimal representation is
|
||||||
|
// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074
|
||||||
|
// (768 digits). If we parse a number whose first digits are equal to a
|
||||||
|
// mean of 2 adjacent doubles (that could have up to 769 digits) the result
|
||||||
|
// must be rounded to the bigger one unless the tail consists of zeros, so
|
||||||
|
// we don't need to preserve all the digits.
|
||||||
|
const int kMaxSignificantDigits = 772;
|
||||||
|
|
||||||
|
|
||||||
|
static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 };
|
||||||
|
static const int kWhitespaceTable7Length = ARRAY_SIZE(kWhitespaceTable7);
|
||||||
|
|
||||||
|
|
||||||
|
static const uc16 kWhitespaceTable16[] = {
|
||||||
|
160, 8232, 8233, 5760, 6158, 8192, 8193, 8194, 8195,
|
||||||
|
8196, 8197, 8198, 8199, 8200, 8201, 8202, 8239, 8287, 12288, 65279
|
||||||
|
};
|
||||||
|
static const int kWhitespaceTable16Length = ARRAY_SIZE(kWhitespaceTable16);
|
||||||
|
|
||||||
|
|
||||||
|
static bool isWhitespace(int x) {
|
||||||
|
if (x < 128) {
|
||||||
|
for (int i = 0; i < kWhitespaceTable7Length; i++) {
|
||||||
|
if (kWhitespaceTable7[i] == x) return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < kWhitespaceTable16Length; i++) {
|
||||||
|
if (kWhitespaceTable16[i] == x) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true if a nonspace found and false if the end has reached.
|
||||||
|
template <class Iterator>
|
||||||
|
static inline bool AdvanceToNonspace(Iterator* current, Iterator end) {
|
||||||
|
while (*current != end) {
|
||||||
|
if (!isWhitespace(**current)) return true;
|
||||||
|
++*current;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool isDigit(int x, int radix) {
|
||||||
|
return (x >= '0' && x <= '9' && x < '0' + radix)
|
||||||
|
|| (radix > 10 && x >= 'a' && x < 'a' + radix - 10)
|
||||||
|
|| (radix > 10 && x >= 'A' && x < 'A' + radix - 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static double SignedZero(bool sign) {
|
||||||
|
return sign ? -0.0 : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true if 'c' is a decimal digit that is valid for the given radix.
|
||||||
|
//
|
||||||
|
// The function is small and could be inlined, but VS2012 emitted a warning
|
||||||
|
// because it constant-propagated the radix and concluded that the last
|
||||||
|
// condition was always true. By moving it into a separate function the
|
||||||
|
// compiler wouldn't warn anymore.
|
||||||
|
#if _MSC_VER
|
||||||
|
#pragma optimize("",off)
|
||||||
|
static bool IsDecimalDigitForRadix(int c, int radix) {
|
||||||
|
return '0' <= c && c <= '9' && (c - '0') < radix;
|
||||||
|
}
|
||||||
|
#pragma optimize("",on)
|
||||||
|
#else
|
||||||
|
static bool inline IsDecimalDigitForRadix(int c, int radix) {
|
||||||
|
return '0' <= c && c <= '9' && (c - '0') < radix;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Returns true if 'c' is a character digit that is valid for the given radix.
|
||||||
|
// The 'a_character' should be 'a' or 'A'.
|
||||||
|
//
|
||||||
|
// The function is small and could be inlined, but VS2012 emitted a warning
|
||||||
|
// because it constant-propagated the radix and concluded that the first
|
||||||
|
// condition was always false. By moving it into a separate function the
|
||||||
|
// compiler wouldn't warn anymore.
|
||||||
|
static bool IsCharacterDigitForRadix(int c, int radix, char a_character) {
|
||||||
|
return radix > 10 && c >= a_character && c < a_character + radix - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end.
|
||||||
|
template <int radix_log_2, class Iterator>
|
||||||
|
static double RadixStringToIeee(Iterator* current,
|
||||||
|
Iterator end,
|
||||||
|
bool sign,
|
||||||
|
bool allow_trailing_junk,
|
||||||
|
double junk_string_value,
|
||||||
|
bool read_as_double,
|
||||||
|
bool* result_is_junk) {
|
||||||
|
ASSERT(*current != end);
|
||||||
|
|
||||||
|
const int kDoubleSize = Double::kSignificandSize;
|
||||||
|
const int kSingleSize = Single::kSignificandSize;
|
||||||
|
const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize;
|
||||||
|
|
||||||
|
*result_is_junk = true;
|
||||||
|
|
||||||
|
// Skip leading 0s.
|
||||||
|
while (**current == '0') {
|
||||||
|
++(*current);
|
||||||
|
if (*current == end) {
|
||||||
|
*result_is_junk = false;
|
||||||
|
return SignedZero(sign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t number = 0;
|
||||||
|
int exponent = 0;
|
||||||
|
const int radix = (1 << radix_log_2);
|
||||||
|
|
||||||
|
do {
|
||||||
|
int digit;
|
||||||
|
if (IsDecimalDigitForRadix(**current, radix)) {
|
||||||
|
digit = static_cast<char>(**current) - '0';
|
||||||
|
} else if (IsCharacterDigitForRadix(**current, radix, 'a')) {
|
||||||
|
digit = static_cast<char>(**current) - 'a' + 10;
|
||||||
|
} else if (IsCharacterDigitForRadix(**current, radix, 'A')) {
|
||||||
|
digit = static_cast<char>(**current) - 'A' + 10;
|
||||||
|
} else {
|
||||||
|
if (allow_trailing_junk || !AdvanceToNonspace(current, end)) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return junk_string_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
number = number * radix + digit;
|
||||||
|
int overflow = static_cast<int>(number >> kSignificandSize);
|
||||||
|
if (overflow != 0) {
|
||||||
|
// Overflow occurred. Need to determine which direction to round the
|
||||||
|
// result.
|
||||||
|
int overflow_bits_count = 1;
|
||||||
|
while (overflow > 1) {
|
||||||
|
overflow_bits_count++;
|
||||||
|
overflow >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dropped_bits_mask = ((1 << overflow_bits_count) - 1);
|
||||||
|
int dropped_bits = static_cast<int>(number) & dropped_bits_mask;
|
||||||
|
number >>= overflow_bits_count;
|
||||||
|
exponent = overflow_bits_count;
|
||||||
|
|
||||||
|
bool zero_tail = true;
|
||||||
|
for (;;) {
|
||||||
|
++(*current);
|
||||||
|
if (*current == end || !isDigit(**current, radix)) break;
|
||||||
|
zero_tail = zero_tail && **current == '0';
|
||||||
|
exponent += radix_log_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allow_trailing_junk && AdvanceToNonspace(current, end)) {
|
||||||
|
return junk_string_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int middle_value = (1 << (overflow_bits_count - 1));
|
||||||
|
if (dropped_bits > middle_value) {
|
||||||
|
number++; // Rounding up.
|
||||||
|
} else if (dropped_bits == middle_value) {
|
||||||
|
// Rounding to even to consistency with decimals: half-way case rounds
|
||||||
|
// up if significant part is odd and down otherwise.
|
||||||
|
if ((number & 1) != 0 || !zero_tail) {
|
||||||
|
number++; // Rounding up.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rounding up may cause overflow.
|
||||||
|
if ((number & ((int64_t)1 << kSignificandSize)) != 0) {
|
||||||
|
exponent++;
|
||||||
|
number >>= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++(*current);
|
||||||
|
} while (*current != end);
|
||||||
|
|
||||||
|
ASSERT(number < ((int64_t)1 << kSignificandSize));
|
||||||
|
ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number);
|
||||||
|
|
||||||
|
*result_is_junk = false;
|
||||||
|
|
||||||
|
if (exponent == 0) {
|
||||||
|
if (sign) {
|
||||||
|
if (number == 0) return -0.0;
|
||||||
|
number = -number;
|
||||||
|
}
|
||||||
|
return static_cast<double>(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(number != 0);
|
||||||
|
return Double(DiyFp(number, exponent)).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class Iterator>
|
||||||
|
double StringToDoubleConverter::StringToIeee(
|
||||||
|
Iterator input,
|
||||||
|
int length,
|
||||||
|
bool read_as_double,
|
||||||
|
int* processed_characters_count) const {
|
||||||
|
Iterator current = input;
|
||||||
|
Iterator end = input + length;
|
||||||
|
|
||||||
|
*processed_characters_count = 0;
|
||||||
|
|
||||||
|
const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0;
|
||||||
|
const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0;
|
||||||
|
const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0;
|
||||||
|
const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0;
|
||||||
|
|
||||||
|
// To make sure that iterator dereferencing is valid the following
|
||||||
|
// convention is used:
|
||||||
|
// 1. Each '++current' statement is followed by check for equality to 'end'.
|
||||||
|
// 2. If AdvanceToNonspace returned false then current == end.
|
||||||
|
// 3. If 'current' becomes equal to 'end' the function returns or goes to
|
||||||
|
// 'parsing_done'.
|
||||||
|
// 4. 'current' is not dereferenced after the 'parsing_done' label.
|
||||||
|
// 5. Code before 'parsing_done' may rely on 'current != end'.
|
||||||
|
if (current == end) return empty_string_value_;
|
||||||
|
|
||||||
|
if (allow_leading_spaces || allow_trailing_spaces) {
|
||||||
|
if (!AdvanceToNonspace(¤t, end)) {
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
return empty_string_value_;
|
||||||
|
}
|
||||||
|
if (!allow_leading_spaces && (input != current)) {
|
||||||
|
// No leading spaces allowed, but AdvanceToNonspace moved forward.
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The longest form of simplified number is: "-<significant digits>.1eXXX\0".
|
||||||
|
const int kBufferSize = kMaxSignificantDigits + 10;
|
||||||
|
char buffer[kBufferSize]; // NOLINT: size is known at compile time.
|
||||||
|
int buffer_pos = 0;
|
||||||
|
|
||||||
|
// Exponent will be adjusted if insignificant digits of the integer part
|
||||||
|
// or insignificant leading zeros of the fractional part are dropped.
|
||||||
|
int exponent = 0;
|
||||||
|
int significant_digits = 0;
|
||||||
|
int insignificant_digits = 0;
|
||||||
|
bool nonzero_digit_dropped = false;
|
||||||
|
|
||||||
|
bool sign = false;
|
||||||
|
|
||||||
|
if (*current == '+' || *current == '-') {
|
||||||
|
sign = (*current == '-');
|
||||||
|
++current;
|
||||||
|
Iterator next_non_space = current;
|
||||||
|
// Skip following spaces (if allowed).
|
||||||
|
if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_;
|
||||||
|
if (!allow_spaces_after_sign && (current != next_non_space)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
current = next_non_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (infinity_symbol_ != NULL) {
|
||||||
|
if (*current == infinity_symbol_[0]) {
|
||||||
|
if (!ConsumeSubString(¤t, end, infinity_symbol_)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(buffer_pos == 0);
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
return sign ? -Double::Infinity() : Double::Infinity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nan_symbol_ != NULL) {
|
||||||
|
if (*current == nan_symbol_[0]) {
|
||||||
|
if (!ConsumeSubString(¤t, end, nan_symbol_)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(buffer_pos == 0);
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
return sign ? -Double::NaN() : Double::NaN();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool leading_zero = false;
|
||||||
|
if (*current == '0') {
|
||||||
|
++current;
|
||||||
|
if (current == end) {
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
return SignedZero(sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
leading_zero = true;
|
||||||
|
|
||||||
|
// It could be hexadecimal value.
|
||||||
|
if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
|
||||||
|
++current;
|
||||||
|
if (current == end || !isDigit(*current, 16)) {
|
||||||
|
return junk_string_value_; // "0x".
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result_is_junk;
|
||||||
|
double result = RadixStringToIeee<4>(¤t,
|
||||||
|
end,
|
||||||
|
sign,
|
||||||
|
allow_trailing_junk,
|
||||||
|
junk_string_value_,
|
||||||
|
read_as_double,
|
||||||
|
&result_is_junk);
|
||||||
|
if (!result_is_junk) {
|
||||||
|
if (allow_trailing_spaces) AdvanceToNonspace(¤t, end);
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore leading zeros in the integer part.
|
||||||
|
while (*current == '0') {
|
||||||
|
++current;
|
||||||
|
if (current == end) {
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
return SignedZero(sign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0;
|
||||||
|
|
||||||
|
// Copy significant digits of the integer part (if any) to the buffer.
|
||||||
|
while (*current >= '0' && *current <= '9') {
|
||||||
|
if (significant_digits < kMaxSignificantDigits) {
|
||||||
|
ASSERT(buffer_pos < kBufferSize);
|
||||||
|
buffer[buffer_pos++] = static_cast<char>(*current);
|
||||||
|
significant_digits++;
|
||||||
|
// Will later check if it's an octal in the buffer.
|
||||||
|
} else {
|
||||||
|
insignificant_digits++; // Move the digit into the exponential part.
|
||||||
|
nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
|
||||||
|
}
|
||||||
|
octal = octal && *current < '8';
|
||||||
|
++current;
|
||||||
|
if (current == end) goto parsing_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (significant_digits == 0) {
|
||||||
|
octal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*current == '.') {
|
||||||
|
if (octal && !allow_trailing_junk) return junk_string_value_;
|
||||||
|
if (octal) goto parsing_done;
|
||||||
|
|
||||||
|
++current;
|
||||||
|
if (current == end) {
|
||||||
|
if (significant_digits == 0 && !leading_zero) {
|
||||||
|
return junk_string_value_;
|
||||||
|
} else {
|
||||||
|
goto parsing_done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (significant_digits == 0) {
|
||||||
|
// octal = false;
|
||||||
|
// Integer part consists of 0 or is absent. Significant digits start after
|
||||||
|
// leading zeros (if any).
|
||||||
|
while (*current == '0') {
|
||||||
|
++current;
|
||||||
|
if (current == end) {
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
return SignedZero(sign);
|
||||||
|
}
|
||||||
|
exponent--; // Move this 0 into the exponent.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is a fractional part.
|
||||||
|
// We don't emit a '.', but adjust the exponent instead.
|
||||||
|
while (*current >= '0' && *current <= '9') {
|
||||||
|
if (significant_digits < kMaxSignificantDigits) {
|
||||||
|
ASSERT(buffer_pos < kBufferSize);
|
||||||
|
buffer[buffer_pos++] = static_cast<char>(*current);
|
||||||
|
significant_digits++;
|
||||||
|
exponent--;
|
||||||
|
} else {
|
||||||
|
// Ignore insignificant digits in the fractional part.
|
||||||
|
nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
|
||||||
|
}
|
||||||
|
++current;
|
||||||
|
if (current == end) goto parsing_done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!leading_zero && exponent == 0 && significant_digits == 0) {
|
||||||
|
// If leading_zeros is true then the string contains zeros.
|
||||||
|
// If exponent < 0 then string was [+-]\.0*...
|
||||||
|
// If significant_digits != 0 the string is not equal to 0.
|
||||||
|
// Otherwise there are no digits in the string.
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse exponential part.
|
||||||
|
if (*current == 'e' || *current == 'E') {
|
||||||
|
if (octal && !allow_trailing_junk) return junk_string_value_;
|
||||||
|
if (octal) goto parsing_done;
|
||||||
|
++current;
|
||||||
|
if (current == end) {
|
||||||
|
if (allow_trailing_junk) {
|
||||||
|
goto parsing_done;
|
||||||
|
} else {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char exponen_sign = '+';
|
||||||
|
if (*current == '+' || *current == '-') {
|
||||||
|
exponen_sign = static_cast<char>(*current);
|
||||||
|
++current;
|
||||||
|
if (current == end) {
|
||||||
|
if (allow_trailing_junk) {
|
||||||
|
goto parsing_done;
|
||||||
|
} else {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current == end || *current < '0' || *current > '9') {
|
||||||
|
if (allow_trailing_junk) {
|
||||||
|
goto parsing_done;
|
||||||
|
} else {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int max_exponent = INT_MAX / 2;
|
||||||
|
ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2);
|
||||||
|
int num = 0;
|
||||||
|
do {
|
||||||
|
// Check overflow.
|
||||||
|
int digit = *current - '0';
|
||||||
|
if (num >= max_exponent / 10
|
||||||
|
&& !(num == max_exponent / 10 && digit <= max_exponent % 10)) {
|
||||||
|
num = max_exponent;
|
||||||
|
} else {
|
||||||
|
num = num * 10 + digit;
|
||||||
|
}
|
||||||
|
++current;
|
||||||
|
} while (current != end && *current >= '0' && *current <= '9');
|
||||||
|
|
||||||
|
exponent += (exponen_sign == '-' ? -num : num);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) {
|
||||||
|
return junk_string_value_;
|
||||||
|
}
|
||||||
|
if (allow_trailing_spaces) {
|
||||||
|
AdvanceToNonspace(¤t, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
parsing_done:
|
||||||
|
exponent += insignificant_digits;
|
||||||
|
|
||||||
|
if (octal) {
|
||||||
|
double result;
|
||||||
|
bool result_is_junk;
|
||||||
|
char* start = buffer;
|
||||||
|
result = RadixStringToIeee<3>(&start,
|
||||||
|
buffer + buffer_pos,
|
||||||
|
sign,
|
||||||
|
allow_trailing_junk,
|
||||||
|
junk_string_value_,
|
||||||
|
read_as_double,
|
||||||
|
&result_is_junk);
|
||||||
|
ASSERT(!result_is_junk);
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nonzero_digit_dropped) {
|
||||||
|
buffer[buffer_pos++] = '1';
|
||||||
|
exponent--;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(buffer_pos < kBufferSize);
|
||||||
|
buffer[buffer_pos] = '\0';
|
||||||
|
|
||||||
|
double converted;
|
||||||
|
if (read_as_double) {
|
||||||
|
converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
|
||||||
|
} else {
|
||||||
|
converted = Strtof(Vector<const char>(buffer, buffer_pos), exponent);
|
||||||
|
}
|
||||||
|
*processed_characters_count = static_cast<int>(current - input);
|
||||||
|
return sign? -converted: converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double StringToDoubleConverter::StringToDouble(
|
||||||
|
const char* buffer,
|
||||||
|
int length,
|
||||||
|
int* processed_characters_count) const {
|
||||||
|
return StringToIeee(buffer, length, true, processed_characters_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double StringToDoubleConverter::StringToDouble(
|
||||||
|
const uc16* buffer,
|
||||||
|
int length,
|
||||||
|
int* processed_characters_count) const {
|
||||||
|
return StringToIeee(buffer, length, true, processed_characters_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float StringToDoubleConverter::StringToFloat(
|
||||||
|
const char* buffer,
|
||||||
|
int length,
|
||||||
|
int* processed_characters_count) const {
|
||||||
|
return static_cast<float>(StringToIeee(buffer, length, false,
|
||||||
|
processed_characters_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float StringToDoubleConverter::StringToFloat(
|
||||||
|
const uc16* buffer,
|
||||||
|
int length,
|
||||||
|
int* processed_characters_count) const {
|
||||||
|
return static_cast<float>(StringToIeee(buffer, length, false,
|
||||||
|
processed_characters_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
@ -0,0 +1,543 @@
|
|||||||
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_
|
||||||
|
#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
class DoubleToStringConverter {
|
||||||
|
public:
|
||||||
|
// When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint
|
||||||
|
// or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the
|
||||||
|
// function returns false.
|
||||||
|
static const int kMaxFixedDigitsBeforePoint = 60;
|
||||||
|
static const int kMaxFixedDigitsAfterPoint = 60;
|
||||||
|
|
||||||
|
// When calling ToExponential with a requested_digits
|
||||||
|
// parameter > kMaxExponentialDigits then the function returns false.
|
||||||
|
static const int kMaxExponentialDigits = 120;
|
||||||
|
|
||||||
|
// When calling ToPrecision with a requested_digits
|
||||||
|
// parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits
|
||||||
|
// then the function returns false.
|
||||||
|
static const int kMinPrecisionDigits = 1;
|
||||||
|
static const int kMaxPrecisionDigits = 120;
|
||||||
|
|
||||||
|
enum Flags {
|
||||||
|
NO_FLAGS = 0,
|
||||||
|
EMIT_POSITIVE_EXPONENT_SIGN = 1,
|
||||||
|
EMIT_TRAILING_DECIMAL_POINT = 2,
|
||||||
|
EMIT_TRAILING_ZERO_AFTER_POINT = 4,
|
||||||
|
UNIQUE_ZERO = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flags should be a bit-or combination of the possible Flags-enum.
|
||||||
|
// - NO_FLAGS: no special flags.
|
||||||
|
// - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent
|
||||||
|
// form, emits a '+' for positive exponents. Example: 1.2e+2.
|
||||||
|
// - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is
|
||||||
|
// converted into decimal format then a trailing decimal point is appended.
|
||||||
|
// Example: 2345.0 is converted to "2345.".
|
||||||
|
// - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point
|
||||||
|
// emits a trailing '0'-character. This flag requires the
|
||||||
|
// EXMIT_TRAILING_DECIMAL_POINT flag.
|
||||||
|
// Example: 2345.0 is converted to "2345.0".
|
||||||
|
// - UNIQUE_ZERO: "-0.0" is converted to "0.0".
|
||||||
|
//
|
||||||
|
// Infinity symbol and nan_symbol provide the string representation for these
|
||||||
|
// special values. If the string is NULL and the special value is encountered
|
||||||
|
// then the conversion functions return false.
|
||||||
|
//
|
||||||
|
// The exponent_character is used in exponential representations. It is
|
||||||
|
// usually 'e' or 'E'.
|
||||||
|
//
|
||||||
|
// When converting to the shortest representation the converter will
|
||||||
|
// represent input numbers in decimal format if they are in the interval
|
||||||
|
// [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[
|
||||||
|
// (lower boundary included, greater boundary excluded).
|
||||||
|
// Example: with decimal_in_shortest_low = -6 and
|
||||||
|
// decimal_in_shortest_high = 21:
|
||||||
|
// ToShortest(0.000001) -> "0.000001"
|
||||||
|
// ToShortest(0.0000001) -> "1e-7"
|
||||||
|
// ToShortest(111111111111111111111.0) -> "111111111111111110000"
|
||||||
|
// ToShortest(100000000000000000000.0) -> "100000000000000000000"
|
||||||
|
// ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21"
|
||||||
|
//
|
||||||
|
// When converting to precision mode the converter may add
|
||||||
|
// max_leading_padding_zeroes before returning the number in exponential
|
||||||
|
// format.
|
||||||
|
// Example with max_leading_padding_zeroes_in_precision_mode = 6.
|
||||||
|
// ToPrecision(0.0000012345, 2) -> "0.0000012"
|
||||||
|
// ToPrecision(0.00000012345, 2) -> "1.2e-7"
|
||||||
|
// Similarily the converter may add up to
|
||||||
|
// max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid
|
||||||
|
// returning an exponential representation. A zero added by the
|
||||||
|
// EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit.
|
||||||
|
// Examples for max_trailing_padding_zeroes_in_precision_mode = 1:
|
||||||
|
// ToPrecision(230.0, 2) -> "230"
|
||||||
|
// ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT.
|
||||||
|
// ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT.
|
||||||
|
DoubleToStringConverter(int flags,
|
||||||
|
const char* infinity_symbol,
|
||||||
|
const char* nan_symbol,
|
||||||
|
char exponent_character,
|
||||||
|
int decimal_in_shortest_low,
|
||||||
|
int decimal_in_shortest_high,
|
||||||
|
int max_leading_padding_zeroes_in_precision_mode,
|
||||||
|
int max_trailing_padding_zeroes_in_precision_mode)
|
||||||
|
: flags_(flags),
|
||||||
|
infinity_symbol_(infinity_symbol),
|
||||||
|
nan_symbol_(nan_symbol),
|
||||||
|
exponent_character_(exponent_character),
|
||||||
|
decimal_in_shortest_low_(decimal_in_shortest_low),
|
||||||
|
decimal_in_shortest_high_(decimal_in_shortest_high),
|
||||||
|
max_leading_padding_zeroes_in_precision_mode_(
|
||||||
|
max_leading_padding_zeroes_in_precision_mode),
|
||||||
|
max_trailing_padding_zeroes_in_precision_mode_(
|
||||||
|
max_trailing_padding_zeroes_in_precision_mode) {
|
||||||
|
// When 'trailing zero after the point' is set, then 'trailing point'
|
||||||
|
// must be set too.
|
||||||
|
ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) ||
|
||||||
|
!((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a converter following the EcmaScript specification.
|
||||||
|
static const DoubleToStringConverter& EcmaScriptConverter();
|
||||||
|
|
||||||
|
// Computes the shortest string of digits that correctly represent the input
|
||||||
|
// number. Depending on decimal_in_shortest_low and decimal_in_shortest_high
|
||||||
|
// (see constructor) it then either returns a decimal representation, or an
|
||||||
|
// exponential representation.
|
||||||
|
// Example with decimal_in_shortest_low = -6,
|
||||||
|
// decimal_in_shortest_high = 21,
|
||||||
|
// EMIT_POSITIVE_EXPONENT_SIGN activated, and
|
||||||
|
// EMIT_TRAILING_DECIMAL_POINT deactived:
|
||||||
|
// ToShortest(0.000001) -> "0.000001"
|
||||||
|
// ToShortest(0.0000001) -> "1e-7"
|
||||||
|
// ToShortest(111111111111111111111.0) -> "111111111111111110000"
|
||||||
|
// ToShortest(100000000000000000000.0) -> "100000000000000000000"
|
||||||
|
// ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21"
|
||||||
|
//
|
||||||
|
// Note: the conversion may round the output if the returned string
|
||||||
|
// is accurate enough to uniquely identify the input-number.
|
||||||
|
// For example the most precise representation of the double 9e59 equals
|
||||||
|
// "899999999999999918767229449717619953810131273674690656206848", but
|
||||||
|
// the converter will return the shorter (but still correct) "9e59".
|
||||||
|
//
|
||||||
|
// Returns true if the conversion succeeds. The conversion always succeeds
|
||||||
|
// except when the input value is special and no infinity_symbol or
|
||||||
|
// nan_symbol has been given to the constructor.
|
||||||
|
bool ToShortest(double value, StringBuilder* result_builder) const {
|
||||||
|
return ToShortestIeeeNumber(value, result_builder, SHORTEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as ToShortest, but for single-precision floats.
|
||||||
|
bool ToShortestSingle(float value, StringBuilder* result_builder) const {
|
||||||
|
return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Computes a decimal representation with a fixed number of digits after the
|
||||||
|
// decimal point. The last emitted digit is rounded.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// ToFixed(3.12, 1) -> "3.1"
|
||||||
|
// ToFixed(3.1415, 3) -> "3.142"
|
||||||
|
// ToFixed(1234.56789, 4) -> "1234.5679"
|
||||||
|
// ToFixed(1.23, 5) -> "1.23000"
|
||||||
|
// ToFixed(0.1, 4) -> "0.1000"
|
||||||
|
// ToFixed(1e30, 2) -> "1000000000000000019884624838656.00"
|
||||||
|
// ToFixed(0.1, 30) -> "0.100000000000000005551115123126"
|
||||||
|
// ToFixed(0.1, 17) -> "0.10000000000000001"
|
||||||
|
//
|
||||||
|
// If requested_digits equals 0, then the tail of the result depends on
|
||||||
|
// the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT.
|
||||||
|
// Examples, for requested_digits == 0,
|
||||||
|
// let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be
|
||||||
|
// - false and false: then 123.45 -> 123
|
||||||
|
// 0.678 -> 1
|
||||||
|
// - true and false: then 123.45 -> 123.
|
||||||
|
// 0.678 -> 1.
|
||||||
|
// - true and true: then 123.45 -> 123.0
|
||||||
|
// 0.678 -> 1.0
|
||||||
|
//
|
||||||
|
// Returns true if the conversion succeeds. The conversion always succeeds
|
||||||
|
// except for the following cases:
|
||||||
|
// - the input value is special and no infinity_symbol or nan_symbol has
|
||||||
|
// been provided to the constructor,
|
||||||
|
// - 'value' > 10^kMaxFixedDigitsBeforePoint, or
|
||||||
|
// - 'requested_digits' > kMaxFixedDigitsAfterPoint.
|
||||||
|
// The last two conditions imply that the result will never contain more than
|
||||||
|
// 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters
|
||||||
|
// (one additional character for the sign, and one for the decimal point).
|
||||||
|
bool ToFixed(double value,
|
||||||
|
int requested_digits,
|
||||||
|
StringBuilder* result_builder) const;
|
||||||
|
|
||||||
|
// Computes a representation in exponential format with requested_digits
|
||||||
|
// after the decimal point. The last emitted digit is rounded.
|
||||||
|
// If requested_digits equals -1, then the shortest exponential representation
|
||||||
|
// is computed.
|
||||||
|
//
|
||||||
|
// Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and
|
||||||
|
// exponent_character set to 'e'.
|
||||||
|
// ToExponential(3.12, 1) -> "3.1e0"
|
||||||
|
// ToExponential(5.0, 3) -> "5.000e0"
|
||||||
|
// ToExponential(0.001, 2) -> "1.00e-3"
|
||||||
|
// ToExponential(3.1415, -1) -> "3.1415e0"
|
||||||
|
// ToExponential(3.1415, 4) -> "3.1415e0"
|
||||||
|
// ToExponential(3.1415, 3) -> "3.142e0"
|
||||||
|
// ToExponential(123456789000000, 3) -> "1.235e14"
|
||||||
|
// ToExponential(1000000000000000019884624838656.0, -1) -> "1e30"
|
||||||
|
// ToExponential(1000000000000000019884624838656.0, 32) ->
|
||||||
|
// "1.00000000000000001988462483865600e30"
|
||||||
|
// ToExponential(1234, 0) -> "1e3"
|
||||||
|
//
|
||||||
|
// Returns true if the conversion succeeds. The conversion always succeeds
|
||||||
|
// except for the following cases:
|
||||||
|
// - the input value is special and no infinity_symbol or nan_symbol has
|
||||||
|
// been provided to the constructor,
|
||||||
|
// - 'requested_digits' > kMaxExponentialDigits.
|
||||||
|
// The last condition implies that the result will never contain more than
|
||||||
|
// kMaxExponentialDigits + 8 characters (the sign, the digit before the
|
||||||
|
// decimal point, the decimal point, the exponent character, the
|
||||||
|
// exponent's sign, and at most 3 exponent digits).
|
||||||
|
bool ToExponential(double value,
|
||||||
|
int requested_digits,
|
||||||
|
StringBuilder* result_builder) const;
|
||||||
|
|
||||||
|
// Computes 'precision' leading digits of the given 'value' and returns them
|
||||||
|
// either in exponential or decimal format, depending on
|
||||||
|
// max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the
|
||||||
|
// constructor).
|
||||||
|
// The last computed digit is rounded.
|
||||||
|
//
|
||||||
|
// Example with max_leading_padding_zeroes_in_precision_mode = 6.
|
||||||
|
// ToPrecision(0.0000012345, 2) -> "0.0000012"
|
||||||
|
// ToPrecision(0.00000012345, 2) -> "1.2e-7"
|
||||||
|
// Similarily the converter may add up to
|
||||||
|
// max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid
|
||||||
|
// returning an exponential representation. A zero added by the
|
||||||
|
// EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit.
|
||||||
|
// Examples for max_trailing_padding_zeroes_in_precision_mode = 1:
|
||||||
|
// ToPrecision(230.0, 2) -> "230"
|
||||||
|
// ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT.
|
||||||
|
// ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT.
|
||||||
|
// Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no
|
||||||
|
// EMIT_TRAILING_ZERO_AFTER_POINT:
|
||||||
|
// ToPrecision(123450.0, 6) -> "123450"
|
||||||
|
// ToPrecision(123450.0, 5) -> "123450"
|
||||||
|
// ToPrecision(123450.0, 4) -> "123500"
|
||||||
|
// ToPrecision(123450.0, 3) -> "123000"
|
||||||
|
// ToPrecision(123450.0, 2) -> "1.2e5"
|
||||||
|
//
|
||||||
|
// Returns true if the conversion succeeds. The conversion always succeeds
|
||||||
|
// except for the following cases:
|
||||||
|
// - the input value is special and no infinity_symbol or nan_symbol has
|
||||||
|
// been provided to the constructor,
|
||||||
|
// - precision < kMinPericisionDigits
|
||||||
|
// - precision > kMaxPrecisionDigits
|
||||||
|
// The last condition implies that the result will never contain more than
|
||||||
|
// kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the
|
||||||
|
// exponent character, the exponent's sign, and at most 3 exponent digits).
|
||||||
|
bool ToPrecision(double value,
|
||||||
|
int precision,
|
||||||
|
StringBuilder* result_builder) const;
|
||||||
|
|
||||||
|
enum DtoaMode {
|
||||||
|
// Produce the shortest correct representation.
|
||||||
|
// For example the output of 0.299999999999999988897 is (the less accurate
|
||||||
|
// but correct) 0.3.
|
||||||
|
SHORTEST,
|
||||||
|
// Same as SHORTEST, but for single-precision floats.
|
||||||
|
SHORTEST_SINGLE,
|
||||||
|
// Produce a fixed number of digits after the decimal point.
|
||||||
|
// For instance fixed(0.1, 4) becomes 0.1000
|
||||||
|
// If the input number is big, the output will be big.
|
||||||
|
FIXED,
|
||||||
|
// Fixed number of digits (independent of the decimal point).
|
||||||
|
PRECISION
|
||||||
|
};
|
||||||
|
|
||||||
|
// The maximal number of digits that are needed to emit a double in base 10.
|
||||||
|
// A higher precision can be achieved by using more digits, but the shortest
|
||||||
|
// accurate representation of any double will never use more digits than
|
||||||
|
// kBase10MaximalLength.
|
||||||
|
// Note that DoubleToAscii null-terminates its input. So the given buffer
|
||||||
|
// should be at least kBase10MaximalLength + 1 characters long.
|
||||||
|
static const int kBase10MaximalLength = 17;
|
||||||
|
|
||||||
|
// Converts the given double 'v' to ascii. 'v' must not be NaN, +Infinity, or
|
||||||
|
// -Infinity. In SHORTEST_SINGLE-mode this restriction also applies to 'v'
|
||||||
|
// after it has been casted to a single-precision float. That is, in this
|
||||||
|
// mode static_cast<float>(v) must not be NaN, +Infinity or -Infinity.
|
||||||
|
//
|
||||||
|
// The result should be interpreted as buffer * 10^(point-length).
|
||||||
|
//
|
||||||
|
// The output depends on the given mode:
|
||||||
|
// - SHORTEST: produce the least amount of digits for which the internal
|
||||||
|
// identity requirement is still satisfied. If the digits are printed
|
||||||
|
// (together with the correct exponent) then reading this number will give
|
||||||
|
// 'v' again. The buffer will choose the representation that is closest to
|
||||||
|
// 'v'. If there are two at the same distance, than the one farther away
|
||||||
|
// from 0 is chosen (halfway cases - ending with 5 - are rounded up).
|
||||||
|
// In this mode the 'requested_digits' parameter is ignored.
|
||||||
|
// - SHORTEST_SINGLE: same as SHORTEST but with single-precision.
|
||||||
|
// - FIXED: produces digits necessary to print a given number with
|
||||||
|
// 'requested_digits' digits after the decimal point. The produced digits
|
||||||
|
// might be too short in which case the caller has to fill the remainder
|
||||||
|
// with '0's.
|
||||||
|
// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
|
||||||
|
// Halfway cases are rounded towards +/-Infinity (away from 0). The call
|
||||||
|
// toFixed(0.15, 2) thus returns buffer="2", point=0.
|
||||||
|
// The returned buffer may contain digits that would be truncated from the
|
||||||
|
// shortest representation of the input.
|
||||||
|
// - PRECISION: produces 'requested_digits' where the first digit is not '0'.
|
||||||
|
// Even though the length of produced digits usually equals
|
||||||
|
// 'requested_digits', the function is allowed to return fewer digits, in
|
||||||
|
// which case the caller has to fill the missing digits with '0's.
|
||||||
|
// Halfway cases are again rounded away from 0.
|
||||||
|
// DoubleToAscii expects the given buffer to be big enough to hold all
|
||||||
|
// digits and a terminating null-character. In SHORTEST-mode it expects a
|
||||||
|
// buffer of at least kBase10MaximalLength + 1. In all other modes the
|
||||||
|
// requested_digits parameter and the padding-zeroes limit the size of the
|
||||||
|
// output. Don't forget the decimal point, the exponent character and the
|
||||||
|
// terminating null-character when computing the maximal output size.
|
||||||
|
// The given length is only used in debug mode to ensure the buffer is big
|
||||||
|
// enough.
|
||||||
|
static void DoubleToAscii(double v,
|
||||||
|
DtoaMode mode,
|
||||||
|
int requested_digits,
|
||||||
|
char* buffer,
|
||||||
|
int buffer_length,
|
||||||
|
bool* sign,
|
||||||
|
int* length,
|
||||||
|
int* point);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Implementation for ToShortest and ToShortestSingle.
|
||||||
|
bool ToShortestIeeeNumber(double value,
|
||||||
|
StringBuilder* result_builder,
|
||||||
|
DtoaMode mode) const;
|
||||||
|
|
||||||
|
// If the value is a special value (NaN or Infinity) constructs the
|
||||||
|
// corresponding string using the configured infinity/nan-symbol.
|
||||||
|
// If either of them is NULL or the value is not special then the
|
||||||
|
// function returns false.
|
||||||
|
bool HandleSpecialValues(double value, StringBuilder* result_builder) const;
|
||||||
|
// Constructs an exponential representation (i.e. 1.234e56).
|
||||||
|
// The given exponent assumes a decimal point after the first decimal digit.
|
||||||
|
void CreateExponentialRepresentation(const char* decimal_digits,
|
||||||
|
int length,
|
||||||
|
int exponent,
|
||||||
|
StringBuilder* result_builder) const;
|
||||||
|
// Creates a decimal representation (i.e 1234.5678).
|
||||||
|
void CreateDecimalRepresentation(const char* decimal_digits,
|
||||||
|
int length,
|
||||||
|
int decimal_point,
|
||||||
|
int digits_after_point,
|
||||||
|
StringBuilder* result_builder) const;
|
||||||
|
|
||||||
|
const int flags_;
|
||||||
|
const char* const infinity_symbol_;
|
||||||
|
const char* const nan_symbol_;
|
||||||
|
const char exponent_character_;
|
||||||
|
const int decimal_in_shortest_low_;
|
||||||
|
const int decimal_in_shortest_high_;
|
||||||
|
const int max_leading_padding_zeroes_in_precision_mode_;
|
||||||
|
const int max_trailing_padding_zeroes_in_precision_mode_;
|
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class StringToDoubleConverter {
|
||||||
|
public:
|
||||||
|
// Enumeration for allowing octals and ignoring junk when converting
|
||||||
|
// strings to numbers.
|
||||||
|
enum Flags {
|
||||||
|
NO_FLAGS = 0,
|
||||||
|
ALLOW_HEX = 1,
|
||||||
|
ALLOW_OCTALS = 2,
|
||||||
|
ALLOW_TRAILING_JUNK = 4,
|
||||||
|
ALLOW_LEADING_SPACES = 8,
|
||||||
|
ALLOW_TRAILING_SPACES = 16,
|
||||||
|
ALLOW_SPACES_AFTER_SIGN = 32
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flags should be a bit-or combination of the possible Flags-enum.
|
||||||
|
// - NO_FLAGS: no special flags.
|
||||||
|
// - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers.
|
||||||
|
// Ex: StringToDouble("0x1234") -> 4660.0
|
||||||
|
// In StringToDouble("0x1234.56") the characters ".56" are trailing
|
||||||
|
// junk. The result of the call is hence dependent on
|
||||||
|
// the ALLOW_TRAILING_JUNK flag and/or the junk value.
|
||||||
|
// With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK,
|
||||||
|
// the string will not be parsed as "0" followed by junk.
|
||||||
|
//
|
||||||
|
// - ALLOW_OCTALS: recognizes the prefix "0" for octals:
|
||||||
|
// If a sequence of octal digits starts with '0', then the number is
|
||||||
|
// read as octal integer. Octal numbers may only be integers.
|
||||||
|
// Ex: StringToDouble("01234") -> 668.0
|
||||||
|
// StringToDouble("012349") -> 12349.0 // Not a sequence of octal
|
||||||
|
// // digits.
|
||||||
|
// In StringToDouble("01234.56") the characters ".56" are trailing
|
||||||
|
// junk. The result of the call is hence dependent on
|
||||||
|
// the ALLOW_TRAILING_JUNK flag and/or the junk value.
|
||||||
|
// In StringToDouble("01234e56") the characters "e56" are trailing
|
||||||
|
// junk, too.
|
||||||
|
// - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of
|
||||||
|
// a double literal.
|
||||||
|
// - ALLOW_LEADING_SPACES: skip over leading whitespace, including spaces,
|
||||||
|
// new-lines, and tabs.
|
||||||
|
// - ALLOW_TRAILING_SPACES: ignore trailing whitespace.
|
||||||
|
// - ALLOW_SPACES_AFTER_SIGN: ignore whitespace after the sign.
|
||||||
|
// Ex: StringToDouble("- 123.2") -> -123.2.
|
||||||
|
// StringToDouble("+ 123.2") -> 123.2
|
||||||
|
//
|
||||||
|
// empty_string_value is returned when an empty string is given as input.
|
||||||
|
// If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string
|
||||||
|
// containing only spaces is converted to the 'empty_string_value', too.
|
||||||
|
//
|
||||||
|
// junk_string_value is returned when
|
||||||
|
// a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not
|
||||||
|
// part of a double-literal) is found.
|
||||||
|
// b) ALLOW_TRAILING_JUNK is set, but the string does not start with a
|
||||||
|
// double literal.
|
||||||
|
//
|
||||||
|
// infinity_symbol and nan_symbol are strings that are used to detect
|
||||||
|
// inputs that represent infinity and NaN. They can be null, in which case
|
||||||
|
// they are ignored.
|
||||||
|
// The conversion routine first reads any possible signs. Then it compares the
|
||||||
|
// following character of the input-string with the first character of
|
||||||
|
// the infinity, and nan-symbol. If either matches, the function assumes, that
|
||||||
|
// a match has been found, and expects the following input characters to match
|
||||||
|
// the remaining characters of the special-value symbol.
|
||||||
|
// This means that the following restrictions apply to special-value symbols:
|
||||||
|
// - they must not start with signs ('+', or '-'),
|
||||||
|
// - they must not have the same first character.
|
||||||
|
// - they must not start with digits.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// flags = ALLOW_HEX | ALLOW_TRAILING_JUNK,
|
||||||
|
// empty_string_value = 0.0,
|
||||||
|
// junk_string_value = NaN,
|
||||||
|
// infinity_symbol = "infinity",
|
||||||
|
// nan_symbol = "nan":
|
||||||
|
// StringToDouble("0x1234") -> 4660.0.
|
||||||
|
// StringToDouble("0x1234K") -> 4660.0.
|
||||||
|
// StringToDouble("") -> 0.0 // empty_string_value.
|
||||||
|
// StringToDouble(" ") -> NaN // junk_string_value.
|
||||||
|
// StringToDouble(" 1") -> NaN // junk_string_value.
|
||||||
|
// StringToDouble("0x") -> NaN // junk_string_value.
|
||||||
|
// StringToDouble("-123.45") -> -123.45.
|
||||||
|
// StringToDouble("--123.45") -> NaN // junk_string_value.
|
||||||
|
// StringToDouble("123e45") -> 123e45.
|
||||||
|
// StringToDouble("123E45") -> 123e45.
|
||||||
|
// StringToDouble("123e+45") -> 123e45.
|
||||||
|
// StringToDouble("123E-45") -> 123e-45.
|
||||||
|
// StringToDouble("123e") -> 123.0 // trailing junk ignored.
|
||||||
|
// StringToDouble("123e-") -> 123.0 // trailing junk ignored.
|
||||||
|
// StringToDouble("+NaN") -> NaN // NaN string literal.
|
||||||
|
// StringToDouble("-infinity") -> -inf. // infinity literal.
|
||||||
|
// StringToDouble("Infinity") -> NaN // junk_string_value.
|
||||||
|
//
|
||||||
|
// flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES,
|
||||||
|
// empty_string_value = 0.0,
|
||||||
|
// junk_string_value = NaN,
|
||||||
|
// infinity_symbol = NULL,
|
||||||
|
// nan_symbol = NULL:
|
||||||
|
// StringToDouble("0x1234") -> NaN // junk_string_value.
|
||||||
|
// StringToDouble("01234") -> 668.0.
|
||||||
|
// StringToDouble("") -> 0.0 // empty_string_value.
|
||||||
|
// StringToDouble(" ") -> 0.0 // empty_string_value.
|
||||||
|
// StringToDouble(" 1") -> 1.0
|
||||||
|
// StringToDouble("0x") -> NaN // junk_string_value.
|
||||||
|
// StringToDouble("0123e45") -> NaN // junk_string_value.
|
||||||
|
// StringToDouble("01239E45") -> 1239e45.
|
||||||
|
// StringToDouble("-infinity") -> NaN // junk_string_value.
|
||||||
|
// StringToDouble("NaN") -> NaN // junk_string_value.
|
||||||
|
StringToDoubleConverter(int flags,
|
||||||
|
double empty_string_value,
|
||||||
|
double junk_string_value,
|
||||||
|
const char* infinity_symbol,
|
||||||
|
const char* nan_symbol)
|
||||||
|
: flags_(flags),
|
||||||
|
empty_string_value_(empty_string_value),
|
||||||
|
junk_string_value_(junk_string_value),
|
||||||
|
infinity_symbol_(infinity_symbol),
|
||||||
|
nan_symbol_(nan_symbol) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performs the conversion.
|
||||||
|
// The output parameter 'processed_characters_count' is set to the number
|
||||||
|
// of characters that have been processed to read the number.
|
||||||
|
// Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included
|
||||||
|
// in the 'processed_characters_count'. Trailing junk is never included.
|
||||||
|
double StringToDouble(const char* buffer,
|
||||||
|
int length,
|
||||||
|
int* processed_characters_count) const;
|
||||||
|
|
||||||
|
// Same as StringToDouble above but for 16 bit characters.
|
||||||
|
double StringToDouble(const uc16* buffer,
|
||||||
|
int length,
|
||||||
|
int* processed_characters_count) const;
|
||||||
|
|
||||||
|
// Same as StringToDouble but reads a float.
|
||||||
|
// Note that this is not equivalent to static_cast<float>(StringToDouble(...))
|
||||||
|
// due to potential double-rounding.
|
||||||
|
float StringToFloat(const char* buffer,
|
||||||
|
int length,
|
||||||
|
int* processed_characters_count) const;
|
||||||
|
|
||||||
|
// Same as StringToFloat above but for 16 bit characters.
|
||||||
|
float StringToFloat(const uc16* buffer,
|
||||||
|
int length,
|
||||||
|
int* processed_characters_count) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int flags_;
|
||||||
|
const double empty_string_value_;
|
||||||
|
const double junk_string_value_;
|
||||||
|
const char* const infinity_symbol_;
|
||||||
|
const char* const nan_symbol_;
|
||||||
|
|
||||||
|
template <class Iterator>
|
||||||
|
double StringToIeee(Iterator start_pointer,
|
||||||
|
int length,
|
||||||
|
bool read_as_double,
|
||||||
|
int* processed_characters_count) const;
|
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_
|
665
contrib/libdouble-conversion/double-conversion/fast-dtoa.cc
Normal file
665
contrib/libdouble-conversion/double-conversion/fast-dtoa.cc
Normal file
@ -0,0 +1,665 @@
|
|||||||
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "fast-dtoa.h"
|
||||||
|
|
||||||
|
#include "cached-powers.h"
|
||||||
|
#include "diy-fp.h"
|
||||||
|
#include "ieee.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
// The minimal and maximal target exponent define the range of w's binary
|
||||||
|
// exponent, where 'w' is the result of multiplying the input by a cached power
|
||||||
|
// of ten.
|
||||||
|
//
|
||||||
|
// A different range might be chosen on a different platform, to optimize digit
|
||||||
|
// generation, but a smaller range requires more powers of ten to be cached.
|
||||||
|
static const int kMinimalTargetExponent = -60;
|
||||||
|
static const int kMaximalTargetExponent = -32;
|
||||||
|
|
||||||
|
|
||||||
|
// Adjusts the last digit of the generated number, and screens out generated
|
||||||
|
// solutions that may be inaccurate. A solution may be inaccurate if it is
|
||||||
|
// outside the safe interval, or if we cannot prove that it is closer to the
|
||||||
|
// input than a neighboring representation of the same length.
|
||||||
|
//
|
||||||
|
// Input: * buffer containing the digits of too_high / 10^kappa
|
||||||
|
// * the buffer's length
|
||||||
|
// * distance_too_high_w == (too_high - w).f() * unit
|
||||||
|
// * unsafe_interval == (too_high - too_low).f() * unit
|
||||||
|
// * rest = (too_high - buffer * 10^kappa).f() * unit
|
||||||
|
// * ten_kappa = 10^kappa * unit
|
||||||
|
// * unit = the common multiplier
|
||||||
|
// Output: returns true if the buffer is guaranteed to contain the closest
|
||||||
|
// representable number to the input.
|
||||||
|
// Modifies the generated digits in the buffer to approach (round towards) w.
|
||||||
|
static bool RoundWeed(Vector<char> buffer,
|
||||||
|
int length,
|
||||||
|
uint64_t distance_too_high_w,
|
||||||
|
uint64_t unsafe_interval,
|
||||||
|
uint64_t rest,
|
||||||
|
uint64_t ten_kappa,
|
||||||
|
uint64_t unit) {
|
||||||
|
uint64_t small_distance = distance_too_high_w - unit;
|
||||||
|
uint64_t big_distance = distance_too_high_w + unit;
|
||||||
|
// Let w_low = too_high - big_distance, and
|
||||||
|
// w_high = too_high - small_distance.
|
||||||
|
// Note: w_low < w < w_high
|
||||||
|
//
|
||||||
|
// The real w (* unit) must lie somewhere inside the interval
|
||||||
|
// ]w_low; w_high[ (often written as "(w_low; w_high)")
|
||||||
|
|
||||||
|
// Basically the buffer currently contains a number in the unsafe interval
|
||||||
|
// ]too_low; too_high[ with too_low < w < too_high
|
||||||
|
//
|
||||||
|
// too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
// ^v 1 unit ^ ^ ^ ^
|
||||||
|
// boundary_high --------------------- . . . .
|
||||||
|
// ^v 1 unit . . . .
|
||||||
|
// - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . .
|
||||||
|
// . . ^ . .
|
||||||
|
// . big_distance . . .
|
||||||
|
// . . . . rest
|
||||||
|
// small_distance . . . .
|
||||||
|
// v . . . .
|
||||||
|
// w_high - - - - - - - - - - - - - - - - - - . . . .
|
||||||
|
// ^v 1 unit . . . .
|
||||||
|
// w ---------------------------------------- . . . .
|
||||||
|
// ^v 1 unit v . . .
|
||||||
|
// w_low - - - - - - - - - - - - - - - - - - - - - . . .
|
||||||
|
// . . v
|
||||||
|
// buffer --------------------------------------------------+-------+--------
|
||||||
|
// . .
|
||||||
|
// safe_interval .
|
||||||
|
// v .
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
|
||||||
|
// ^v 1 unit .
|
||||||
|
// boundary_low ------------------------- unsafe_interval
|
||||||
|
// ^v 1 unit v
|
||||||
|
// too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Note that the value of buffer could lie anywhere inside the range too_low
|
||||||
|
// to too_high.
|
||||||
|
//
|
||||||
|
// boundary_low, boundary_high and w are approximations of the real boundaries
|
||||||
|
// and v (the input number). They are guaranteed to be precise up to one unit.
|
||||||
|
// In fact the error is guaranteed to be strictly less than one unit.
|
||||||
|
//
|
||||||
|
// Anything that lies outside the unsafe interval is guaranteed not to round
|
||||||
|
// to v when read again.
|
||||||
|
// Anything that lies inside the safe interval is guaranteed to round to v
|
||||||
|
// when read again.
|
||||||
|
// If the number inside the buffer lies inside the unsafe interval but not
|
||||||
|
// inside the safe interval then we simply do not know and bail out (returning
|
||||||
|
// false).
|
||||||
|
//
|
||||||
|
// Similarly we have to take into account the imprecision of 'w' when finding
|
||||||
|
// the closest representation of 'w'. If we have two potential
|
||||||
|
// representations, and one is closer to both w_low and w_high, then we know
|
||||||
|
// it is closer to the actual value v.
|
||||||
|
//
|
||||||
|
// By generating the digits of too_high we got the largest (closest to
|
||||||
|
// too_high) buffer that is still in the unsafe interval. In the case where
|
||||||
|
// w_high < buffer < too_high we try to decrement the buffer.
|
||||||
|
// This way the buffer approaches (rounds towards) w.
|
||||||
|
// There are 3 conditions that stop the decrementation process:
|
||||||
|
// 1) the buffer is already below w_high
|
||||||
|
// 2) decrementing the buffer would make it leave the unsafe interval
|
||||||
|
// 3) decrementing the buffer would yield a number below w_high and farther
|
||||||
|
// away than the current number. In other words:
|
||||||
|
// (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high
|
||||||
|
// Instead of using the buffer directly we use its distance to too_high.
|
||||||
|
// Conceptually rest ~= too_high - buffer
|
||||||
|
// We need to do the following tests in this order to avoid over- and
|
||||||
|
// underflows.
|
||||||
|
ASSERT(rest <= unsafe_interval);
|
||||||
|
while (rest < small_distance && // Negated condition 1
|
||||||
|
unsafe_interval - rest >= ten_kappa && // Negated condition 2
|
||||||
|
(rest + ten_kappa < small_distance || // buffer{-1} > w_high
|
||||||
|
small_distance - rest >= rest + ten_kappa - small_distance)) {
|
||||||
|
buffer[length - 1]--;
|
||||||
|
rest += ten_kappa;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have approached w+ as much as possible. We now test if approaching w-
|
||||||
|
// would require changing the buffer. If yes, then we have two possible
|
||||||
|
// representations close to w, but we cannot decide which one is closer.
|
||||||
|
if (rest < big_distance &&
|
||||||
|
unsafe_interval - rest >= ten_kappa &&
|
||||||
|
(rest + ten_kappa < big_distance ||
|
||||||
|
big_distance - rest > rest + ten_kappa - big_distance)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weeding test.
|
||||||
|
// The safe interval is [too_low + 2 ulp; too_high - 2 ulp]
|
||||||
|
// Since too_low = too_high - unsafe_interval this is equivalent to
|
||||||
|
// [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp]
|
||||||
|
// Conceptually we have: rest ~= too_high - buffer
|
||||||
|
return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Rounds the buffer upwards if the result is closer to v by possibly adding
|
||||||
|
// 1 to the buffer. If the precision of the calculation is not sufficient to
|
||||||
|
// round correctly, return false.
|
||||||
|
// The rounding might shift the whole buffer in which case the kappa is
|
||||||
|
// adjusted. For example "99", kappa = 3 might become "10", kappa = 4.
|
||||||
|
//
|
||||||
|
// If 2*rest > ten_kappa then the buffer needs to be round up.
|
||||||
|
// rest can have an error of +/- 1 unit. This function accounts for the
|
||||||
|
// imprecision and returns false, if the rounding direction cannot be
|
||||||
|
// unambiguously determined.
|
||||||
|
//
|
||||||
|
// Precondition: rest < ten_kappa.
|
||||||
|
static bool RoundWeedCounted(Vector<char> buffer,
|
||||||
|
int length,
|
||||||
|
uint64_t rest,
|
||||||
|
uint64_t ten_kappa,
|
||||||
|
uint64_t unit,
|
||||||
|
int* kappa) {
|
||||||
|
ASSERT(rest < ten_kappa);
|
||||||
|
// The following tests are done in a specific order to avoid overflows. They
|
||||||
|
// will work correctly with any uint64 values of rest < ten_kappa and unit.
|
||||||
|
//
|
||||||
|
// If the unit is too big, then we don't know which way to round. For example
|
||||||
|
// a unit of 50 means that the real number lies within rest +/- 50. If
|
||||||
|
// 10^kappa == 40 then there is no way to tell which way to round.
|
||||||
|
if (unit >= ten_kappa) return false;
|
||||||
|
// Even if unit is just half the size of 10^kappa we are already completely
|
||||||
|
// lost. (And after the previous test we know that the expression will not
|
||||||
|
// over/underflow.)
|
||||||
|
if (ten_kappa - unit <= unit) return false;
|
||||||
|
// If 2 * (rest + unit) <= 10^kappa we can safely round down.
|
||||||
|
if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If 2 * (rest - unit) >= 10^kappa, then we can safely round up.
|
||||||
|
if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) {
|
||||||
|
// Increment the last digit recursively until we find a non '9' digit.
|
||||||
|
buffer[length - 1]++;
|
||||||
|
for (int i = length - 1; i > 0; --i) {
|
||||||
|
if (buffer[i] != '0' + 10) break;
|
||||||
|
buffer[i] = '0';
|
||||||
|
buffer[i - 1]++;
|
||||||
|
}
|
||||||
|
// If the first digit is now '0'+ 10 we had a buffer with all '9's. With the
|
||||||
|
// exception of the first digit all digits are now '0'. Simply switch the
|
||||||
|
// first digit to '1' and adjust the kappa. Example: "99" becomes "10" and
|
||||||
|
// the power (the kappa) is increased.
|
||||||
|
if (buffer[0] == '0' + 10) {
|
||||||
|
buffer[0] = '1';
|
||||||
|
(*kappa) += 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the biggest power of ten that is less than or equal to the given
|
||||||
|
// number. We furthermore receive the maximum number of bits 'number' has.
|
||||||
|
//
|
||||||
|
// Returns power == 10^(exponent_plus_one-1) such that
|
||||||
|
// power <= number < power * 10.
|
||||||
|
// If number_bits == 0 then 0^(0-1) is returned.
|
||||||
|
// The number of bits must be <= 32.
|
||||||
|
// Precondition: number < (1 << (number_bits + 1)).
|
||||||
|
|
||||||
|
// Inspired by the method for finding an integer log base 10 from here:
|
||||||
|
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
|
||||||
|
static unsigned int const kSmallPowersOfTen[] =
|
||||||
|
{0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
|
||||||
|
1000000000};
|
||||||
|
|
||||||
|
static void BiggestPowerTen(uint32_t number,
|
||||||
|
int number_bits,
|
||||||
|
uint32_t* power,
|
||||||
|
int* exponent_plus_one) {
|
||||||
|
ASSERT(number < (1u << (number_bits + 1)));
|
||||||
|
// 1233/4096 is approximately 1/lg(10).
|
||||||
|
int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12);
|
||||||
|
// We increment to skip over the first entry in the kPowersOf10 table.
|
||||||
|
// Note: kPowersOf10[i] == 10^(i-1).
|
||||||
|
exponent_plus_one_guess++;
|
||||||
|
// We don't have any guarantees that 2^number_bits <= number.
|
||||||
|
if (number < kSmallPowersOfTen[exponent_plus_one_guess]) {
|
||||||
|
exponent_plus_one_guess--;
|
||||||
|
}
|
||||||
|
*power = kSmallPowersOfTen[exponent_plus_one_guess];
|
||||||
|
*exponent_plus_one = exponent_plus_one_guess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates the digits of input number w.
|
||||||
|
// w is a floating-point number (DiyFp), consisting of a significand and an
|
||||||
|
// exponent. Its exponent is bounded by kMinimalTargetExponent and
|
||||||
|
// kMaximalTargetExponent.
|
||||||
|
// Hence -60 <= w.e() <= -32.
|
||||||
|
//
|
||||||
|
// Returns false if it fails, in which case the generated digits in the buffer
|
||||||
|
// should not be used.
|
||||||
|
// Preconditions:
|
||||||
|
// * low, w and high are correct up to 1 ulp (unit in the last place). That
|
||||||
|
// is, their error must be less than a unit of their last digits.
|
||||||
|
// * low.e() == w.e() == high.e()
|
||||||
|
// * low < w < high, and taking into account their error: low~ <= high~
|
||||||
|
// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
|
||||||
|
// Postconditions: returns false if procedure fails.
|
||||||
|
// otherwise:
|
||||||
|
// * buffer is not null-terminated, but len contains the number of digits.
|
||||||
|
// * buffer contains the shortest possible decimal digit-sequence
|
||||||
|
// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
|
||||||
|
// correct values of low and high (without their error).
|
||||||
|
// * if more than one decimal representation gives the minimal number of
|
||||||
|
// decimal digits then the one closest to W (where W is the correct value
|
||||||
|
// of w) is chosen.
|
||||||
|
// Remark: this procedure takes into account the imprecision of its input
|
||||||
|
// numbers. If the precision is not enough to guarantee all the postconditions
|
||||||
|
// then false is returned. This usually happens rarely (~0.5%).
|
||||||
|
//
|
||||||
|
// Say, for the sake of example, that
|
||||||
|
// w.e() == -48, and w.f() == 0x1234567890abcdef
|
||||||
|
// w's value can be computed by w.f() * 2^w.e()
|
||||||
|
// We can obtain w's integral digits by simply shifting w.f() by -w.e().
|
||||||
|
// -> w's integral part is 0x1234
|
||||||
|
// w's fractional part is therefore 0x567890abcdef.
|
||||||
|
// Printing w's integral part is easy (simply print 0x1234 in decimal).
|
||||||
|
// In order to print its fraction we repeatedly multiply the fraction by 10 and
|
||||||
|
// get each digit. Example the first digit after the point would be computed by
|
||||||
|
// (0x567890abcdef * 10) >> 48. -> 3
|
||||||
|
// The whole thing becomes slightly more complicated because we want to stop
|
||||||
|
// once we have enough digits. That is, once the digits inside the buffer
|
||||||
|
// represent 'w' we can stop. Everything inside the interval low - high
|
||||||
|
// represents w. However we have to pay attention to low, high and w's
|
||||||
|
// imprecision.
|
||||||
|
static bool DigitGen(DiyFp low,
|
||||||
|
DiyFp w,
|
||||||
|
DiyFp high,
|
||||||
|
Vector<char> buffer,
|
||||||
|
int* length,
|
||||||
|
int* kappa) {
|
||||||
|
ASSERT(low.e() == w.e() && w.e() == high.e());
|
||||||
|
ASSERT(low.f() + 1 <= high.f() - 1);
|
||||||
|
ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
|
||||||
|
// low, w and high are imprecise, but by less than one ulp (unit in the last
|
||||||
|
// place).
|
||||||
|
// If we remove (resp. add) 1 ulp from low (resp. high) we are certain that
|
||||||
|
// the new numbers are outside of the interval we want the final
|
||||||
|
// representation to lie in.
|
||||||
|
// Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield
|
||||||
|
// numbers that are certain to lie in the interval. We will use this fact
|
||||||
|
// later on.
|
||||||
|
// We will now start by generating the digits within the uncertain
|
||||||
|
// interval. Later we will weed out representations that lie outside the safe
|
||||||
|
// interval and thus _might_ lie outside the correct interval.
|
||||||
|
uint64_t unit = 1;
|
||||||
|
DiyFp too_low = DiyFp(low.f() - unit, low.e());
|
||||||
|
DiyFp too_high = DiyFp(high.f() + unit, high.e());
|
||||||
|
// too_low and too_high are guaranteed to lie outside the interval we want the
|
||||||
|
// generated number in.
|
||||||
|
DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low);
|
||||||
|
// We now cut the input number into two parts: the integral digits and the
|
||||||
|
// fractionals. We will not write any decimal separator though, but adapt
|
||||||
|
// kappa instead.
|
||||||
|
// Reminder: we are currently computing the digits (stored inside the buffer)
|
||||||
|
// such that: too_low < buffer * 10^kappa < too_high
|
||||||
|
// We use too_high for the digit_generation and stop as soon as possible.
|
||||||
|
// If we stop early we effectively round down.
|
||||||
|
DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
|
||||||
|
// Division by one is a shift.
|
||||||
|
uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e());
|
||||||
|
// Modulo by one is an and.
|
||||||
|
uint64_t fractionals = too_high.f() & (one.f() - 1);
|
||||||
|
uint32_t divisor;
|
||||||
|
int divisor_exponent_plus_one;
|
||||||
|
BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
|
||||||
|
&divisor, &divisor_exponent_plus_one);
|
||||||
|
*kappa = divisor_exponent_plus_one;
|
||||||
|
*length = 0;
|
||||||
|
// Loop invariant: buffer = too_high / 10^kappa (integer division)
|
||||||
|
// The invariant holds for the first iteration: kappa has been initialized
|
||||||
|
// with the divisor exponent + 1. And the divisor is the biggest power of ten
|
||||||
|
// that is smaller than integrals.
|
||||||
|
while (*kappa > 0) {
|
||||||
|
int digit = integrals / divisor;
|
||||||
|
ASSERT(digit <= 9);
|
||||||
|
buffer[*length] = static_cast<char>('0' + digit);
|
||||||
|
(*length)++;
|
||||||
|
integrals %= divisor;
|
||||||
|
(*kappa)--;
|
||||||
|
// Note that kappa now equals the exponent of the divisor and that the
|
||||||
|
// invariant thus holds again.
|
||||||
|
uint64_t rest =
|
||||||
|
(static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
|
||||||
|
// Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
|
||||||
|
// Reminder: unsafe_interval.e() == one.e()
|
||||||
|
if (rest < unsafe_interval.f()) {
|
||||||
|
// Rounding down (by not emitting the remaining digits) yields a number
|
||||||
|
// that lies within the unsafe interval.
|
||||||
|
return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(),
|
||||||
|
unsafe_interval.f(), rest,
|
||||||
|
static_cast<uint64_t>(divisor) << -one.e(), unit);
|
||||||
|
}
|
||||||
|
divisor /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The integrals have been generated. We are at the point of the decimal
|
||||||
|
// separator. In the following loop we simply multiply the remaining digits by
|
||||||
|
// 10 and divide by one. We just need to pay attention to multiply associated
|
||||||
|
// data (like the interval or 'unit'), too.
|
||||||
|
// Note that the multiplication by 10 does not overflow, because w.e >= -60
|
||||||
|
// and thus one.e >= -60.
|
||||||
|
ASSERT(one.e() >= -60);
|
||||||
|
ASSERT(fractionals < one.f());
|
||||||
|
ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
|
||||||
|
for (;;) {
|
||||||
|
fractionals *= 10;
|
||||||
|
unit *= 10;
|
||||||
|
unsafe_interval.set_f(unsafe_interval.f() * 10);
|
||||||
|
// Integer division by one.
|
||||||
|
int digit = static_cast<int>(fractionals >> -one.e());
|
||||||
|
ASSERT(digit <= 9);
|
||||||
|
buffer[*length] = static_cast<char>('0' + digit);
|
||||||
|
(*length)++;
|
||||||
|
fractionals &= one.f() - 1; // Modulo by one.
|
||||||
|
(*kappa)--;
|
||||||
|
if (fractionals < unsafe_interval.f()) {
|
||||||
|
return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit,
|
||||||
|
unsafe_interval.f(), fractionals, one.f(), unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Generates (at most) requested_digits digits of input number w.
|
||||||
|
// w is a floating-point number (DiyFp), consisting of a significand and an
|
||||||
|
// exponent. Its exponent is bounded by kMinimalTargetExponent and
|
||||||
|
// kMaximalTargetExponent.
|
||||||
|
// Hence -60 <= w.e() <= -32.
|
||||||
|
//
|
||||||
|
// Returns false if it fails, in which case the generated digits in the buffer
|
||||||
|
// should not be used.
|
||||||
|
// Preconditions:
|
||||||
|
// * w is correct up to 1 ulp (unit in the last place). That
|
||||||
|
// is, its error must be strictly less than a unit of its last digit.
|
||||||
|
// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
|
||||||
|
//
|
||||||
|
// Postconditions: returns false if procedure fails.
|
||||||
|
// otherwise:
|
||||||
|
// * buffer is not null-terminated, but length contains the number of
|
||||||
|
// digits.
|
||||||
|
// * the representation in buffer is the most precise representation of
|
||||||
|
// requested_digits digits.
|
||||||
|
// * buffer contains at most requested_digits digits of w. If there are less
|
||||||
|
// than requested_digits digits then some trailing '0's have been removed.
|
||||||
|
// * kappa is such that
|
||||||
|
// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2.
|
||||||
|
//
|
||||||
|
// Remark: This procedure takes into account the imprecision of its input
|
||||||
|
// numbers. If the precision is not enough to guarantee all the postconditions
|
||||||
|
// then false is returned. This usually happens rarely, but the failure-rate
|
||||||
|
// increases with higher requested_digits.
|
||||||
|
static bool DigitGenCounted(DiyFp w,
|
||||||
|
int requested_digits,
|
||||||
|
Vector<char> buffer,
|
||||||
|
int* length,
|
||||||
|
int* kappa) {
|
||||||
|
ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
|
||||||
|
ASSERT(kMinimalTargetExponent >= -60);
|
||||||
|
ASSERT(kMaximalTargetExponent <= -32);
|
||||||
|
// w is assumed to have an error less than 1 unit. Whenever w is scaled we
|
||||||
|
// also scale its error.
|
||||||
|
uint64_t w_error = 1;
|
||||||
|
// We cut the input number into two parts: the integral digits and the
|
||||||
|
// fractional digits. We don't emit any decimal separator, but adapt kappa
|
||||||
|
// instead. Example: instead of writing "1.2" we put "12" into the buffer and
|
||||||
|
// increase kappa by 1.
|
||||||
|
DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
|
||||||
|
// Division by one is a shift.
|
||||||
|
uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e());
|
||||||
|
// Modulo by one is an and.
|
||||||
|
uint64_t fractionals = w.f() & (one.f() - 1);
|
||||||
|
uint32_t divisor;
|
||||||
|
int divisor_exponent_plus_one;
|
||||||
|
BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
|
||||||
|
&divisor, &divisor_exponent_plus_one);
|
||||||
|
*kappa = divisor_exponent_plus_one;
|
||||||
|
*length = 0;
|
||||||
|
|
||||||
|
// Loop invariant: buffer = w / 10^kappa (integer division)
|
||||||
|
// The invariant holds for the first iteration: kappa has been initialized
|
||||||
|
// with the divisor exponent + 1. And the divisor is the biggest power of ten
|
||||||
|
// that is smaller than 'integrals'.
|
||||||
|
while (*kappa > 0) {
|
||||||
|
int digit = integrals / divisor;
|
||||||
|
ASSERT(digit <= 9);
|
||||||
|
buffer[*length] = static_cast<char>('0' + digit);
|
||||||
|
(*length)++;
|
||||||
|
requested_digits--;
|
||||||
|
integrals %= divisor;
|
||||||
|
(*kappa)--;
|
||||||
|
// Note that kappa now equals the exponent of the divisor and that the
|
||||||
|
// invariant thus holds again.
|
||||||
|
if (requested_digits == 0) break;
|
||||||
|
divisor /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requested_digits == 0) {
|
||||||
|
uint64_t rest =
|
||||||
|
(static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
|
||||||
|
return RoundWeedCounted(buffer, *length, rest,
|
||||||
|
static_cast<uint64_t>(divisor) << -one.e(), w_error,
|
||||||
|
kappa);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The integrals have been generated. We are at the point of the decimal
|
||||||
|
// separator. In the following loop we simply multiply the remaining digits by
|
||||||
|
// 10 and divide by one. We just need to pay attention to multiply associated
|
||||||
|
// data (the 'unit'), too.
|
||||||
|
// Note that the multiplication by 10 does not overflow, because w.e >= -60
|
||||||
|
// and thus one.e >= -60.
|
||||||
|
ASSERT(one.e() >= -60);
|
||||||
|
ASSERT(fractionals < one.f());
|
||||||
|
ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
|
||||||
|
while (requested_digits > 0 && fractionals > w_error) {
|
||||||
|
fractionals *= 10;
|
||||||
|
w_error *= 10;
|
||||||
|
// Integer division by one.
|
||||||
|
int digit = static_cast<int>(fractionals >> -one.e());
|
||||||
|
ASSERT(digit <= 9);
|
||||||
|
buffer[*length] = static_cast<char>('0' + digit);
|
||||||
|
(*length)++;
|
||||||
|
requested_digits--;
|
||||||
|
fractionals &= one.f() - 1; // Modulo by one.
|
||||||
|
(*kappa)--;
|
||||||
|
}
|
||||||
|
if (requested_digits != 0) return false;
|
||||||
|
return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error,
|
||||||
|
kappa);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Provides a decimal representation of v.
|
||||||
|
// Returns true if it succeeds, otherwise the result cannot be trusted.
|
||||||
|
// There will be *length digits inside the buffer (not null-terminated).
|
||||||
|
// If the function returns true then
|
||||||
|
// v == (double) (buffer * 10^decimal_exponent).
|
||||||
|
// The digits in the buffer are the shortest representation possible: no
|
||||||
|
// 0.09999999999999999 instead of 0.1. The shorter representation will even be
|
||||||
|
// chosen even if the longer one would be closer to v.
|
||||||
|
// The last digit will be closest to the actual v. That is, even if several
|
||||||
|
// digits might correctly yield 'v' when read again, the closest will be
|
||||||
|
// computed.
|
||||||
|
static bool Grisu3(double v,
|
||||||
|
FastDtoaMode mode,
|
||||||
|
Vector<char> buffer,
|
||||||
|
int* length,
|
||||||
|
int* decimal_exponent) {
|
||||||
|
DiyFp w = Double(v).AsNormalizedDiyFp();
|
||||||
|
// boundary_minus and boundary_plus are the boundaries between v and its
|
||||||
|
// closest floating-point neighbors. Any number strictly between
|
||||||
|
// boundary_minus and boundary_plus will round to v when convert to a double.
|
||||||
|
// Grisu3 will never output representations that lie exactly on a boundary.
|
||||||
|
DiyFp boundary_minus, boundary_plus;
|
||||||
|
if (mode == FAST_DTOA_SHORTEST) {
|
||||||
|
Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus);
|
||||||
|
} else {
|
||||||
|
ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE);
|
||||||
|
float single_v = static_cast<float>(v);
|
||||||
|
Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus);
|
||||||
|
}
|
||||||
|
ASSERT(boundary_plus.e() == w.e());
|
||||||
|
DiyFp ten_mk; // Cached power of ten: 10^-k
|
||||||
|
int mk; // -k
|
||||||
|
int ten_mk_minimal_binary_exponent =
|
||||||
|
kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
|
||||||
|
int ten_mk_maximal_binary_exponent =
|
||||||
|
kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
|
||||||
|
PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
|
||||||
|
ten_mk_minimal_binary_exponent,
|
||||||
|
ten_mk_maximal_binary_exponent,
|
||||||
|
&ten_mk, &mk);
|
||||||
|
ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
|
||||||
|
DiyFp::kSignificandSize) &&
|
||||||
|
(kMaximalTargetExponent >= w.e() + ten_mk.e() +
|
||||||
|
DiyFp::kSignificandSize));
|
||||||
|
// Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
|
||||||
|
// 64 bit significand and ten_mk is thus only precise up to 64 bits.
|
||||||
|
|
||||||
|
// The DiyFp::Times procedure rounds its result, and ten_mk is approximated
|
||||||
|
// too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
|
||||||
|
// off by a small amount.
|
||||||
|
// In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
|
||||||
|
// In other words: let f = scaled_w.f() and e = scaled_w.e(), then
|
||||||
|
// (f-1) * 2^e < w*10^k < (f+1) * 2^e
|
||||||
|
DiyFp scaled_w = DiyFp::Times(w, ten_mk);
|
||||||
|
ASSERT(scaled_w.e() ==
|
||||||
|
boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize);
|
||||||
|
// In theory it would be possible to avoid some recomputations by computing
|
||||||
|
// the difference between w and boundary_minus/plus (a power of 2) and to
|
||||||
|
// compute scaled_boundary_minus/plus by subtracting/adding from
|
||||||
|
// scaled_w. However the code becomes much less readable and the speed
|
||||||
|
// enhancements are not terriffic.
|
||||||
|
DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk);
|
||||||
|
DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk);
|
||||||
|
|
||||||
|
// DigitGen will generate the digits of scaled_w. Therefore we have
|
||||||
|
// v == (double) (scaled_w * 10^-mk).
|
||||||
|
// Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
|
||||||
|
// integer than it will be updated. For instance if scaled_w == 1.23 then
|
||||||
|
// the buffer will be filled with "123" und the decimal_exponent will be
|
||||||
|
// decreased by 2.
|
||||||
|
int kappa;
|
||||||
|
bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus,
|
||||||
|
buffer, length, &kappa);
|
||||||
|
*decimal_exponent = -mk + kappa;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The "counted" version of grisu3 (see above) only generates requested_digits
|
||||||
|
// number of digits. This version does not generate the shortest representation,
|
||||||
|
// and with enough requested digits 0.1 will at some point print as 0.9999999...
|
||||||
|
// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and
|
||||||
|
// therefore the rounding strategy for halfway cases is irrelevant.
|
||||||
|
static bool Grisu3Counted(double v,
|
||||||
|
int requested_digits,
|
||||||
|
Vector<char> buffer,
|
||||||
|
int* length,
|
||||||
|
int* decimal_exponent) {
|
||||||
|
DiyFp w = Double(v).AsNormalizedDiyFp();
|
||||||
|
DiyFp ten_mk; // Cached power of ten: 10^-k
|
||||||
|
int mk; // -k
|
||||||
|
int ten_mk_minimal_binary_exponent =
|
||||||
|
kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
|
||||||
|
int ten_mk_maximal_binary_exponent =
|
||||||
|
kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
|
||||||
|
PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
|
||||||
|
ten_mk_minimal_binary_exponent,
|
||||||
|
ten_mk_maximal_binary_exponent,
|
||||||
|
&ten_mk, &mk);
|
||||||
|
ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
|
||||||
|
DiyFp::kSignificandSize) &&
|
||||||
|
(kMaximalTargetExponent >= w.e() + ten_mk.e() +
|
||||||
|
DiyFp::kSignificandSize));
|
||||||
|
// Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
|
||||||
|
// 64 bit significand and ten_mk is thus only precise up to 64 bits.
|
||||||
|
|
||||||
|
// The DiyFp::Times procedure rounds its result, and ten_mk is approximated
|
||||||
|
// too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
|
||||||
|
// off by a small amount.
|
||||||
|
// In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
|
||||||
|
// In other words: let f = scaled_w.f() and e = scaled_w.e(), then
|
||||||
|
// (f-1) * 2^e < w*10^k < (f+1) * 2^e
|
||||||
|
DiyFp scaled_w = DiyFp::Times(w, ten_mk);
|
||||||
|
|
||||||
|
// We now have (double) (scaled_w * 10^-mk).
|
||||||
|
// DigitGen will generate the first requested_digits digits of scaled_w and
|
||||||
|
// return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It
|
||||||
|
// will not always be exactly the same since DigitGenCounted only produces a
|
||||||
|
// limited number of digits.)
|
||||||
|
int kappa;
|
||||||
|
bool result = DigitGenCounted(scaled_w, requested_digits,
|
||||||
|
buffer, length, &kappa);
|
||||||
|
*decimal_exponent = -mk + kappa;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool FastDtoa(double v,
|
||||||
|
FastDtoaMode mode,
|
||||||
|
int requested_digits,
|
||||||
|
Vector<char> buffer,
|
||||||
|
int* length,
|
||||||
|
int* decimal_point) {
|
||||||
|
ASSERT(v > 0);
|
||||||
|
ASSERT(!Double(v).IsSpecial());
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
int decimal_exponent = 0;
|
||||||
|
switch (mode) {
|
||||||
|
case FAST_DTOA_SHORTEST:
|
||||||
|
case FAST_DTOA_SHORTEST_SINGLE:
|
||||||
|
result = Grisu3(v, mode, buffer, length, &decimal_exponent);
|
||||||
|
break;
|
||||||
|
case FAST_DTOA_PRECISION:
|
||||||
|
result = Grisu3Counted(v, requested_digits,
|
||||||
|
buffer, length, &decimal_exponent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
*decimal_point = *length + decimal_exponent;
|
||||||
|
buffer[*length] = '\0';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
88
contrib/libdouble-conversion/double-conversion/fast-dtoa.h
Normal file
88
contrib/libdouble-conversion/double-conversion/fast-dtoa.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_
|
||||||
|
#define DOUBLE_CONVERSION_FAST_DTOA_H_
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
enum FastDtoaMode {
|
||||||
|
// Computes the shortest representation of the given input. The returned
|
||||||
|
// result will be the most accurate number of this length. Longer
|
||||||
|
// representations might be more accurate.
|
||||||
|
FAST_DTOA_SHORTEST,
|
||||||
|
// Same as FAST_DTOA_SHORTEST but for single-precision floats.
|
||||||
|
FAST_DTOA_SHORTEST_SINGLE,
|
||||||
|
// Computes a representation where the precision (number of digits) is
|
||||||
|
// given as input. The precision is independent of the decimal point.
|
||||||
|
FAST_DTOA_PRECISION
|
||||||
|
};
|
||||||
|
|
||||||
|
// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not
|
||||||
|
// include the terminating '\0' character.
|
||||||
|
static const int kFastDtoaMaximalLength = 17;
|
||||||
|
// Same for single-precision numbers.
|
||||||
|
static const int kFastDtoaMaximalSingleLength = 9;
|
||||||
|
|
||||||
|
// Provides a decimal representation of v.
|
||||||
|
// The result should be interpreted as buffer * 10^(point - length).
|
||||||
|
//
|
||||||
|
// Precondition:
|
||||||
|
// * v must be a strictly positive finite double.
|
||||||
|
//
|
||||||
|
// Returns true if it succeeds, otherwise the result can not be trusted.
|
||||||
|
// There will be *length digits inside the buffer followed by a null terminator.
|
||||||
|
// If the function returns true and mode equals
|
||||||
|
// - FAST_DTOA_SHORTEST, then
|
||||||
|
// the parameter requested_digits is ignored.
|
||||||
|
// The result satisfies
|
||||||
|
// v == (double) (buffer * 10^(point - length)).
|
||||||
|
// The digits in the buffer are the shortest representation possible. E.g.
|
||||||
|
// if 0.099999999999 and 0.1 represent the same double then "1" is returned
|
||||||
|
// with point = 0.
|
||||||
|
// The last digit will be closest to the actual v. That is, even if several
|
||||||
|
// digits might correctly yield 'v' when read again, the buffer will contain
|
||||||
|
// the one closest to v.
|
||||||
|
// - FAST_DTOA_PRECISION, then
|
||||||
|
// the buffer contains requested_digits digits.
|
||||||
|
// the difference v - (buffer * 10^(point-length)) is closest to zero for
|
||||||
|
// all possible representations of requested_digits digits.
|
||||||
|
// If there are two values that are equally close, then FastDtoa returns
|
||||||
|
// false.
|
||||||
|
// For both modes the buffer must be large enough to hold the result.
|
||||||
|
bool FastDtoa(double d,
|
||||||
|
FastDtoaMode mode,
|
||||||
|
int requested_digits,
|
||||||
|
Vector<char> buffer,
|
||||||
|
int* length,
|
||||||
|
int* decimal_point);
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_FAST_DTOA_H_
|
404
contrib/libdouble-conversion/double-conversion/fixed-dtoa.cc
Normal file
404
contrib/libdouble-conversion/double-conversion/fixed-dtoa.cc
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "fixed-dtoa.h"
|
||||||
|
#include "ieee.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
// Represents a 128bit type. This class should be replaced by a native type on
|
||||||
|
// platforms that support 128bit integers.
|
||||||
|
class UInt128 {
|
||||||
|
public:
|
||||||
|
UInt128() : high_bits_(0), low_bits_(0) { }
|
||||||
|
UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { }
|
||||||
|
|
||||||
|
void Multiply(uint32_t multiplicand) {
|
||||||
|
uint64_t accumulator;
|
||||||
|
|
||||||
|
accumulator = (low_bits_ & kMask32) * multiplicand;
|
||||||
|
uint32_t part = static_cast<uint32_t>(accumulator & kMask32);
|
||||||
|
accumulator >>= 32;
|
||||||
|
accumulator = accumulator + (low_bits_ >> 32) * multiplicand;
|
||||||
|
low_bits_ = (accumulator << 32) + part;
|
||||||
|
accumulator >>= 32;
|
||||||
|
accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
|
||||||
|
part = static_cast<uint32_t>(accumulator & kMask32);
|
||||||
|
accumulator >>= 32;
|
||||||
|
accumulator = accumulator + (high_bits_ >> 32) * multiplicand;
|
||||||
|
high_bits_ = (accumulator << 32) + part;
|
||||||
|
ASSERT((accumulator >> 32) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shift(int shift_amount) {
|
||||||
|
ASSERT(-64 <= shift_amount && shift_amount <= 64);
|
||||||
|
if (shift_amount == 0) {
|
||||||
|
return;
|
||||||
|
} else if (shift_amount == -64) {
|
||||||
|
high_bits_ = low_bits_;
|
||||||
|
low_bits_ = 0;
|
||||||
|
} else if (shift_amount == 64) {
|
||||||
|
low_bits_ = high_bits_;
|
||||||
|
high_bits_ = 0;
|
||||||
|
} else if (shift_amount <= 0) {
|
||||||
|
high_bits_ <<= -shift_amount;
|
||||||
|
high_bits_ += low_bits_ >> (64 + shift_amount);
|
||||||
|
low_bits_ <<= -shift_amount;
|
||||||
|
} else {
|
||||||
|
low_bits_ >>= shift_amount;
|
||||||
|
low_bits_ += high_bits_ << (64 - shift_amount);
|
||||||
|
high_bits_ >>= shift_amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifies *this to *this MOD (2^power).
|
||||||
|
// Returns *this DIV (2^power).
|
||||||
|
int DivModPowerOf2(int power) {
|
||||||
|
if (power >= 64) {
|
||||||
|
int result = static_cast<int>(high_bits_ >> (power - 64));
|
||||||
|
high_bits_ -= static_cast<uint64_t>(result) << (power - 64);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
uint64_t part_low = low_bits_ >> power;
|
||||||
|
uint64_t part_high = high_bits_ << (64 - power);
|
||||||
|
int result = static_cast<int>(part_low + part_high);
|
||||||
|
high_bits_ = 0;
|
||||||
|
low_bits_ -= part_low << power;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsZero() const {
|
||||||
|
return high_bits_ == 0 && low_bits_ == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BitAt(int position) {
|
||||||
|
if (position >= 64) {
|
||||||
|
return static_cast<int>(high_bits_ >> (position - 64)) & 1;
|
||||||
|
} else {
|
||||||
|
return static_cast<int>(low_bits_ >> position) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const uint64_t kMask32 = 0xFFFFFFFF;
|
||||||
|
// Value == (high_bits_ << 64) + low_bits_
|
||||||
|
uint64_t high_bits_;
|
||||||
|
uint64_t low_bits_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const int kDoubleSignificandSize = 53; // Includes the hidden bit.
|
||||||
|
|
||||||
|
|
||||||
|
static void FillDigits32FixedLength(uint32_t number, int requested_length,
|
||||||
|
Vector<char> buffer, int* length) {
|
||||||
|
for (int i = requested_length - 1; i >= 0; --i) {
|
||||||
|
buffer[(*length) + i] = '0' + number % 10;
|
||||||
|
number /= 10;
|
||||||
|
}
|
||||||
|
*length += requested_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) {
|
||||||
|
int number_length = 0;
|
||||||
|
// We fill the digits in reverse order and exchange them afterwards.
|
||||||
|
while (number != 0) {
|
||||||
|
int digit = number % 10;
|
||||||
|
number /= 10;
|
||||||
|
buffer[(*length) + number_length] = static_cast<char>('0' + digit);
|
||||||
|
number_length++;
|
||||||
|
}
|
||||||
|
// Exchange the digits.
|
||||||
|
int i = *length;
|
||||||
|
int j = *length + number_length - 1;
|
||||||
|
while (i < j) {
|
||||||
|
char tmp = buffer[i];
|
||||||
|
buffer[i] = buffer[j];
|
||||||
|
buffer[j] = tmp;
|
||||||
|
i++;
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
*length += number_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void FillDigits64FixedLength(uint64_t number,
|
||||||
|
Vector<char> buffer, int* length) {
|
||||||
|
const uint32_t kTen7 = 10000000;
|
||||||
|
// For efficiency cut the number into 3 uint32_t parts, and print those.
|
||||||
|
uint32_t part2 = static_cast<uint32_t>(number % kTen7);
|
||||||
|
number /= kTen7;
|
||||||
|
uint32_t part1 = static_cast<uint32_t>(number % kTen7);
|
||||||
|
uint32_t part0 = static_cast<uint32_t>(number / kTen7);
|
||||||
|
|
||||||
|
FillDigits32FixedLength(part0, 3, buffer, length);
|
||||||
|
FillDigits32FixedLength(part1, 7, buffer, length);
|
||||||
|
FillDigits32FixedLength(part2, 7, buffer, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) {
|
||||||
|
const uint32_t kTen7 = 10000000;
|
||||||
|
// For efficiency cut the number into 3 uint32_t parts, and print those.
|
||||||
|
uint32_t part2 = static_cast<uint32_t>(number % kTen7);
|
||||||
|
number /= kTen7;
|
||||||
|
uint32_t part1 = static_cast<uint32_t>(number % kTen7);
|
||||||
|
uint32_t part0 = static_cast<uint32_t>(number / kTen7);
|
||||||
|
|
||||||
|
if (part0 != 0) {
|
||||||
|
FillDigits32(part0, buffer, length);
|
||||||
|
FillDigits32FixedLength(part1, 7, buffer, length);
|
||||||
|
FillDigits32FixedLength(part2, 7, buffer, length);
|
||||||
|
} else if (part1 != 0) {
|
||||||
|
FillDigits32(part1, buffer, length);
|
||||||
|
FillDigits32FixedLength(part2, 7, buffer, length);
|
||||||
|
} else {
|
||||||
|
FillDigits32(part2, buffer, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) {
|
||||||
|
// An empty buffer represents 0.
|
||||||
|
if (*length == 0) {
|
||||||
|
buffer[0] = '1';
|
||||||
|
*decimal_point = 1;
|
||||||
|
*length = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Round the last digit until we either have a digit that was not '9' or until
|
||||||
|
// we reached the first digit.
|
||||||
|
buffer[(*length) - 1]++;
|
||||||
|
for (int i = (*length) - 1; i > 0; --i) {
|
||||||
|
if (buffer[i] != '0' + 10) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffer[i] = '0';
|
||||||
|
buffer[i - 1]++;
|
||||||
|
}
|
||||||
|
// If the first digit is now '0' + 10, we would need to set it to '0' and add
|
||||||
|
// a '1' in front. However we reach the first digit only if all following
|
||||||
|
// digits had been '9' before rounding up. Now all trailing digits are '0' and
|
||||||
|
// we simply switch the first digit to '1' and update the decimal-point
|
||||||
|
// (indicating that the point is now one digit to the right).
|
||||||
|
if (buffer[0] == '0' + 10) {
|
||||||
|
buffer[0] = '1';
|
||||||
|
(*decimal_point)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The given fractionals number represents a fixed-point number with binary
|
||||||
|
// point at bit (-exponent).
|
||||||
|
// Preconditions:
|
||||||
|
// -128 <= exponent <= 0.
|
||||||
|
// 0 <= fractionals * 2^exponent < 1
|
||||||
|
// The buffer holds the result.
|
||||||
|
// The function will round its result. During the rounding-process digits not
|
||||||
|
// generated by this function might be updated, and the decimal-point variable
|
||||||
|
// might be updated. If this function generates the digits 99 and the buffer
|
||||||
|
// already contained "199" (thus yielding a buffer of "19999") then a
|
||||||
|
// rounding-up will change the contents of the buffer to "20000".
|
||||||
|
static void FillFractionals(uint64_t fractionals, int exponent,
|
||||||
|
int fractional_count, Vector<char> buffer,
|
||||||
|
int* length, int* decimal_point) {
|
||||||
|
ASSERT(-128 <= exponent && exponent <= 0);
|
||||||
|
// 'fractionals' is a fixed-point number, with binary point at bit
|
||||||
|
// (-exponent). Inside the function the non-converted remainder of fractionals
|
||||||
|
// is a fixed-point number, with binary point at bit 'point'.
|
||||||
|
if (-exponent <= 64) {
|
||||||
|
// One 64 bit number is sufficient.
|
||||||
|
ASSERT(fractionals >> 56 == 0);
|
||||||
|
int point = -exponent;
|
||||||
|
for (int i = 0; i < fractional_count; ++i) {
|
||||||
|
if (fractionals == 0) break;
|
||||||
|
// Instead of multiplying by 10 we multiply by 5 and adjust the point
|
||||||
|
// location. This way the fractionals variable will not overflow.
|
||||||
|
// Invariant at the beginning of the loop: fractionals < 2^point.
|
||||||
|
// Initially we have: point <= 64 and fractionals < 2^56
|
||||||
|
// After each iteration the point is decremented by one.
|
||||||
|
// Note that 5^3 = 125 < 128 = 2^7.
|
||||||
|
// Therefore three iterations of this loop will not overflow fractionals
|
||||||
|
// (even without the subtraction at the end of the loop body). At this
|
||||||
|
// time point will satisfy point <= 61 and therefore fractionals < 2^point
|
||||||
|
// and any further multiplication of fractionals by 5 will not overflow.
|
||||||
|
fractionals *= 5;
|
||||||
|
point--;
|
||||||
|
int digit = static_cast<int>(fractionals >> point);
|
||||||
|
ASSERT(digit <= 9);
|
||||||
|
buffer[*length] = static_cast<char>('0' + digit);
|
||||||
|
(*length)++;
|
||||||
|
fractionals -= static_cast<uint64_t>(digit) << point;
|
||||||
|
}
|
||||||
|
// If the first bit after the point is set we have to round up.
|
||||||
|
if (((fractionals >> (point - 1)) & 1) == 1) {
|
||||||
|
RoundUp(buffer, length, decimal_point);
|
||||||
|
}
|
||||||
|
} else { // We need 128 bits.
|
||||||
|
ASSERT(64 < -exponent && -exponent <= 128);
|
||||||
|
UInt128 fractionals128 = UInt128(fractionals, 0);
|
||||||
|
fractionals128.Shift(-exponent - 64);
|
||||||
|
int point = 128;
|
||||||
|
for (int i = 0; i < fractional_count; ++i) {
|
||||||
|
if (fractionals128.IsZero()) break;
|
||||||
|
// As before: instead of multiplying by 10 we multiply by 5 and adjust the
|
||||||
|
// point location.
|
||||||
|
// This multiplication will not overflow for the same reasons as before.
|
||||||
|
fractionals128.Multiply(5);
|
||||||
|
point--;
|
||||||
|
int digit = fractionals128.DivModPowerOf2(point);
|
||||||
|
ASSERT(digit <= 9);
|
||||||
|
buffer[*length] = static_cast<char>('0' + digit);
|
||||||
|
(*length)++;
|
||||||
|
}
|
||||||
|
if (fractionals128.BitAt(point - 1) == 1) {
|
||||||
|
RoundUp(buffer, length, decimal_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Removes leading and trailing zeros.
|
||||||
|
// If leading zeros are removed then the decimal point position is adjusted.
|
||||||
|
static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) {
|
||||||
|
while (*length > 0 && buffer[(*length) - 1] == '0') {
|
||||||
|
(*length)--;
|
||||||
|
}
|
||||||
|
int first_non_zero = 0;
|
||||||
|
while (first_non_zero < *length && buffer[first_non_zero] == '0') {
|
||||||
|
first_non_zero++;
|
||||||
|
}
|
||||||
|
if (first_non_zero != 0) {
|
||||||
|
for (int i = first_non_zero; i < *length; ++i) {
|
||||||
|
buffer[i - first_non_zero] = buffer[i];
|
||||||
|
}
|
||||||
|
*length -= first_non_zero;
|
||||||
|
*decimal_point -= first_non_zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool FastFixedDtoa(double v,
|
||||||
|
int fractional_count,
|
||||||
|
Vector<char> buffer,
|
||||||
|
int* length,
|
||||||
|
int* decimal_point) {
|
||||||
|
const uint32_t kMaxUInt32 = 0xFFFFFFFF;
|
||||||
|
uint64_t significand = Double(v).Significand();
|
||||||
|
int exponent = Double(v).Exponent();
|
||||||
|
// v = significand * 2^exponent (with significand a 53bit integer).
|
||||||
|
// If the exponent is larger than 20 (i.e. we may have a 73bit number) then we
|
||||||
|
// don't know how to compute the representation. 2^73 ~= 9.5*10^21.
|
||||||
|
// If necessary this limit could probably be increased, but we don't need
|
||||||
|
// more.
|
||||||
|
if (exponent > 20) return false;
|
||||||
|
if (fractional_count > 20) return false;
|
||||||
|
*length = 0;
|
||||||
|
// At most kDoubleSignificandSize bits of the significand are non-zero.
|
||||||
|
// Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero
|
||||||
|
// bits: 0..11*..0xxx..53*..xx
|
||||||
|
if (exponent + kDoubleSignificandSize > 64) {
|
||||||
|
// The exponent must be > 11.
|
||||||
|
//
|
||||||
|
// We know that v = significand * 2^exponent.
|
||||||
|
// And the exponent > 11.
|
||||||
|
// We simplify the task by dividing v by 10^17.
|
||||||
|
// The quotient delivers the first digits, and the remainder fits into a 64
|
||||||
|
// bit number.
|
||||||
|
// Dividing by 10^17 is equivalent to dividing by 5^17*2^17.
|
||||||
|
const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17
|
||||||
|
uint64_t divisor = kFive17;
|
||||||
|
int divisor_power = 17;
|
||||||
|
uint64_t dividend = significand;
|
||||||
|
uint32_t quotient;
|
||||||
|
uint64_t remainder;
|
||||||
|
// Let v = f * 2^e with f == significand and e == exponent.
|
||||||
|
// Then need q (quotient) and r (remainder) as follows:
|
||||||
|
// v = q * 10^17 + r
|
||||||
|
// f * 2^e = q * 10^17 + r
|
||||||
|
// f * 2^e = q * 5^17 * 2^17 + r
|
||||||
|
// If e > 17 then
|
||||||
|
// f * 2^(e-17) = q * 5^17 + r/2^17
|
||||||
|
// else
|
||||||
|
// f = q * 5^17 * 2^(17-e) + r/2^e
|
||||||
|
if (exponent > divisor_power) {
|
||||||
|
// We only allow exponents of up to 20 and therefore (17 - e) <= 3
|
||||||
|
dividend <<= exponent - divisor_power;
|
||||||
|
quotient = static_cast<uint32_t>(dividend / divisor);
|
||||||
|
remainder = (dividend % divisor) << divisor_power;
|
||||||
|
} else {
|
||||||
|
divisor <<= divisor_power - exponent;
|
||||||
|
quotient = static_cast<uint32_t>(dividend / divisor);
|
||||||
|
remainder = (dividend % divisor) << exponent;
|
||||||
|
}
|
||||||
|
FillDigits32(quotient, buffer, length);
|
||||||
|
FillDigits64FixedLength(remainder, buffer, length);
|
||||||
|
*decimal_point = *length;
|
||||||
|
} else if (exponent >= 0) {
|
||||||
|
// 0 <= exponent <= 11
|
||||||
|
significand <<= exponent;
|
||||||
|
FillDigits64(significand, buffer, length);
|
||||||
|
*decimal_point = *length;
|
||||||
|
} else if (exponent > -kDoubleSignificandSize) {
|
||||||
|
// We have to cut the number.
|
||||||
|
uint64_t integrals = significand >> -exponent;
|
||||||
|
uint64_t fractionals = significand - (integrals << -exponent);
|
||||||
|
if (integrals > kMaxUInt32) {
|
||||||
|
FillDigits64(integrals, buffer, length);
|
||||||
|
} else {
|
||||||
|
FillDigits32(static_cast<uint32_t>(integrals), buffer, length);
|
||||||
|
}
|
||||||
|
*decimal_point = *length;
|
||||||
|
FillFractionals(fractionals, exponent, fractional_count,
|
||||||
|
buffer, length, decimal_point);
|
||||||
|
} else if (exponent < -128) {
|
||||||
|
// This configuration (with at most 20 digits) means that all digits must be
|
||||||
|
// 0.
|
||||||
|
ASSERT(fractional_count <= 20);
|
||||||
|
buffer[0] = '\0';
|
||||||
|
*length = 0;
|
||||||
|
*decimal_point = -fractional_count;
|
||||||
|
} else {
|
||||||
|
*decimal_point = 0;
|
||||||
|
FillFractionals(significand, exponent, fractional_count,
|
||||||
|
buffer, length, decimal_point);
|
||||||
|
}
|
||||||
|
TrimZeros(buffer, length, decimal_point);
|
||||||
|
buffer[*length] = '\0';
|
||||||
|
if ((*length) == 0) {
|
||||||
|
// The string is empty and the decimal_point thus has no importance. Mimick
|
||||||
|
// Gay's dtoa and and set it to -fractional_count.
|
||||||
|
*decimal_point = -fractional_count;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
56
contrib/libdouble-conversion/double-conversion/fixed-dtoa.h
Normal file
56
contrib/libdouble-conversion/double-conversion/fixed-dtoa.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_
|
||||||
|
#define DOUBLE_CONVERSION_FIXED_DTOA_H_
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
// Produces digits necessary to print a given number with
|
||||||
|
// 'fractional_count' digits after the decimal point.
|
||||||
|
// The buffer must be big enough to hold the result plus one terminating null
|
||||||
|
// character.
|
||||||
|
//
|
||||||
|
// The produced digits might be too short in which case the caller has to fill
|
||||||
|
// the gaps with '0's.
|
||||||
|
// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and
|
||||||
|
// decimal_point = -2.
|
||||||
|
// Halfway cases are rounded towards +/-Infinity (away from 0). The call
|
||||||
|
// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0.
|
||||||
|
// The returned buffer may contain digits that would be truncated from the
|
||||||
|
// shortest representation of the input.
|
||||||
|
//
|
||||||
|
// This method only works for some parameters. If it can't handle the input it
|
||||||
|
// returns false. The output is null-terminated when the function succeeds.
|
||||||
|
bool FastFixedDtoa(double v, int fractional_count,
|
||||||
|
Vector<char> buffer, int* length, int* decimal_point);
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_
|
402
contrib/libdouble-conversion/double-conversion/ieee.h
Normal file
402
contrib/libdouble-conversion/double-conversion/ieee.h
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_DOUBLE_H_
|
||||||
|
#define DOUBLE_CONVERSION_DOUBLE_H_
|
||||||
|
|
||||||
|
#include "diy-fp.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
// We assume that doubles and uint64_t have the same endianness.
|
||||||
|
static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); }
|
||||||
|
static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); }
|
||||||
|
static uint32_t float_to_uint32(float f) { return BitCast<uint32_t>(f); }
|
||||||
|
static float uint32_to_float(uint32_t d32) { return BitCast<float>(d32); }
|
||||||
|
|
||||||
|
// Helper functions for doubles.
|
||||||
|
class Double {
|
||||||
|
public:
|
||||||
|
static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000);
|
||||||
|
static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000);
|
||||||
|
static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF);
|
||||||
|
static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000);
|
||||||
|
static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
|
||||||
|
static const int kSignificandSize = 53;
|
||||||
|
|
||||||
|
Double() : d64_(0) {}
|
||||||
|
explicit Double(double d) : d64_(double_to_uint64(d)) {}
|
||||||
|
explicit Double(uint64_t d64) : d64_(d64) {}
|
||||||
|
explicit Double(DiyFp diy_fp)
|
||||||
|
: d64_(DiyFpToUint64(diy_fp)) {}
|
||||||
|
|
||||||
|
// The value encoded by this Double must be greater or equal to +0.0.
|
||||||
|
// It must not be special (infinity, or NaN).
|
||||||
|
DiyFp AsDiyFp() const {
|
||||||
|
ASSERT(Sign() > 0);
|
||||||
|
ASSERT(!IsSpecial());
|
||||||
|
return DiyFp(Significand(), Exponent());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The value encoded by this Double must be strictly greater than 0.
|
||||||
|
DiyFp AsNormalizedDiyFp() const {
|
||||||
|
ASSERT(value() > 0.0);
|
||||||
|
uint64_t f = Significand();
|
||||||
|
int e = Exponent();
|
||||||
|
|
||||||
|
// The current double could be a denormal.
|
||||||
|
while ((f & kHiddenBit) == 0) {
|
||||||
|
f <<= 1;
|
||||||
|
e--;
|
||||||
|
}
|
||||||
|
// Do the final shifts in one go.
|
||||||
|
f <<= DiyFp::kSignificandSize - kSignificandSize;
|
||||||
|
e -= DiyFp::kSignificandSize - kSignificandSize;
|
||||||
|
return DiyFp(f, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the double's bit as uint64.
|
||||||
|
uint64_t AsUint64() const {
|
||||||
|
return d64_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the next greater double. Returns +infinity on input +infinity.
|
||||||
|
double NextDouble() const {
|
||||||
|
if (d64_ == kInfinity) return Double(kInfinity).value();
|
||||||
|
if (Sign() < 0 && Significand() == 0) {
|
||||||
|
// -0.0
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
if (Sign() < 0) {
|
||||||
|
return Double(d64_ - 1).value();
|
||||||
|
} else {
|
||||||
|
return Double(d64_ + 1).value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double PreviousDouble() const {
|
||||||
|
if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity();
|
||||||
|
if (Sign() < 0) {
|
||||||
|
return Double(d64_ + 1).value();
|
||||||
|
} else {
|
||||||
|
if (Significand() == 0) return -0.0;
|
||||||
|
return Double(d64_ - 1).value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Exponent() const {
|
||||||
|
if (IsDenormal()) return kDenormalExponent;
|
||||||
|
|
||||||
|
uint64_t d64 = AsUint64();
|
||||||
|
int biased_e =
|
||||||
|
static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize);
|
||||||
|
return biased_e - kExponentBias;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Significand() const {
|
||||||
|
uint64_t d64 = AsUint64();
|
||||||
|
uint64_t significand = d64 & kSignificandMask;
|
||||||
|
if (!IsDenormal()) {
|
||||||
|
return significand + kHiddenBit;
|
||||||
|
} else {
|
||||||
|
return significand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the double is a denormal.
|
||||||
|
bool IsDenormal() const {
|
||||||
|
uint64_t d64 = AsUint64();
|
||||||
|
return (d64 & kExponentMask) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We consider denormals not to be special.
|
||||||
|
// Hence only Infinity and NaN are special.
|
||||||
|
bool IsSpecial() const {
|
||||||
|
uint64_t d64 = AsUint64();
|
||||||
|
return (d64 & kExponentMask) == kExponentMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNan() const {
|
||||||
|
uint64_t d64 = AsUint64();
|
||||||
|
return ((d64 & kExponentMask) == kExponentMask) &&
|
||||||
|
((d64 & kSignificandMask) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInfinite() const {
|
||||||
|
uint64_t d64 = AsUint64();
|
||||||
|
return ((d64 & kExponentMask) == kExponentMask) &&
|
||||||
|
((d64 & kSignificandMask) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sign() const {
|
||||||
|
uint64_t d64 = AsUint64();
|
||||||
|
return (d64 & kSignMask) == 0? 1: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: the value encoded by this Double must be greater or equal
|
||||||
|
// than +0.0.
|
||||||
|
DiyFp UpperBoundary() const {
|
||||||
|
ASSERT(Sign() > 0);
|
||||||
|
return DiyFp(Significand() * 2 + 1, Exponent() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the two boundaries of this.
|
||||||
|
// The bigger boundary (m_plus) is normalized. The lower boundary has the same
|
||||||
|
// exponent as m_plus.
|
||||||
|
// Precondition: the value encoded by this Double must be greater than 0.
|
||||||
|
void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
|
||||||
|
ASSERT(value() > 0.0);
|
||||||
|
DiyFp v = this->AsDiyFp();
|
||||||
|
DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
|
||||||
|
DiyFp m_minus;
|
||||||
|
if (LowerBoundaryIsCloser()) {
|
||||||
|
m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
|
||||||
|
} else {
|
||||||
|
m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
|
||||||
|
}
|
||||||
|
m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
|
||||||
|
m_minus.set_e(m_plus.e());
|
||||||
|
*out_m_plus = m_plus;
|
||||||
|
*out_m_minus = m_minus;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LowerBoundaryIsCloser() const {
|
||||||
|
// The boundary is closer if the significand is of the form f == 2^p-1 then
|
||||||
|
// the lower boundary is closer.
|
||||||
|
// Think of v = 1000e10 and v- = 9999e9.
|
||||||
|
// Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
|
||||||
|
// at a distance of 1e8.
|
||||||
|
// The only exception is for the smallest normal: the largest denormal is
|
||||||
|
// at the same distance as its successor.
|
||||||
|
// Note: denormals have the same exponent as the smallest normals.
|
||||||
|
bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0);
|
||||||
|
return physical_significand_is_zero && (Exponent() != kDenormalExponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
double value() const { return uint64_to_double(d64_); }
|
||||||
|
|
||||||
|
// Returns the significand size for a given order of magnitude.
|
||||||
|
// If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
|
||||||
|
// This function returns the number of significant binary digits v will have
|
||||||
|
// once it's encoded into a double. In almost all cases this is equal to
|
||||||
|
// kSignificandSize. The only exceptions are denormals. They start with
|
||||||
|
// leading zeroes and their effective significand-size is hence smaller.
|
||||||
|
static int SignificandSizeForOrderOfMagnitude(int order) {
|
||||||
|
if (order >= (kDenormalExponent + kSignificandSize)) {
|
||||||
|
return kSignificandSize;
|
||||||
|
}
|
||||||
|
if (order <= kDenormalExponent) return 0;
|
||||||
|
return order - kDenormalExponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double Infinity() {
|
||||||
|
return Double(kInfinity).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static double NaN() {
|
||||||
|
return Double(kNaN).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
|
||||||
|
static const int kDenormalExponent = -kExponentBias + 1;
|
||||||
|
static const int kMaxExponent = 0x7FF - kExponentBias;
|
||||||
|
static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000);
|
||||||
|
static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000);
|
||||||
|
|
||||||
|
const uint64_t d64_;
|
||||||
|
|
||||||
|
static uint64_t DiyFpToUint64(DiyFp diy_fp) {
|
||||||
|
uint64_t significand = diy_fp.f();
|
||||||
|
int exponent = diy_fp.e();
|
||||||
|
while (significand > kHiddenBit + kSignificandMask) {
|
||||||
|
significand >>= 1;
|
||||||
|
exponent++;
|
||||||
|
}
|
||||||
|
if (exponent >= kMaxExponent) {
|
||||||
|
return kInfinity;
|
||||||
|
}
|
||||||
|
if (exponent < kDenormalExponent) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) {
|
||||||
|
significand <<= 1;
|
||||||
|
exponent--;
|
||||||
|
}
|
||||||
|
uint64_t biased_exponent;
|
||||||
|
if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) {
|
||||||
|
biased_exponent = 0;
|
||||||
|
} else {
|
||||||
|
biased_exponent = static_cast<uint64_t>(exponent + kExponentBias);
|
||||||
|
}
|
||||||
|
return (significand & kSignificandMask) |
|
||||||
|
(biased_exponent << kPhysicalSignificandSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Double);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Single {
|
||||||
|
public:
|
||||||
|
static const uint32_t kSignMask = 0x80000000;
|
||||||
|
static const uint32_t kExponentMask = 0x7F800000;
|
||||||
|
static const uint32_t kSignificandMask = 0x007FFFFF;
|
||||||
|
static const uint32_t kHiddenBit = 0x00800000;
|
||||||
|
static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit.
|
||||||
|
static const int kSignificandSize = 24;
|
||||||
|
|
||||||
|
Single() : d32_(0) {}
|
||||||
|
explicit Single(float f) : d32_(float_to_uint32(f)) {}
|
||||||
|
explicit Single(uint32_t d32) : d32_(d32) {}
|
||||||
|
|
||||||
|
// The value encoded by this Single must be greater or equal to +0.0.
|
||||||
|
// It must not be special (infinity, or NaN).
|
||||||
|
DiyFp AsDiyFp() const {
|
||||||
|
ASSERT(Sign() > 0);
|
||||||
|
ASSERT(!IsSpecial());
|
||||||
|
return DiyFp(Significand(), Exponent());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the single's bit as uint64.
|
||||||
|
uint32_t AsUint32() const {
|
||||||
|
return d32_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Exponent() const {
|
||||||
|
if (IsDenormal()) return kDenormalExponent;
|
||||||
|
|
||||||
|
uint32_t d32 = AsUint32();
|
||||||
|
int biased_e =
|
||||||
|
static_cast<int>((d32 & kExponentMask) >> kPhysicalSignificandSize);
|
||||||
|
return biased_e - kExponentBias;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Significand() const {
|
||||||
|
uint32_t d32 = AsUint32();
|
||||||
|
uint32_t significand = d32 & kSignificandMask;
|
||||||
|
if (!IsDenormal()) {
|
||||||
|
return significand + kHiddenBit;
|
||||||
|
} else {
|
||||||
|
return significand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the single is a denormal.
|
||||||
|
bool IsDenormal() const {
|
||||||
|
uint32_t d32 = AsUint32();
|
||||||
|
return (d32 & kExponentMask) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We consider denormals not to be special.
|
||||||
|
// Hence only Infinity and NaN are special.
|
||||||
|
bool IsSpecial() const {
|
||||||
|
uint32_t d32 = AsUint32();
|
||||||
|
return (d32 & kExponentMask) == kExponentMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNan() const {
|
||||||
|
uint32_t d32 = AsUint32();
|
||||||
|
return ((d32 & kExponentMask) == kExponentMask) &&
|
||||||
|
((d32 & kSignificandMask) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInfinite() const {
|
||||||
|
uint32_t d32 = AsUint32();
|
||||||
|
return ((d32 & kExponentMask) == kExponentMask) &&
|
||||||
|
((d32 & kSignificandMask) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sign() const {
|
||||||
|
uint32_t d32 = AsUint32();
|
||||||
|
return (d32 & kSignMask) == 0? 1: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the two boundaries of this.
|
||||||
|
// The bigger boundary (m_plus) is normalized. The lower boundary has the same
|
||||||
|
// exponent as m_plus.
|
||||||
|
// Precondition: the value encoded by this Single must be greater than 0.
|
||||||
|
void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
|
||||||
|
ASSERT(value() > 0.0);
|
||||||
|
DiyFp v = this->AsDiyFp();
|
||||||
|
DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
|
||||||
|
DiyFp m_minus;
|
||||||
|
if (LowerBoundaryIsCloser()) {
|
||||||
|
m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
|
||||||
|
} else {
|
||||||
|
m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
|
||||||
|
}
|
||||||
|
m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
|
||||||
|
m_minus.set_e(m_plus.e());
|
||||||
|
*out_m_plus = m_plus;
|
||||||
|
*out_m_minus = m_minus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precondition: the value encoded by this Single must be greater or equal
|
||||||
|
// than +0.0.
|
||||||
|
DiyFp UpperBoundary() const {
|
||||||
|
ASSERT(Sign() > 0);
|
||||||
|
return DiyFp(Significand() * 2 + 1, Exponent() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LowerBoundaryIsCloser() const {
|
||||||
|
// The boundary is closer if the significand is of the form f == 2^p-1 then
|
||||||
|
// the lower boundary is closer.
|
||||||
|
// Think of v = 1000e10 and v- = 9999e9.
|
||||||
|
// Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
|
||||||
|
// at a distance of 1e8.
|
||||||
|
// The only exception is for the smallest normal: the largest denormal is
|
||||||
|
// at the same distance as its successor.
|
||||||
|
// Note: denormals have the same exponent as the smallest normals.
|
||||||
|
bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0);
|
||||||
|
return physical_significand_is_zero && (Exponent() != kDenormalExponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
float value() const { return uint32_to_float(d32_); }
|
||||||
|
|
||||||
|
static float Infinity() {
|
||||||
|
return Single(kInfinity).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static float NaN() {
|
||||||
|
return Single(kNaN).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int kExponentBias = 0x7F + kPhysicalSignificandSize;
|
||||||
|
static const int kDenormalExponent = -kExponentBias + 1;
|
||||||
|
static const int kMaxExponent = 0xFF - kExponentBias;
|
||||||
|
static const uint32_t kInfinity = 0x7F800000;
|
||||||
|
static const uint32_t kNaN = 0x7FC00000;
|
||||||
|
|
||||||
|
const uint32_t d32_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Single);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_DOUBLE_H_
|
555
contrib/libdouble-conversion/double-conversion/strtod.cc
Normal file
555
contrib/libdouble-conversion/double-conversion/strtod.cc
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "strtod.h"
|
||||||
|
#include "bignum.h"
|
||||||
|
#include "cached-powers.h"
|
||||||
|
#include "ieee.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
// 2^53 = 9007199254740992.
|
||||||
|
// Any integer with at most 15 decimal digits will hence fit into a double
|
||||||
|
// (which has a 53bit significand) without loss of precision.
|
||||||
|
static const int kMaxExactDoubleIntegerDecimalDigits = 15;
|
||||||
|
// 2^64 = 18446744073709551616 > 10^19
|
||||||
|
static const int kMaxUint64DecimalDigits = 19;
|
||||||
|
|
||||||
|
// Max double: 1.7976931348623157 x 10^308
|
||||||
|
// Min non-zero double: 4.9406564584124654 x 10^-324
|
||||||
|
// Any x >= 10^309 is interpreted as +infinity.
|
||||||
|
// Any x <= 10^-324 is interpreted as 0.
|
||||||
|
// Note that 2.5e-324 (despite being smaller than the min double) will be read
|
||||||
|
// as non-zero (equal to the min non-zero double).
|
||||||
|
static const int kMaxDecimalPower = 309;
|
||||||
|
static const int kMinDecimalPower = -324;
|
||||||
|
|
||||||
|
// 2^64 = 18446744073709551616
|
||||||
|
static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF);
|
||||||
|
|
||||||
|
|
||||||
|
static const double exact_powers_of_ten[] = {
|
||||||
|
1.0, // 10^0
|
||||||
|
10.0,
|
||||||
|
100.0,
|
||||||
|
1000.0,
|
||||||
|
10000.0,
|
||||||
|
100000.0,
|
||||||
|
1000000.0,
|
||||||
|
10000000.0,
|
||||||
|
100000000.0,
|
||||||
|
1000000000.0,
|
||||||
|
10000000000.0, // 10^10
|
||||||
|
100000000000.0,
|
||||||
|
1000000000000.0,
|
||||||
|
10000000000000.0,
|
||||||
|
100000000000000.0,
|
||||||
|
1000000000000000.0,
|
||||||
|
10000000000000000.0,
|
||||||
|
100000000000000000.0,
|
||||||
|
1000000000000000000.0,
|
||||||
|
10000000000000000000.0,
|
||||||
|
100000000000000000000.0, // 10^20
|
||||||
|
1000000000000000000000.0,
|
||||||
|
// 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22
|
||||||
|
10000000000000000000000.0
|
||||||
|
};
|
||||||
|
static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten);
|
||||||
|
|
||||||
|
// Maximum number of significant digits in the decimal representation.
|
||||||
|
// In fact the value is 772 (see conversions.cc), but to give us some margin
|
||||||
|
// we round up to 780.
|
||||||
|
static const int kMaxSignificantDecimalDigits = 780;
|
||||||
|
|
||||||
|
static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
|
||||||
|
for (int i = 0; i < buffer.length(); i++) {
|
||||||
|
if (buffer[i] != '0') {
|
||||||
|
return buffer.SubVector(i, buffer.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Vector<const char>(buffer.start(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
|
||||||
|
for (int i = buffer.length() - 1; i >= 0; --i) {
|
||||||
|
if (buffer[i] != '0') {
|
||||||
|
return buffer.SubVector(0, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Vector<const char>(buffer.start(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void CutToMaxSignificantDigits(Vector<const char> buffer,
|
||||||
|
int exponent,
|
||||||
|
char* significant_buffer,
|
||||||
|
int* significant_exponent) {
|
||||||
|
for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) {
|
||||||
|
significant_buffer[i] = buffer[i];
|
||||||
|
}
|
||||||
|
// The input buffer has been trimmed. Therefore the last digit must be
|
||||||
|
// different from '0'.
|
||||||
|
ASSERT(buffer[buffer.length() - 1] != '0');
|
||||||
|
// Set the last digit to be non-zero. This is sufficient to guarantee
|
||||||
|
// correct rounding.
|
||||||
|
significant_buffer[kMaxSignificantDecimalDigits - 1] = '1';
|
||||||
|
*significant_exponent =
|
||||||
|
exponent + (buffer.length() - kMaxSignificantDecimalDigits);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits.
|
||||||
|
// If possible the input-buffer is reused, but if the buffer needs to be
|
||||||
|
// modified (due to cutting), then the input needs to be copied into the
|
||||||
|
// buffer_copy_space.
|
||||||
|
static void TrimAndCut(Vector<const char> buffer, int exponent,
|
||||||
|
char* buffer_copy_space, int space_size,
|
||||||
|
Vector<const char>* trimmed, int* updated_exponent) {
|
||||||
|
Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
|
||||||
|
Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed);
|
||||||
|
exponent += left_trimmed.length() - right_trimmed.length();
|
||||||
|
if (right_trimmed.length() > kMaxSignificantDecimalDigits) {
|
||||||
|
(void) space_size; // Mark variable as used.
|
||||||
|
ASSERT(space_size >= kMaxSignificantDecimalDigits);
|
||||||
|
CutToMaxSignificantDigits(right_trimmed, exponent,
|
||||||
|
buffer_copy_space, updated_exponent);
|
||||||
|
*trimmed = Vector<const char>(buffer_copy_space,
|
||||||
|
kMaxSignificantDecimalDigits);
|
||||||
|
} else {
|
||||||
|
*trimmed = right_trimmed;
|
||||||
|
*updated_exponent = exponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Reads digits from the buffer and converts them to a uint64.
|
||||||
|
// Reads in as many digits as fit into a uint64.
|
||||||
|
// When the string starts with "1844674407370955161" no further digit is read.
|
||||||
|
// Since 2^64 = 18446744073709551616 it would still be possible read another
|
||||||
|
// digit if it was less or equal than 6, but this would complicate the code.
|
||||||
|
static uint64_t ReadUint64(Vector<const char> buffer,
|
||||||
|
int* number_of_read_digits) {
|
||||||
|
uint64_t result = 0;
|
||||||
|
int i = 0;
|
||||||
|
while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) {
|
||||||
|
int digit = buffer[i++] - '0';
|
||||||
|
ASSERT(0 <= digit && digit <= 9);
|
||||||
|
result = 10 * result + digit;
|
||||||
|
}
|
||||||
|
*number_of_read_digits = i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Reads a DiyFp from the buffer.
|
||||||
|
// The returned DiyFp is not necessarily normalized.
|
||||||
|
// If remaining_decimals is zero then the returned DiyFp is accurate.
|
||||||
|
// Otherwise it has been rounded and has error of at most 1/2 ulp.
|
||||||
|
static void ReadDiyFp(Vector<const char> buffer,
|
||||||
|
DiyFp* result,
|
||||||
|
int* remaining_decimals) {
|
||||||
|
int read_digits;
|
||||||
|
uint64_t significand = ReadUint64(buffer, &read_digits);
|
||||||
|
if (buffer.length() == read_digits) {
|
||||||
|
*result = DiyFp(significand, 0);
|
||||||
|
*remaining_decimals = 0;
|
||||||
|
} else {
|
||||||
|
// Round the significand.
|
||||||
|
if (buffer[read_digits] >= '5') {
|
||||||
|
significand++;
|
||||||
|
}
|
||||||
|
// Compute the binary exponent.
|
||||||
|
int exponent = 0;
|
||||||
|
*result = DiyFp(significand, exponent);
|
||||||
|
*remaining_decimals = buffer.length() - read_digits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool DoubleStrtod(Vector<const char> trimmed,
|
||||||
|
int exponent,
|
||||||
|
double* result) {
|
||||||
|
#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
|
||||||
|
// On x86 the floating-point stack can be 64 or 80 bits wide. If it is
|
||||||
|
// 80 bits wide (as is the case on Linux) then double-rounding occurs and the
|
||||||
|
// result is not accurate.
|
||||||
|
// We know that Windows32 uses 64 bits and is therefore accurate.
|
||||||
|
// Note that the ARM simulator is compiled for 32bits. It therefore exhibits
|
||||||
|
// the same problem.
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) {
|
||||||
|
int read_digits;
|
||||||
|
// The trimmed input fits into a double.
|
||||||
|
// If the 10^exponent (resp. 10^-exponent) fits into a double too then we
|
||||||
|
// can compute the result-double simply by multiplying (resp. dividing) the
|
||||||
|
// two numbers.
|
||||||
|
// This is possible because IEEE guarantees that floating-point operations
|
||||||
|
// return the best possible approximation.
|
||||||
|
if (exponent < 0 && -exponent < kExactPowersOfTenSize) {
|
||||||
|
// 10^-exponent fits into a double.
|
||||||
|
*result = static_cast<double>(ReadUint64(trimmed, &read_digits));
|
||||||
|
ASSERT(read_digits == trimmed.length());
|
||||||
|
*result /= exact_powers_of_ten[-exponent];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (0 <= exponent && exponent < kExactPowersOfTenSize) {
|
||||||
|
// 10^exponent fits into a double.
|
||||||
|
*result = static_cast<double>(ReadUint64(trimmed, &read_digits));
|
||||||
|
ASSERT(read_digits == trimmed.length());
|
||||||
|
*result *= exact_powers_of_ten[exponent];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int remaining_digits =
|
||||||
|
kMaxExactDoubleIntegerDecimalDigits - trimmed.length();
|
||||||
|
if ((0 <= exponent) &&
|
||||||
|
(exponent - remaining_digits < kExactPowersOfTenSize)) {
|
||||||
|
// The trimmed string was short and we can multiply it with
|
||||||
|
// 10^remaining_digits. As a result the remaining exponent now fits
|
||||||
|
// into a double too.
|
||||||
|
*result = static_cast<double>(ReadUint64(trimmed, &read_digits));
|
||||||
|
ASSERT(read_digits == trimmed.length());
|
||||||
|
*result *= exact_powers_of_ten[remaining_digits];
|
||||||
|
*result *= exact_powers_of_ten[exponent - remaining_digits];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns 10^exponent as an exact DiyFp.
|
||||||
|
// The given exponent must be in the range [1; kDecimalExponentDistance[.
|
||||||
|
static DiyFp AdjustmentPowerOfTen(int exponent) {
|
||||||
|
ASSERT(0 < exponent);
|
||||||
|
ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance);
|
||||||
|
// Simply hardcode the remaining powers for the given decimal exponent
|
||||||
|
// distance.
|
||||||
|
ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8);
|
||||||
|
switch (exponent) {
|
||||||
|
case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60);
|
||||||
|
case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57);
|
||||||
|
case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54);
|
||||||
|
case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50);
|
||||||
|
case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47);
|
||||||
|
case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44);
|
||||||
|
case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40);
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If the function returns true then the result is the correct double.
|
||||||
|
// Otherwise it is either the correct double or the double that is just below
|
||||||
|
// the correct double.
|
||||||
|
static bool DiyFpStrtod(Vector<const char> buffer,
|
||||||
|
int exponent,
|
||||||
|
double* result) {
|
||||||
|
DiyFp input;
|
||||||
|
int remaining_decimals;
|
||||||
|
ReadDiyFp(buffer, &input, &remaining_decimals);
|
||||||
|
// Since we may have dropped some digits the input is not accurate.
|
||||||
|
// If remaining_decimals is different than 0 than the error is at most
|
||||||
|
// .5 ulp (unit in the last place).
|
||||||
|
// We don't want to deal with fractions and therefore keep a common
|
||||||
|
// denominator.
|
||||||
|
const int kDenominatorLog = 3;
|
||||||
|
const int kDenominator = 1 << kDenominatorLog;
|
||||||
|
// Move the remaining decimals into the exponent.
|
||||||
|
exponent += remaining_decimals;
|
||||||
|
uint64_t error = (remaining_decimals == 0 ? 0 : kDenominator / 2);
|
||||||
|
|
||||||
|
int old_e = input.e();
|
||||||
|
input.Normalize();
|
||||||
|
error <<= old_e - input.e();
|
||||||
|
|
||||||
|
ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent);
|
||||||
|
if (exponent < PowersOfTenCache::kMinDecimalExponent) {
|
||||||
|
*result = 0.0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
DiyFp cached_power;
|
||||||
|
int cached_decimal_exponent;
|
||||||
|
PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent,
|
||||||
|
&cached_power,
|
||||||
|
&cached_decimal_exponent);
|
||||||
|
|
||||||
|
if (cached_decimal_exponent != exponent) {
|
||||||
|
int adjustment_exponent = exponent - cached_decimal_exponent;
|
||||||
|
DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent);
|
||||||
|
input.Multiply(adjustment_power);
|
||||||
|
if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) {
|
||||||
|
// The product of input with the adjustment power fits into a 64 bit
|
||||||
|
// integer.
|
||||||
|
ASSERT(DiyFp::kSignificandSize == 64);
|
||||||
|
} else {
|
||||||
|
// The adjustment power is exact. There is hence only an error of 0.5.
|
||||||
|
error += kDenominator / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.Multiply(cached_power);
|
||||||
|
// The error introduced by a multiplication of a*b equals
|
||||||
|
// error_a + error_b + error_a*error_b/2^64 + 0.5
|
||||||
|
// Substituting a with 'input' and b with 'cached_power' we have
|
||||||
|
// error_b = 0.5 (all cached powers have an error of less than 0.5 ulp),
|
||||||
|
// error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64
|
||||||
|
int error_b = kDenominator / 2;
|
||||||
|
int error_ab = (error == 0 ? 0 : 1); // We round up to 1.
|
||||||
|
int fixed_error = kDenominator / 2;
|
||||||
|
error += error_b + error_ab + fixed_error;
|
||||||
|
|
||||||
|
old_e = input.e();
|
||||||
|
input.Normalize();
|
||||||
|
error <<= old_e - input.e();
|
||||||
|
|
||||||
|
// See if the double's significand changes if we add/subtract the error.
|
||||||
|
int order_of_magnitude = DiyFp::kSignificandSize + input.e();
|
||||||
|
int effective_significand_size =
|
||||||
|
Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude);
|
||||||
|
int precision_digits_count =
|
||||||
|
DiyFp::kSignificandSize - effective_significand_size;
|
||||||
|
if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) {
|
||||||
|
// This can only happen for very small denormals. In this case the
|
||||||
|
// half-way multiplied by the denominator exceeds the range of an uint64.
|
||||||
|
// Simply shift everything to the right.
|
||||||
|
int shift_amount = (precision_digits_count + kDenominatorLog) -
|
||||||
|
DiyFp::kSignificandSize + 1;
|
||||||
|
input.set_f(input.f() >> shift_amount);
|
||||||
|
input.set_e(input.e() + shift_amount);
|
||||||
|
// We add 1 for the lost precision of error, and kDenominator for
|
||||||
|
// the lost precision of input.f().
|
||||||
|
error = (error >> shift_amount) + 1 + kDenominator;
|
||||||
|
precision_digits_count -= shift_amount;
|
||||||
|
}
|
||||||
|
// We use uint64_ts now. This only works if the DiyFp uses uint64_ts too.
|
||||||
|
ASSERT(DiyFp::kSignificandSize == 64);
|
||||||
|
ASSERT(precision_digits_count < 64);
|
||||||
|
uint64_t one64 = 1;
|
||||||
|
uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1;
|
||||||
|
uint64_t precision_bits = input.f() & precision_bits_mask;
|
||||||
|
uint64_t half_way = one64 << (precision_digits_count - 1);
|
||||||
|
precision_bits *= kDenominator;
|
||||||
|
half_way *= kDenominator;
|
||||||
|
DiyFp rounded_input(input.f() >> precision_digits_count,
|
||||||
|
input.e() + precision_digits_count);
|
||||||
|
if (precision_bits >= half_way + error) {
|
||||||
|
rounded_input.set_f(rounded_input.f() + 1);
|
||||||
|
}
|
||||||
|
// If the last_bits are too close to the half-way case than we are too
|
||||||
|
// inaccurate and round down. In this case we return false so that we can
|
||||||
|
// fall back to a more precise algorithm.
|
||||||
|
|
||||||
|
*result = Double(rounded_input).value();
|
||||||
|
if (half_way - error < precision_bits && precision_bits < half_way + error) {
|
||||||
|
// Too imprecise. The caller will have to fall back to a slower version.
|
||||||
|
// However the returned number is guaranteed to be either the correct
|
||||||
|
// double, or the next-lower double.
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns
|
||||||
|
// - -1 if buffer*10^exponent < diy_fp.
|
||||||
|
// - 0 if buffer*10^exponent == diy_fp.
|
||||||
|
// - +1 if buffer*10^exponent > diy_fp.
|
||||||
|
// Preconditions:
|
||||||
|
// buffer.length() + exponent <= kMaxDecimalPower + 1
|
||||||
|
// buffer.length() + exponent > kMinDecimalPower
|
||||||
|
// buffer.length() <= kMaxDecimalSignificantDigits
|
||||||
|
static int CompareBufferWithDiyFp(Vector<const char> buffer,
|
||||||
|
int exponent,
|
||||||
|
DiyFp diy_fp) {
|
||||||
|
ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1);
|
||||||
|
ASSERT(buffer.length() + exponent > kMinDecimalPower);
|
||||||
|
ASSERT(buffer.length() <= kMaxSignificantDecimalDigits);
|
||||||
|
// Make sure that the Bignum will be able to hold all our numbers.
|
||||||
|
// Our Bignum implementation has a separate field for exponents. Shifts will
|
||||||
|
// consume at most one bigit (< 64 bits).
|
||||||
|
// ln(10) == 3.3219...
|
||||||
|
ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits);
|
||||||
|
Bignum buffer_bignum;
|
||||||
|
Bignum diy_fp_bignum;
|
||||||
|
buffer_bignum.AssignDecimalString(buffer);
|
||||||
|
diy_fp_bignum.AssignUInt64(diy_fp.f());
|
||||||
|
if (exponent >= 0) {
|
||||||
|
buffer_bignum.MultiplyByPowerOfTen(exponent);
|
||||||
|
} else {
|
||||||
|
diy_fp_bignum.MultiplyByPowerOfTen(-exponent);
|
||||||
|
}
|
||||||
|
if (diy_fp.e() > 0) {
|
||||||
|
diy_fp_bignum.ShiftLeft(diy_fp.e());
|
||||||
|
} else {
|
||||||
|
buffer_bignum.ShiftLeft(-diy_fp.e());
|
||||||
|
}
|
||||||
|
return Bignum::Compare(buffer_bignum, diy_fp_bignum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true if the guess is the correct double.
|
||||||
|
// Returns false, when guess is either correct or the next-lower double.
|
||||||
|
static bool ComputeGuess(Vector<const char> trimmed, int exponent,
|
||||||
|
double* guess) {
|
||||||
|
if (trimmed.length() == 0) {
|
||||||
|
*guess = 0.0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) {
|
||||||
|
*guess = Double::Infinity();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (exponent + trimmed.length() <= kMinDecimalPower) {
|
||||||
|
*guess = 0.0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DoubleStrtod(trimmed, exponent, guess) ||
|
||||||
|
DiyFpStrtod(trimmed, exponent, guess)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (*guess == Double::Infinity()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Strtod(Vector<const char> buffer, int exponent) {
|
||||||
|
char copy_buffer[kMaxSignificantDecimalDigits];
|
||||||
|
Vector<const char> trimmed;
|
||||||
|
int updated_exponent;
|
||||||
|
TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
|
||||||
|
&trimmed, &updated_exponent);
|
||||||
|
exponent = updated_exponent;
|
||||||
|
|
||||||
|
double guess;
|
||||||
|
bool is_correct = ComputeGuess(trimmed, exponent, &guess);
|
||||||
|
if (is_correct) return guess;
|
||||||
|
|
||||||
|
DiyFp upper_boundary = Double(guess).UpperBoundary();
|
||||||
|
int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
|
||||||
|
if (comparison < 0) {
|
||||||
|
return guess;
|
||||||
|
} else if (comparison > 0) {
|
||||||
|
return Double(guess).NextDouble();
|
||||||
|
} else if ((Double(guess).Significand() & 1) == 0) {
|
||||||
|
// Round towards even.
|
||||||
|
return guess;
|
||||||
|
} else {
|
||||||
|
return Double(guess).NextDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float Strtof(Vector<const char> buffer, int exponent) {
|
||||||
|
char copy_buffer[kMaxSignificantDecimalDigits];
|
||||||
|
Vector<const char> trimmed;
|
||||||
|
int updated_exponent;
|
||||||
|
TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
|
||||||
|
&trimmed, &updated_exponent);
|
||||||
|
exponent = updated_exponent;
|
||||||
|
|
||||||
|
double double_guess;
|
||||||
|
bool is_correct = ComputeGuess(trimmed, exponent, &double_guess);
|
||||||
|
|
||||||
|
float float_guess = static_cast<float>(double_guess);
|
||||||
|
if (float_guess == double_guess) {
|
||||||
|
// This shortcut triggers for integer values.
|
||||||
|
return float_guess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must catch double-rounding. Say the double has been rounded up, and is
|
||||||
|
// now a boundary of a float, and rounds up again. This is why we have to
|
||||||
|
// look at previous too.
|
||||||
|
// Example (in decimal numbers):
|
||||||
|
// input: 12349
|
||||||
|
// high-precision (4 digits): 1235
|
||||||
|
// low-precision (3 digits):
|
||||||
|
// when read from input: 123
|
||||||
|
// when rounded from high precision: 124.
|
||||||
|
// To do this we simply look at the neigbors of the correct result and see
|
||||||
|
// if they would round to the same float. If the guess is not correct we have
|
||||||
|
// to look at four values (since two different doubles could be the correct
|
||||||
|
// double).
|
||||||
|
|
||||||
|
double double_next = Double(double_guess).NextDouble();
|
||||||
|
double double_previous = Double(double_guess).PreviousDouble();
|
||||||
|
|
||||||
|
float f1 = static_cast<float>(double_previous);
|
||||||
|
float f2 = float_guess;
|
||||||
|
float f3 = static_cast<float>(double_next);
|
||||||
|
float f4;
|
||||||
|
if (is_correct) {
|
||||||
|
f4 = f3;
|
||||||
|
} else {
|
||||||
|
double double_next2 = Double(double_next).NextDouble();
|
||||||
|
f4 = static_cast<float>(double_next2);
|
||||||
|
}
|
||||||
|
(void) f2; // Mark variable as used.
|
||||||
|
ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4);
|
||||||
|
|
||||||
|
// If the guess doesn't lie near a single-precision boundary we can simply
|
||||||
|
// return its float-value.
|
||||||
|
if (f1 == f4) {
|
||||||
|
return float_guess;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT((f1 != f2 && f2 == f3 && f3 == f4) ||
|
||||||
|
(f1 == f2 && f2 != f3 && f3 == f4) ||
|
||||||
|
(f1 == f2 && f2 == f3 && f3 != f4));
|
||||||
|
|
||||||
|
// guess and next are the two possible canditates (in the same way that
|
||||||
|
// double_guess was the lower candidate for a double-precision guess).
|
||||||
|
float guess = f1;
|
||||||
|
float next = f4;
|
||||||
|
DiyFp upper_boundary;
|
||||||
|
if (guess == 0.0f) {
|
||||||
|
float min_float = 1e-45f;
|
||||||
|
upper_boundary = Double(static_cast<double>(min_float) / 2).AsDiyFp();
|
||||||
|
} else {
|
||||||
|
upper_boundary = Single(guess).UpperBoundary();
|
||||||
|
}
|
||||||
|
int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
|
||||||
|
if (comparison < 0) {
|
||||||
|
return guess;
|
||||||
|
} else if (comparison > 0) {
|
||||||
|
return next;
|
||||||
|
} else if ((Single(guess).Significand() & 1) == 0) {
|
||||||
|
// Round towards even.
|
||||||
|
return guess;
|
||||||
|
} else {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
45
contrib/libdouble-conversion/double-conversion/strtod.h
Normal file
45
contrib/libdouble-conversion/double-conversion/strtod.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_STRTOD_H_
|
||||||
|
#define DOUBLE_CONVERSION_STRTOD_H_
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
// The buffer must only contain digits in the range [0-9]. It must not
|
||||||
|
// contain a dot or a sign. It must not start with '0', and must not be empty.
|
||||||
|
double Strtod(Vector<const char> buffer, int exponent);
|
||||||
|
|
||||||
|
// The buffer must only contain digits in the range [0-9]. It must not
|
||||||
|
// contain a dot or a sign. It must not start with '0', and must not be empty.
|
||||||
|
float Strtof(Vector<const char> buffer, int exponent);
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_STRTOD_H_
|
341
contrib/libdouble-conversion/double-conversion/utils.h
Normal file
341
contrib/libdouble-conversion/double-conversion/utils.h
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef DOUBLE_CONVERSION_UTILS_H_
|
||||||
|
#define DOUBLE_CONVERSION_UTILS_H_
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#ifndef ASSERT
|
||||||
|
#define ASSERT(condition) \
|
||||||
|
assert(condition);
|
||||||
|
#endif
|
||||||
|
#ifndef UNIMPLEMENTED
|
||||||
|
#define UNIMPLEMENTED() (abort())
|
||||||
|
#endif
|
||||||
|
#ifndef DOUBLE_CONVERSION_NO_RETURN
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define DOUBLE_CONVERSION_NO_RETURN __declspec(noreturn)
|
||||||
|
#else
|
||||||
|
#define DOUBLE_CONVERSION_NO_RETURN __attribute__((noreturn))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifndef UNREACHABLE
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
void DOUBLE_CONVERSION_NO_RETURN abort_noreturn();
|
||||||
|
inline void abort_noreturn() { abort(); }
|
||||||
|
#define UNREACHABLE() (abort_noreturn())
|
||||||
|
#else
|
||||||
|
#define UNREACHABLE() (abort())
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Double operations detection based on target architecture.
|
||||||
|
// Linux uses a 80bit wide floating point stack on x86. This induces double
|
||||||
|
// rounding, which in turn leads to wrong results.
|
||||||
|
// An easy way to test if the floating-point operations are correct is to
|
||||||
|
// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then
|
||||||
|
// the result is equal to 89255e-22.
|
||||||
|
// The best way to test this, is to create a division-function and to compare
|
||||||
|
// the output of the division with the expected result. (Inlining must be
|
||||||
|
// disabled.)
|
||||||
|
// On Linux,x86 89255e-22 != Div_double(89255.0/1e22)
|
||||||
|
#if defined(_M_X64) || defined(__x86_64__) || \
|
||||||
|
defined(__ARMEL__) || defined(__avr32__) || \
|
||||||
|
defined(__hppa__) || defined(__ia64__) || \
|
||||||
|
defined(__mips__) || \
|
||||||
|
defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \
|
||||||
|
defined(_POWER) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
|
||||||
|
defined(__sparc__) || defined(__sparc) || defined(__s390__) || \
|
||||||
|
defined(__SH4__) || defined(__alpha__) || \
|
||||||
|
defined(_MIPS_ARCH_MIPS32R2) || \
|
||||||
|
defined(__AARCH64EL__) || defined(__aarch64__)
|
||||||
|
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
|
||||||
|
#elif defined(__mc68000__)
|
||||||
|
#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
|
||||||
|
#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
|
||||||
|
#if defined(_WIN32)
|
||||||
|
// Windows uses a 64bit wide floating point stack.
|
||||||
|
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
|
||||||
|
#else
|
||||||
|
#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
|
||||||
|
#endif // _WIN32
|
||||||
|
#else
|
||||||
|
#error Target architecture was not detected as supported by Double-Conversion.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
|
||||||
|
#else
|
||||||
|
#define DOUBLE_CONVERSION_UNUSED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
|
||||||
|
typedef signed char int8_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef short int16_t; // NOLINT
|
||||||
|
typedef unsigned short uint16_t; // NOLINT
|
||||||
|
typedef int int32_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
typedef __int64 int64_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
// intptr_t and friends are defined in crtdefs.h through stdio.h.
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef uint16_t uc16;
|
||||||
|
|
||||||
|
// The following macro works on both 32 and 64-bit platforms.
|
||||||
|
// Usage: instead of writing 0x1234567890123456
|
||||||
|
// write UINT64_2PART_C(0x12345678,90123456);
|
||||||
|
#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
|
||||||
|
|
||||||
|
|
||||||
|
// The expression ARRAY_SIZE(a) is a compile-time constant of type
|
||||||
|
// size_t which represents the number of elements of the given
|
||||||
|
// array. You should only use ARRAY_SIZE on statically allocated
|
||||||
|
// arrays.
|
||||||
|
#ifndef ARRAY_SIZE
|
||||||
|
#define ARRAY_SIZE(a) \
|
||||||
|
((sizeof(a) / sizeof(*(a))) / \
|
||||||
|
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// A macro to disallow the evil copy constructor and operator= functions
|
||||||
|
// This should be used in the private: declarations for a class
|
||||||
|
#ifndef DISALLOW_COPY_AND_ASSIGN
|
||||||
|
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||||
|
TypeName(const TypeName&); \
|
||||||
|
void operator=(const TypeName&)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// A macro to disallow all the implicit constructors, namely the
|
||||||
|
// default constructor, copy constructor and operator= functions.
|
||||||
|
//
|
||||||
|
// This should be used in the private: declarations for a class
|
||||||
|
// that wants to prevent anyone from instantiating it. This is
|
||||||
|
// especially useful for classes containing only static methods.
|
||||||
|
#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS
|
||||||
|
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||||
|
TypeName(); \
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(TypeName)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace double_conversion {
|
||||||
|
|
||||||
|
static const int kCharSize = sizeof(char);
|
||||||
|
|
||||||
|
// Returns the maximum of the two parameters.
|
||||||
|
template <typename T>
|
||||||
|
static T Max(T a, T b) {
|
||||||
|
return a < b ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns the minimum of the two parameters.
|
||||||
|
template <typename T>
|
||||||
|
static T Min(T a, T b) {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline int StrLength(const char* string) {
|
||||||
|
size_t length = strlen(string);
|
||||||
|
ASSERT(length == static_cast<size_t>(static_cast<int>(length)));
|
||||||
|
return static_cast<int>(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a simplified version of V8's Vector class.
|
||||||
|
template <typename T>
|
||||||
|
class Vector {
|
||||||
|
public:
|
||||||
|
Vector() : start_(NULL), length_(0) {}
|
||||||
|
Vector(T* data, int len) : start_(data), length_(len) {
|
||||||
|
ASSERT(len == 0 || (len > 0 && data != NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a vector using the same backing storage as this one,
|
||||||
|
// spanning from and including 'from', to but not including 'to'.
|
||||||
|
Vector<T> SubVector(int from, int to) {
|
||||||
|
ASSERT(to <= length_);
|
||||||
|
ASSERT(from < to);
|
||||||
|
ASSERT(0 <= from);
|
||||||
|
return Vector<T>(start() + from, to - from);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the length of the vector.
|
||||||
|
int length() const { return length_; }
|
||||||
|
|
||||||
|
// Returns whether or not the vector is empty.
|
||||||
|
bool is_empty() const { return length_ == 0; }
|
||||||
|
|
||||||
|
// Returns the pointer to the start of the data in the vector.
|
||||||
|
T* start() const { return start_; }
|
||||||
|
|
||||||
|
// Access individual vector elements - checks bounds in debug mode.
|
||||||
|
T& operator[](int index) const {
|
||||||
|
ASSERT(0 <= index && index < length_);
|
||||||
|
return start_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
T& first() { return start_[0]; }
|
||||||
|
|
||||||
|
T& last() { return start_[length_ - 1]; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* start_;
|
||||||
|
int length_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Helper class for building result strings in a character buffer. The
|
||||||
|
// purpose of the class is to use safe operations that checks the
|
||||||
|
// buffer bounds on all operations in debug mode.
|
||||||
|
class StringBuilder {
|
||||||
|
public:
|
||||||
|
StringBuilder(char* buffer, int buffer_size)
|
||||||
|
: buffer_(buffer, buffer_size), position_(0) { }
|
||||||
|
|
||||||
|
~StringBuilder() { if (!is_finalized()) Finalize(); }
|
||||||
|
|
||||||
|
int size() const { return buffer_.length(); }
|
||||||
|
|
||||||
|
// Get the current position in the builder.
|
||||||
|
int position() const {
|
||||||
|
ASSERT(!is_finalized());
|
||||||
|
return position_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the position.
|
||||||
|
void Reset() { position_ = 0; }
|
||||||
|
|
||||||
|
// Add a single character to the builder. It is not allowed to add
|
||||||
|
// 0-characters; use the Finalize() method to terminate the string
|
||||||
|
// instead.
|
||||||
|
void AddCharacter(char c) {
|
||||||
|
ASSERT(c != '\0');
|
||||||
|
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||||
|
buffer_[position_++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an entire string to the builder. Uses strlen() internally to
|
||||||
|
// compute the length of the input string.
|
||||||
|
void AddString(const char* s) {
|
||||||
|
AddSubstring(s, StrLength(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the first 'n' characters of the given string 's' to the
|
||||||
|
// builder. The input string must have enough characters.
|
||||||
|
void AddSubstring(const char* s, int n) {
|
||||||
|
ASSERT(!is_finalized() && position_ + n < buffer_.length());
|
||||||
|
ASSERT(static_cast<size_t>(n) <= strlen(s));
|
||||||
|
memmove(&buffer_[position_], s, n * kCharSize);
|
||||||
|
position_ += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add character padding to the builder. If count is non-positive,
|
||||||
|
// nothing is added to the builder.
|
||||||
|
void AddPadding(char c, int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
AddCharacter(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize the string by 0-terminating it and returning the buffer.
|
||||||
|
char* Finalize() {
|
||||||
|
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||||
|
buffer_[position_] = '\0';
|
||||||
|
// Make sure nobody managed to add a 0-character to the
|
||||||
|
// buffer while building the string.
|
||||||
|
ASSERT(strlen(buffer_.start()) == static_cast<size_t>(position_));
|
||||||
|
position_ = -1;
|
||||||
|
ASSERT(is_finalized());
|
||||||
|
return buffer_.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<char> buffer_;
|
||||||
|
int position_;
|
||||||
|
|
||||||
|
bool is_finalized() const { return position_ < 0; }
|
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
|
||||||
|
};
|
||||||
|
|
||||||
|
// The type-based aliasing rule allows the compiler to assume that pointers of
|
||||||
|
// different types (for some definition of different) never alias each other.
|
||||||
|
// Thus the following code does not work:
|
||||||
|
//
|
||||||
|
// float f = foo();
|
||||||
|
// int fbits = *(int*)(&f);
|
||||||
|
//
|
||||||
|
// The compiler 'knows' that the int pointer can't refer to f since the types
|
||||||
|
// don't match, so the compiler may cache f in a register, leaving random data
|
||||||
|
// in fbits. Using C++ style casts makes no difference, however a pointer to
|
||||||
|
// char data is assumed to alias any other pointer. This is the 'memcpy
|
||||||
|
// exception'.
|
||||||
|
//
|
||||||
|
// Bit_cast uses the memcpy exception to move the bits from a variable of one
|
||||||
|
// type of a variable of another type. Of course the end result is likely to
|
||||||
|
// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005)
|
||||||
|
// will completely optimize BitCast away.
|
||||||
|
//
|
||||||
|
// There is an additional use for BitCast.
|
||||||
|
// Recent gccs will warn when they see casts that may result in breakage due to
|
||||||
|
// the type-based aliasing rule. If you have checked that there is no breakage
|
||||||
|
// you can use BitCast to cast one pointer type to another. This confuses gcc
|
||||||
|
// enough that it can no longer see that you have cast one pointer type to
|
||||||
|
// another thus avoiding the warning.
|
||||||
|
template <class Dest, class Source>
|
||||||
|
inline Dest BitCast(const Source& source) {
|
||||||
|
// Compile time assertion: sizeof(Dest) == sizeof(Source)
|
||||||
|
// A compile error here means your Dest and Source have different sizes.
|
||||||
|
DOUBLE_CONVERSION_UNUSED
|
||||||
|
typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1];
|
||||||
|
|
||||||
|
Dest dest;
|
||||||
|
memmove(&dest, &source, sizeof(dest));
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Dest, class Source>
|
||||||
|
inline Dest BitCast(Source* source) {
|
||||||
|
return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace double_conversion
|
||||||
|
|
||||||
|
#endif // DOUBLE_CONVERSION_UTILS_H_
|
5
contrib/libfarmhash/CMakeLists.txt
Normal file
5
contrib/libfarmhash/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include_directories (${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
add_library(farmhash
|
||||||
|
farmhash.cc
|
||||||
|
farmhash.h)
|
19
contrib/libfarmhash/COPYING
Normal file
19
contrib/libfarmhash/COPYING
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2014 Google, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
8
contrib/libfarmhash/README
Normal file
8
contrib/libfarmhash/README
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Original URL: https://code.google.com/p/farmhash/
|
||||||
|
Created from version:
|
||||||
|
|
||||||
|
commit afdb2e459d030afd1f3b6116545397509326385e
|
||||||
|
Author: gpike@google.com <gpike@google.com@34f43262-9e73-f86a-efff-89e1528fadaf>
|
||||||
|
Date: Sun Mar 1 20:04:23 2015 +0000
|
||||||
|
|
||||||
|
Various additions, updates, and fixes for FarmHash 1.1.
|
11854
contrib/libfarmhash/farmhash.cc
Normal file
11854
contrib/libfarmhash/farmhash.cc
Normal file
File diff suppressed because it is too large
Load Diff
290
contrib/libfarmhash/farmhash.h
Normal file
290
contrib/libfarmhash/farmhash.h
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
// Copyright (c) 2014 Google, Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// FarmHash, by Geoff Pike
|
||||||
|
|
||||||
|
//
|
||||||
|
// http://code.google.com/p/farmhash/
|
||||||
|
//
|
||||||
|
// This file provides a few functions for hashing strings and other
|
||||||
|
// data. All of them are high-quality functions in the sense that
|
||||||
|
// they do well on standard tests such as Austin Appleby's SMHasher.
|
||||||
|
// They're also fast. FarmHash is the successor to CityHash.
|
||||||
|
//
|
||||||
|
// Functions in the FarmHash family are not suitable for cryptography.
|
||||||
|
//
|
||||||
|
// WARNING: This code has been only lightly tested on big-endian platforms!
|
||||||
|
// It is known to work well on little-endian platforms that have a small penalty
|
||||||
|
// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
|
||||||
|
// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
|
||||||
|
// bug reports are welcome.
|
||||||
|
//
|
||||||
|
// By the way, for some hash functions, given strings a and b, the hash
|
||||||
|
// of a+b is easily derived from the hashes of a and b. This property
|
||||||
|
// doesn't hold for any hash functions in this file.
|
||||||
|
|
||||||
|
#ifndef FARM_HASH_H_
|
||||||
|
#define FARM_HASH_H_
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h> // for memcpy and memset
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#ifndef NAMESPACE_FOR_HASH_FUNCTIONS
|
||||||
|
#define NAMESPACE_FOR_HASH_FUNCTIONS farmhash
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace NAMESPACE_FOR_HASH_FUNCTIONS {
|
||||||
|
|
||||||
|
#if defined(FARMHASH_UINT128_T_DEFINED)
|
||||||
|
inline uint64_t Uint128Low64(const uint128_t x) {
|
||||||
|
return static_cast<uint64_t>(x);
|
||||||
|
}
|
||||||
|
inline uint64_t Uint128High64(const uint128_t x) {
|
||||||
|
return static_cast<uint64_t>(x >> 64);
|
||||||
|
}
|
||||||
|
inline uint128_t Uint128(uint64_t lo, uint64_t hi) {
|
||||||
|
return lo + (((uint128_t)hi) << 64);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
typedef std::pair<uint64_t, uint64_t> uint128_t;
|
||||||
|
inline uint64_t Uint128Low64(const uint128_t x) { return x.first; }
|
||||||
|
inline uint64_t Uint128High64(const uint128_t x) { return x.second; }
|
||||||
|
inline uint128_t Uint128(uint64_t lo, uint64_t hi) { return uint128_t(lo, hi); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// BASIC STRING HASHING
|
||||||
|
|
||||||
|
// Hash function for a byte array.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
size_t Hash(const char* s, size_t len);
|
||||||
|
|
||||||
|
// Hash function for a byte array. Most useful in 32-bit binaries.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
uint32_t Hash32(const char* s, size_t len);
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 32-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
uint32_t Hash32WithSeed(const char* s, size_t len, uint32_t seed);
|
||||||
|
|
||||||
|
// Hash 128 input bits down to 64 bits of output.
|
||||||
|
// Hash function for a byte array.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
uint64_t Hash64(const char* s, size_t len);
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 64-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
uint64_t Hash64WithSeed(const char* s, size_t len, uint64_t seed);
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, two seeds are also
|
||||||
|
// hashed into the result.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
uint64_t Hash64WithSeeds(const char* s, size_t len,
|
||||||
|
uint64_t seed0, uint64_t seed1);
|
||||||
|
|
||||||
|
// Hash function for a byte array.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
uint128_t Hash128(const char* s, size_t len);
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 128-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
uint128_t Hash128WithSeed(const char* s, size_t len, uint128_t seed);
|
||||||
|
|
||||||
|
// BASIC NON-STRING HASHING
|
||||||
|
|
||||||
|
// This is intended to be a reasonably good hash function.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
inline uint64_t Hash128to64(uint128_t x) {
|
||||||
|
// Murmur-inspired hashing.
|
||||||
|
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
|
||||||
|
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
|
||||||
|
a ^= (a >> 47);
|
||||||
|
uint64_t b = (Uint128High64(x) ^ a) * kMul;
|
||||||
|
b ^= (b >> 47);
|
||||||
|
b *= kMul;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FINGERPRINTING (i.e., good, portable, forever-fixed hash functions)
|
||||||
|
|
||||||
|
// Fingerprint function for a byte array. Most useful in 32-bit binaries.
|
||||||
|
uint32_t Fingerprint32(const char* s, size_t len);
|
||||||
|
|
||||||
|
// Fingerprint function for a byte array.
|
||||||
|
uint64_t Fingerprint64(const char* s, size_t len);
|
||||||
|
|
||||||
|
// Fingerprint function for a byte array.
|
||||||
|
uint128_t Fingerprint128(const char* s, size_t len);
|
||||||
|
|
||||||
|
// This is intended to be a good fingerprinting primitive.
|
||||||
|
// See below for more overloads.
|
||||||
|
inline uint64_t Fingerprint(uint128_t x) {
|
||||||
|
// Murmur-inspired hashing.
|
||||||
|
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
|
||||||
|
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
|
||||||
|
a ^= (a >> 47);
|
||||||
|
uint64_t b = (Uint128High64(x) ^ a) * kMul;
|
||||||
|
b ^= (b >> 44);
|
||||||
|
b *= kMul;
|
||||||
|
b ^= (b >> 41);
|
||||||
|
b *= kMul;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is intended to be a good fingerprinting primitive.
|
||||||
|
inline uint64_t Fingerprint(uint64_t x) {
|
||||||
|
// Murmur-inspired hashing.
|
||||||
|
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
|
||||||
|
uint64_t b = x * kMul;
|
||||||
|
b ^= (b >> 44);
|
||||||
|
b *= kMul;
|
||||||
|
b ^= (b >> 41);
|
||||||
|
b *= kMul;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef FARMHASH_NO_CXX_STRING
|
||||||
|
|
||||||
|
// Convenience functions to hash or fingerprint C++ strings.
|
||||||
|
// These require that Str::data() return a pointer to the first char
|
||||||
|
// (as a const char*) and that Str::length() return the string's length;
|
||||||
|
// they work with std::string, for example.
|
||||||
|
|
||||||
|
// Hash function for a byte array.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
template <typename Str>
|
||||||
|
inline size_t Hash(const Str& s) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Hash(s.data(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash function for a byte array. Most useful in 32-bit binaries.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint32_t Hash32(const Str& s) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Hash32(s.data(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 32-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint32_t Hash32WithSeed(const Str& s, uint32_t seed) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Hash32WithSeed(s.data(), s.length(), seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash 128 input bits down to 64 bits of output.
|
||||||
|
// Hash function for a byte array.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint64_t Hash64(const Str& s) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Hash64(s.data(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 64-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint64_t Hash64WithSeed(const Str& s, uint64_t seed) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Hash64WithSeed(s.data(), s.length(), seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, two seeds are also
|
||||||
|
// hashed into the result.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint64_t Hash64WithSeeds(const Str& s, uint64_t seed0, uint64_t seed1) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Hash64WithSeeds(s.data(), s.length(), seed0, seed1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash function for a byte array.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint128_t Hash128(const Str& s) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Hash128(s.data(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash function for a byte array. For convenience, a 128-bit seed is also
|
||||||
|
// hashed into the result.
|
||||||
|
// May change from time to time, may differ on different platforms, may differ
|
||||||
|
// depending on NDEBUG.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint128_t Hash128WithSeed(const Str& s, uint128_t seed) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Hash128(s.data(), s.length(), seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FINGERPRINTING (i.e., good, portable, forever-fixed hash functions)
|
||||||
|
|
||||||
|
// Fingerprint function for a byte array. Most useful in 32-bit binaries.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint32_t Fingerprint32(const Str& s) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Fingerprint32(s.data(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fingerprint 128 input bits down to 64 bits of output.
|
||||||
|
// Fingerprint function for a byte array.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint64_t Fingerprint64(const Str& s) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Fingerprint64(s.data(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fingerprint function for a byte array.
|
||||||
|
template <typename Str>
|
||||||
|
inline uint128_t Fingerprint128(const Str& s) {
|
||||||
|
assert(sizeof(s[0]) == 1);
|
||||||
|
return Fingerprint128(s.data(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace NAMESPACE_FOR_HASH_FUNCTIONS
|
||||||
|
|
||||||
|
#endif // FARM_HASH_H_
|
6
contrib/liblz4/CMakeLists.txt
Normal file
6
contrib/liblz4/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
add_library (lz4
|
||||||
|
src/lz4.c
|
||||||
|
src/lz4hc.c
|
||||||
|
|
||||||
|
include/lz4/lz4.h
|
||||||
|
include/lz4/lz4hc.h)
|
24
contrib/liblz4/LICENSE
Normal file
24
contrib/liblz4/LICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
LZ4 Library
|
||||||
|
Copyright (c) 2011-2014, Yann Collet
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
360
contrib/liblz4/include/lz4/lz4.h
Normal file
360
contrib/liblz4/include/lz4/lz4.h
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
/*
|
||||||
|
LZ4 - Fast LZ compression algorithm
|
||||||
|
Header File
|
||||||
|
Copyright (C) 2011-2015, Yann Collet.
|
||||||
|
|
||||||
|
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
You can contact the author at :
|
||||||
|
- LZ4 source repository : https://github.com/Cyan4973/lz4
|
||||||
|
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lz4.h provides block compression functions, and gives full buffer control to programmer.
|
||||||
|
* If you need to generate inter-operable compressed data (respecting LZ4 frame specification),
|
||||||
|
* and can let the library handle its own memory, please use lz4frame.h instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Version
|
||||||
|
**************************************/
|
||||||
|
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
|
||||||
|
#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
|
||||||
|
#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */
|
||||||
|
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
|
||||||
|
int LZ4_versionNumber (void);
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Tuning parameter
|
||||||
|
**************************************/
|
||||||
|
/*
|
||||||
|
* LZ4_MEMORY_USAGE :
|
||||||
|
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
|
||||||
|
* Increasing memory usage improves compression ratio
|
||||||
|
* Reduced memory usage can improve speed, due to cache effect
|
||||||
|
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
|
||||||
|
*/
|
||||||
|
#define LZ4_MEMORY_USAGE 14
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Simple Functions
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
|
||||||
|
int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
LZ4_compress_default() :
|
||||||
|
Compresses 'sourceSize' bytes from buffer 'source'
|
||||||
|
into already allocated 'dest' buffer of size 'maxDestSize'.
|
||||||
|
Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
|
||||||
|
It also runs faster, so it's a recommended setting.
|
||||||
|
If the function cannot compress 'source' into a more limited 'dest' budget,
|
||||||
|
compression stops *immediately*, and the function result is zero.
|
||||||
|
As a consequence, 'dest' content is not valid.
|
||||||
|
This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
|
||||||
|
sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
|
||||||
|
maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
|
||||||
|
return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
|
||||||
|
or 0 if compression fails
|
||||||
|
|
||||||
|
LZ4_decompress_safe() :
|
||||||
|
compressedSize : is the precise full size of the compressed block.
|
||||||
|
maxDecompressedSize : is the size of destination buffer, which must be already allocated.
|
||||||
|
return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
|
||||||
|
If destination buffer is not large enough, decoding will stop and output an error code (<0).
|
||||||
|
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||||
|
This function is protected against buffer overflow exploits, including malicious data packets.
|
||||||
|
It never writes outside output buffer, nor reads outside input buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Advanced Functions
|
||||||
|
**************************************/
|
||||||
|
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
|
||||||
|
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
|
||||||
|
|
||||||
|
/*
|
||||||
|
LZ4_compressBound() :
|
||||||
|
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
|
||||||
|
This function is primarily useful for memory allocation purposes (destination buffer size).
|
||||||
|
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
|
||||||
|
Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
|
||||||
|
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
|
||||||
|
return : maximum output size in a "worst case" scenario
|
||||||
|
or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
|
||||||
|
*/
|
||||||
|
int LZ4_compressBound(int inputSize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
LZ4_compress_fast() :
|
||||||
|
Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
|
||||||
|
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
|
||||||
|
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
|
||||||
|
An acceleration value of "1" is the same as regular LZ4_compress_default()
|
||||||
|
Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
|
||||||
|
*/
|
||||||
|
int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
LZ4_compress_fast_extState() :
|
||||||
|
Same compression function, just using an externally allocated memory space to store compression state.
|
||||||
|
Use LZ4_sizeofState() to know how much memory must be allocated,
|
||||||
|
and allocate it on 8-bytes boundaries (using malloc() typically).
|
||||||
|
Then, provide it as 'void* state' to compression function.
|
||||||
|
*/
|
||||||
|
int LZ4_sizeofState(void);
|
||||||
|
int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
LZ4_compress_destSize() :
|
||||||
|
Reverse the logic, by compressing as much data as possible from 'source' buffer
|
||||||
|
into already allocated buffer 'dest' of size 'targetDestSize'.
|
||||||
|
This function either compresses the entire 'source' content into 'dest' if it's large enough,
|
||||||
|
or fill 'dest' buffer completely with as much data as possible from 'source'.
|
||||||
|
*sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
|
||||||
|
New value is necessarily <= old value.
|
||||||
|
return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
|
||||||
|
or 0 if compression fails
|
||||||
|
*/
|
||||||
|
int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
LZ4_decompress_fast() :
|
||||||
|
originalSize : is the original and therefore uncompressed size
|
||||||
|
return : the number of bytes read from the source buffer (in other words, the compressed size)
|
||||||
|
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||||
|
Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
|
||||||
|
note : This function fully respect memory boundaries for properly formed compressed data.
|
||||||
|
It is a bit faster than LZ4_decompress_safe().
|
||||||
|
However, it does not provide any protection against intentionally modified data stream (malicious input).
|
||||||
|
Use this function in trusted environment only (data to decode comes from a trusted source).
|
||||||
|
*/
|
||||||
|
int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
LZ4_decompress_safe_partial() :
|
||||||
|
This function decompress a compressed block of size 'compressedSize' at position 'source'
|
||||||
|
into destination buffer 'dest' of size 'maxDecompressedSize'.
|
||||||
|
The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
|
||||||
|
reducing decompression time.
|
||||||
|
return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
|
||||||
|
Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
|
||||||
|
Always control how many bytes were decoded.
|
||||||
|
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||||
|
This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
|
||||||
|
*/
|
||||||
|
int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************************
|
||||||
|
* Streaming Compression Functions
|
||||||
|
***********************************************/
|
||||||
|
#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
|
||||||
|
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
|
||||||
|
/*
|
||||||
|
* LZ4_stream_t
|
||||||
|
* information structure to track an LZ4 stream.
|
||||||
|
* important : init this structure content before first use !
|
||||||
|
* note : only allocated directly the structure if you are statically linking LZ4
|
||||||
|
* If you are using liblz4 as a DLL, please use below construction methods instead.
|
||||||
|
*/
|
||||||
|
typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LZ4_resetStream
|
||||||
|
* Use this function to init an allocated LZ4_stream_t structure
|
||||||
|
*/
|
||||||
|
void LZ4_resetStream (LZ4_stream_t* streamPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LZ4_createStream will allocate and initialize an LZ4_stream_t structure
|
||||||
|
* LZ4_freeStream releases its memory.
|
||||||
|
* In the context of a DLL (liblz4), please use these methods rather than the static struct.
|
||||||
|
* They are more future proof, in case of a change of LZ4_stream_t size.
|
||||||
|
*/
|
||||||
|
LZ4_stream_t* LZ4_createStream(void);
|
||||||
|
int LZ4_freeStream (LZ4_stream_t* streamPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LZ4_loadDict
|
||||||
|
* Use this function to load a static dictionary into LZ4_stream.
|
||||||
|
* Any previous data will be forgotten, only 'dictionary' will remain in memory.
|
||||||
|
* Loading a size of 0 is allowed.
|
||||||
|
* Return : dictionary size, in bytes (necessarily <= 64 KB)
|
||||||
|
*/
|
||||||
|
int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LZ4_compress_fast_continue
|
||||||
|
* Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
|
||||||
|
* Important : Previous data blocks are assumed to still be present and unmodified !
|
||||||
|
* 'dst' buffer must be already allocated.
|
||||||
|
* If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
|
||||||
|
* If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
|
||||||
|
*/
|
||||||
|
int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LZ4_saveDict
|
||||||
|
* If previously compressed data block is not guaranteed to remain available at its memory location
|
||||||
|
* save it into a safer place (char* safeBuffer)
|
||||||
|
* Note : you don't need to call LZ4_loadDict() afterwards,
|
||||||
|
* dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue()
|
||||||
|
* Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error
|
||||||
|
*/
|
||||||
|
int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* Streaming Decompression Functions
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
#define LZ4_STREAMDECODESIZE_U64 4
|
||||||
|
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
|
||||||
|
typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t;
|
||||||
|
/*
|
||||||
|
* LZ4_streamDecode_t
|
||||||
|
* information structure to track an LZ4 stream.
|
||||||
|
* init this structure content using LZ4_setStreamDecode or memset() before first use !
|
||||||
|
*
|
||||||
|
* In the context of a DLL (liblz4) please prefer usage of construction methods below.
|
||||||
|
* They are more future proof, in case of a change of LZ4_streamDecode_t size in the future.
|
||||||
|
* LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure
|
||||||
|
* LZ4_freeStreamDecode releases its memory.
|
||||||
|
*/
|
||||||
|
LZ4_streamDecode_t* LZ4_createStreamDecode(void);
|
||||||
|
int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LZ4_setStreamDecode
|
||||||
|
* Use this function to instruct where to find the dictionary.
|
||||||
|
* Setting a size of 0 is allowed (same effect as reset).
|
||||||
|
* Return : 1 if OK, 0 if error
|
||||||
|
*/
|
||||||
|
int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*_continue() :
|
||||||
|
These decoding functions allow decompression of multiple blocks in "streaming" mode.
|
||||||
|
Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
|
||||||
|
In the case of a ring buffers, decoding buffer must be either :
|
||||||
|
- Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
|
||||||
|
In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
|
||||||
|
- Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
|
||||||
|
maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
|
||||||
|
In which case, encoding and decoding buffers do not need to be synchronized,
|
||||||
|
and encoding ring buffer can have any size, including small ones ( < 64 KB).
|
||||||
|
- _At least_ 64 KB + 8 bytes + maxBlockSize.
|
||||||
|
In which case, encoding and decoding buffers do not need to be synchronized,
|
||||||
|
and encoding ring buffer can have any size, including larger than decoding buffer.
|
||||||
|
Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
|
||||||
|
and indicate where it is saved using LZ4_setStreamDecode()
|
||||||
|
*/
|
||||||
|
int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
|
||||||
|
int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Advanced decoding functions :
|
||||||
|
*_usingDict() :
|
||||||
|
These decoding functions work the same as
|
||||||
|
a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
|
||||||
|
They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
|
||||||
|
*/
|
||||||
|
int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
|
||||||
|
int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Obsolete Functions
|
||||||
|
**************************************/
|
||||||
|
/* Deprecate Warnings */
|
||||||
|
/* Should these warnings messages be a problem,
|
||||||
|
it is generally possible to disable them,
|
||||||
|
with -Wno-deprecated-declarations for gcc
|
||||||
|
or _CRT_SECURE_NO_WARNINGS in Visual for example.
|
||||||
|
You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */
|
||||||
|
#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||||
|
# define LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||||
|
# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||||
|
# if (LZ4_GCC_VERSION >= 405) || defined(__clang__)
|
||||||
|
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||||
|
# elif (LZ4_GCC_VERSION >= 301)
|
||||||
|
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
|
||||||
|
# else
|
||||||
|
# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
|
||||||
|
# define LZ4_DEPRECATED(message)
|
||||||
|
# endif
|
||||||
|
#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */
|
||||||
|
|
||||||
|
/* Obsolete compression functions */
|
||||||
|
/* These functions are planned to start generate warnings by r131 approximately */
|
||||||
|
int LZ4_compress (const char* source, char* dest, int sourceSize);
|
||||||
|
int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
|
||||||
|
int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
|
||||||
|
int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||||
|
int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
|
||||||
|
int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||||
|
|
||||||
|
/* Obsolete decompression functions */
|
||||||
|
/* These function names are completely deprecated and must no longer be used.
|
||||||
|
They are only provided here for compatibility with older programs.
|
||||||
|
- LZ4_uncompress is the same as LZ4_decompress_fast
|
||||||
|
- LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
|
||||||
|
These function prototypes are now disabled; uncomment them only if you really need them.
|
||||||
|
It is highly recommended to stop using these prototypes and migrate to maintained ones */
|
||||||
|
/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
|
||||||
|
/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
|
||||||
|
|
||||||
|
/* Obsolete streaming functions; use new streaming interface whenever possible */
|
||||||
|
LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
|
||||||
|
LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
|
||||||
|
LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer);
|
||||||
|
LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state);
|
||||||
|
|
||||||
|
/* Obsolete streaming decoding functions */
|
||||||
|
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
|
||||||
|
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
189
contrib/liblz4/include/lz4/lz4hc.h
Normal file
189
contrib/liblz4/include/lz4/lz4hc.h
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
LZ4 HC - High Compression Mode of LZ4
|
||||||
|
Header File
|
||||||
|
Copyright (C) 2011-2015, Yann Collet.
|
||||||
|
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
You can contact the author at :
|
||||||
|
- LZ4 source repository : https://github.com/Cyan4973/lz4
|
||||||
|
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*****************************
|
||||||
|
* Includes
|
||||||
|
*****************************/
|
||||||
|
#include <stddef.h> /* size_t */
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Block Compression
|
||||||
|
**************************************/
|
||||||
|
int LZ4_compress_HC (const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel);
|
||||||
|
/*
|
||||||
|
LZ4_compress_HC :
|
||||||
|
Destination buffer 'dst' must be already allocated.
|
||||||
|
Compression completion is guaranteed if 'dst' buffer is sized to handle worst circumstances (data not compressible)
|
||||||
|
Worst size evaluation is provided by function LZ4_compressBound() (see "lz4.h")
|
||||||
|
srcSize : Max supported value is LZ4_MAX_INPUT_SIZE (see "lz4.h")
|
||||||
|
compressionLevel : Recommended values are between 4 and 9, although any value between 0 and 16 will work.
|
||||||
|
0 means "use default value" (see lz4hc.c).
|
||||||
|
Values >16 behave the same as 16.
|
||||||
|
return : the number of bytes written into buffer 'dst'
|
||||||
|
or 0 if compression fails.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Note :
|
||||||
|
Decompression functions are provided within LZ4 source code (see "lz4.h") (BSD license)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
int LZ4_sizeofStateHC(void);
|
||||||
|
int LZ4_compress_HC_extStateHC(void* state, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel);
|
||||||
|
/*
|
||||||
|
LZ4_compress_HC_extStateHC() :
|
||||||
|
Use this function if you prefer to manually allocate memory for compression tables.
|
||||||
|
To know how much memory must be allocated for the compression tables, use :
|
||||||
|
int LZ4_sizeofStateHC();
|
||||||
|
|
||||||
|
Allocated memory must be aligned on 8-bytes boundaries (which a normal malloc() will do properly).
|
||||||
|
|
||||||
|
The allocated memory can then be provided to the compression functions using 'void* state' parameter.
|
||||||
|
LZ4_compress_HC_extStateHC() is equivalent to previously described function.
|
||||||
|
It just uses externally allocated memory for stateHC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Streaming Compression
|
||||||
|
**************************************/
|
||||||
|
#define LZ4_STREAMHCSIZE 262192
|
||||||
|
#define LZ4_STREAMHCSIZE_SIZET (LZ4_STREAMHCSIZE / sizeof(size_t))
|
||||||
|
typedef struct { size_t table[LZ4_STREAMHCSIZE_SIZET]; } LZ4_streamHC_t;
|
||||||
|
/*
|
||||||
|
LZ4_streamHC_t
|
||||||
|
This structure allows static allocation of LZ4 HC streaming state.
|
||||||
|
State must then be initialized using LZ4_resetStreamHC() before first use.
|
||||||
|
|
||||||
|
Static allocation should only be used in combination with static linking.
|
||||||
|
If you want to use LZ4 as a DLL, please use construction functions below, which are future-proof.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
LZ4_streamHC_t* LZ4_createStreamHC(void);
|
||||||
|
int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr);
|
||||||
|
/*
|
||||||
|
These functions create and release memory for LZ4 HC streaming state.
|
||||||
|
Newly created states are already initialized.
|
||||||
|
Existing state space can be re-used anytime using LZ4_resetStreamHC().
|
||||||
|
If you use LZ4 as a DLL, use these functions instead of static structure allocation,
|
||||||
|
to avoid size mismatch between different versions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void LZ4_resetStreamHC (LZ4_streamHC_t* streamHCPtr, int compressionLevel);
|
||||||
|
int LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize);
|
||||||
|
|
||||||
|
int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr, const char* src, char* dst, int srcSize, int maxDstSize);
|
||||||
|
|
||||||
|
int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
These functions compress data in successive blocks of any size, using previous blocks as dictionary.
|
||||||
|
One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks.
|
||||||
|
There is an exception for ring buffers, which can be smaller 64 KB.
|
||||||
|
Such case is automatically detected and correctly handled by LZ4_compress_HC_continue().
|
||||||
|
|
||||||
|
Before starting compression, state must be properly initialized, using LZ4_resetStreamHC().
|
||||||
|
A first "fictional block" can then be designated as initial dictionary, using LZ4_loadDictHC() (Optional).
|
||||||
|
|
||||||
|
Then, use LZ4_compress_HC_continue() to compress each successive block.
|
||||||
|
It works like LZ4_compress_HC(), but use previous memory blocks as dictionary to improve compression.
|
||||||
|
Previous memory blocks (including initial dictionary when present) must remain accessible and unmodified during compression.
|
||||||
|
As a reminder, size 'dst' buffer to handle worst cases, using LZ4_compressBound(), to ensure success of compression operation.
|
||||||
|
|
||||||
|
If, for any reason, previous data blocks can't be preserved unmodified in memory during next compression block,
|
||||||
|
you must save it to a safer memory space, using LZ4_saveDictHC().
|
||||||
|
Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Deprecated Functions
|
||||||
|
**************************************/
|
||||||
|
/* Deprecate Warnings */
|
||||||
|
/* Should these warnings messages be a problem,
|
||||||
|
it is generally possible to disable them,
|
||||||
|
with -Wno-deprecated-declarations for gcc
|
||||||
|
or _CRT_SECURE_NO_WARNINGS in Visual for example.
|
||||||
|
You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */
|
||||||
|
#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||||
|
# define LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||||
|
# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||||
|
# if (LZ4_GCC_VERSION >= 405) || defined(__clang__)
|
||||||
|
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||||
|
# elif (LZ4_GCC_VERSION >= 301)
|
||||||
|
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
|
||||||
|
# else
|
||||||
|
# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
|
||||||
|
# define LZ4_DEPRECATED(message)
|
||||||
|
# endif
|
||||||
|
#endif // LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||||
|
|
||||||
|
/* compression functions */
|
||||||
|
/* these functions are planned to trigger warning messages by r131 approximately */
|
||||||
|
int LZ4_compressHC (const char* source, char* dest, int inputSize);
|
||||||
|
int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||||
|
int LZ4_compressHC2 (const char* source, char* dest, int inputSize, int compressionLevel);
|
||||||
|
int LZ4_compressHC2_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
|
||||||
|
int LZ4_compressHC_withStateHC (void* state, const char* source, char* dest, int inputSize);
|
||||||
|
int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||||
|
int LZ4_compressHC2_withStateHC (void* state, const char* source, char* dest, int inputSize, int compressionLevel);
|
||||||
|
int LZ4_compressHC2_limitedOutput_withStateHC(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
|
||||||
|
int LZ4_compressHC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize);
|
||||||
|
int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||||
|
|
||||||
|
/* Streaming functions following the older model; should no longer be used */
|
||||||
|
LZ4_DEPRECATED("use LZ4_createStreamHC() instead") void* LZ4_createHC (char* inputBuffer);
|
||||||
|
LZ4_DEPRECATED("use LZ4_saveDictHC() instead") char* LZ4_slideInputBufferHC (void* LZ4HC_Data);
|
||||||
|
LZ4_DEPRECATED("use LZ4_freeStreamHC() instead") int LZ4_freeHC (void* LZ4HC_Data);
|
||||||
|
LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel);
|
||||||
|
LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
|
||||||
|
LZ4_DEPRECATED("use LZ4_createStreamHC() instead") int LZ4_sizeofStreamStateHC(void);
|
||||||
|
LZ4_DEPRECATED("use LZ4_resetStreamHC() instead") int LZ4_resetStreamStateHC(void* state, char* inputBuffer);
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
1516
contrib/liblz4/src/lz4.c
Normal file
1516
contrib/liblz4/src/lz4.c
Normal file
File diff suppressed because it is too large
Load Diff
731
contrib/liblz4/src/lz4hc.c
Normal file
731
contrib/liblz4/src/lz4hc.c
Normal file
@ -0,0 +1,731 @@
|
|||||||
|
/*
|
||||||
|
LZ4 HC - High Compression Mode of LZ4
|
||||||
|
Copyright (C) 2011-2015, Yann Collet.
|
||||||
|
|
||||||
|
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
You can contact the author at :
|
||||||
|
- LZ4 source repository : https://github.com/Cyan4973/lz4
|
||||||
|
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Tuning Parameter
|
||||||
|
**************************************/
|
||||||
|
static const int LZ4HC_compressionLevel_default = 9;
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Includes
|
||||||
|
**************************************/
|
||||||
|
#include <lz4/lz4hc.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Local Compiler Options
|
||||||
|
**************************************/
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic ignored "-Wunused-function"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined (__clang__)
|
||||||
|
# pragma clang diagnostic ignored "-Wunused-function"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Common LZ4 definition
|
||||||
|
**************************************/
|
||||||
|
#define LZ4_COMMONDEFS_ONLY
|
||||||
|
#include "lz4.c"
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Local Constants
|
||||||
|
**************************************/
|
||||||
|
#define DICTIONARY_LOGSIZE 16
|
||||||
|
#define MAXD (1<<DICTIONARY_LOGSIZE)
|
||||||
|
#define MAXD_MASK (MAXD - 1)
|
||||||
|
|
||||||
|
#define HASH_LOG (DICTIONARY_LOGSIZE-1)
|
||||||
|
#define HASHTABLESIZE (1 << HASH_LOG)
|
||||||
|
#define HASH_MASK (HASHTABLESIZE - 1)
|
||||||
|
|
||||||
|
#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)
|
||||||
|
|
||||||
|
static const int g_maxCompressionLevel = 16;
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Local Types
|
||||||
|
**************************************/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
U32 hashTable[HASHTABLESIZE];
|
||||||
|
U16 chainTable[MAXD];
|
||||||
|
const BYTE* end; /* next block here to continue on current prefix */
|
||||||
|
const BYTE* base; /* All index relative to this position */
|
||||||
|
const BYTE* dictBase; /* alternate base for extDict */
|
||||||
|
BYTE* inputBuffer; /* deprecated */
|
||||||
|
U32 dictLimit; /* below that point, need extDict */
|
||||||
|
U32 lowLimit; /* below that point, no more dict */
|
||||||
|
U32 nextToUpdate; /* index from which to continue dictionary update */
|
||||||
|
U32 compressionLevel;
|
||||||
|
} LZ4HC_Data_Structure;
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Local Macros
|
||||||
|
**************************************/
|
||||||
|
#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-HASH_LOG))
|
||||||
|
//#define DELTANEXTU16(p) chainTable[(p) & MAXD_MASK] /* flexible, MAXD dependent */
|
||||||
|
#define DELTANEXTU16(p) chainTable[(U16)(p)] /* faster */
|
||||||
|
|
||||||
|
static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* HC Compression
|
||||||
|
**************************************/
|
||||||
|
static void LZ4HC_init (LZ4HC_Data_Structure* hc4, const BYTE* start)
|
||||||
|
{
|
||||||
|
MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable));
|
||||||
|
MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable));
|
||||||
|
hc4->nextToUpdate = 64 KB;
|
||||||
|
hc4->base = start - 64 KB;
|
||||||
|
hc4->end = start;
|
||||||
|
hc4->dictBase = start - 64 KB;
|
||||||
|
hc4->dictLimit = 64 KB;
|
||||||
|
hc4->lowLimit = 64 KB;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Update chains up to ip (excluded) */
|
||||||
|
FORCE_INLINE void LZ4HC_Insert (LZ4HC_Data_Structure* hc4, const BYTE* ip)
|
||||||
|
{
|
||||||
|
U16* chainTable = hc4->chainTable;
|
||||||
|
U32* HashTable = hc4->hashTable;
|
||||||
|
const BYTE* const base = hc4->base;
|
||||||
|
const U32 target = (U32)(ip - base);
|
||||||
|
U32 idx = hc4->nextToUpdate;
|
||||||
|
|
||||||
|
while(idx < target)
|
||||||
|
{
|
||||||
|
U32 h = LZ4HC_hashPtr(base+idx);
|
||||||
|
size_t delta = idx - HashTable[h];
|
||||||
|
if (delta>MAX_DISTANCE) delta = MAX_DISTANCE;
|
||||||
|
DELTANEXTU16(idx) = (U16)delta;
|
||||||
|
HashTable[h] = idx;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
hc4->nextToUpdate = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FORCE_INLINE int LZ4HC_InsertAndFindBestMatch (LZ4HC_Data_Structure* hc4, /* Index table will be updated */
|
||||||
|
const BYTE* ip, const BYTE* const iLimit,
|
||||||
|
const BYTE** matchpos,
|
||||||
|
const int maxNbAttempts)
|
||||||
|
{
|
||||||
|
U16* const chainTable = hc4->chainTable;
|
||||||
|
U32* const HashTable = hc4->hashTable;
|
||||||
|
const BYTE* const base = hc4->base;
|
||||||
|
const BYTE* const dictBase = hc4->dictBase;
|
||||||
|
const U32 dictLimit = hc4->dictLimit;
|
||||||
|
const U32 lowLimit = (hc4->lowLimit + 64 KB > (U32)(ip-base)) ? hc4->lowLimit : (U32)(ip - base) - (64 KB - 1);
|
||||||
|
U32 matchIndex;
|
||||||
|
const BYTE* match;
|
||||||
|
int nbAttempts=maxNbAttempts;
|
||||||
|
size_t ml=0;
|
||||||
|
|
||||||
|
/* HC4 match finder */
|
||||||
|
LZ4HC_Insert(hc4, ip);
|
||||||
|
matchIndex = HashTable[LZ4HC_hashPtr(ip)];
|
||||||
|
|
||||||
|
while ((matchIndex>=lowLimit) && (nbAttempts))
|
||||||
|
{
|
||||||
|
nbAttempts--;
|
||||||
|
if (matchIndex >= dictLimit)
|
||||||
|
{
|
||||||
|
match = base + matchIndex;
|
||||||
|
if (*(match+ml) == *(ip+ml)
|
||||||
|
&& (LZ4_read32(match) == LZ4_read32(ip)))
|
||||||
|
{
|
||||||
|
size_t mlt = LZ4_count(ip+MINMATCH, match+MINMATCH, iLimit) + MINMATCH;
|
||||||
|
if (mlt > ml) { ml = mlt; *matchpos = match; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
match = dictBase + matchIndex;
|
||||||
|
if (LZ4_read32(match) == LZ4_read32(ip))
|
||||||
|
{
|
||||||
|
size_t mlt;
|
||||||
|
const BYTE* vLimit = ip + (dictLimit - matchIndex);
|
||||||
|
if (vLimit > iLimit) vLimit = iLimit;
|
||||||
|
mlt = LZ4_count(ip+MINMATCH, match+MINMATCH, vLimit) + MINMATCH;
|
||||||
|
if ((ip+mlt == vLimit) && (vLimit < iLimit))
|
||||||
|
mlt += LZ4_count(ip+mlt, base+dictLimit, iLimit);
|
||||||
|
if (mlt > ml) { ml = mlt; *matchpos = base + matchIndex; } /* virtual matchpos */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matchIndex -= DELTANEXTU16(matchIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)ml;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch (
|
||||||
|
LZ4HC_Data_Structure* hc4,
|
||||||
|
const BYTE* const ip,
|
||||||
|
const BYTE* const iLowLimit,
|
||||||
|
const BYTE* const iHighLimit,
|
||||||
|
int longest,
|
||||||
|
const BYTE** matchpos,
|
||||||
|
const BYTE** startpos,
|
||||||
|
const int maxNbAttempts)
|
||||||
|
{
|
||||||
|
U16* const chainTable = hc4->chainTable;
|
||||||
|
U32* const HashTable = hc4->hashTable;
|
||||||
|
const BYTE* const base = hc4->base;
|
||||||
|
const U32 dictLimit = hc4->dictLimit;
|
||||||
|
const BYTE* const lowPrefixPtr = base + dictLimit;
|
||||||
|
const U32 lowLimit = (hc4->lowLimit + 64 KB > (U32)(ip-base)) ? hc4->lowLimit : (U32)(ip - base) - (64 KB - 1);
|
||||||
|
const BYTE* const dictBase = hc4->dictBase;
|
||||||
|
U32 matchIndex;
|
||||||
|
int nbAttempts = maxNbAttempts;
|
||||||
|
int delta = (int)(ip-iLowLimit);
|
||||||
|
|
||||||
|
|
||||||
|
/* First Match */
|
||||||
|
LZ4HC_Insert(hc4, ip);
|
||||||
|
matchIndex = HashTable[LZ4HC_hashPtr(ip)];
|
||||||
|
|
||||||
|
while ((matchIndex>=lowLimit) && (nbAttempts))
|
||||||
|
{
|
||||||
|
nbAttempts--;
|
||||||
|
if (matchIndex >= dictLimit)
|
||||||
|
{
|
||||||
|
const BYTE* matchPtr = base + matchIndex;
|
||||||
|
if (*(iLowLimit + longest) == *(matchPtr - delta + longest))
|
||||||
|
if (LZ4_read32(matchPtr) == LZ4_read32(ip))
|
||||||
|
{
|
||||||
|
int mlt = MINMATCH + LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit);
|
||||||
|
int back = 0;
|
||||||
|
|
||||||
|
while ((ip+back>iLowLimit)
|
||||||
|
&& (matchPtr+back > lowPrefixPtr)
|
||||||
|
&& (ip[back-1] == matchPtr[back-1]))
|
||||||
|
back--;
|
||||||
|
|
||||||
|
mlt -= back;
|
||||||
|
|
||||||
|
if (mlt > longest)
|
||||||
|
{
|
||||||
|
longest = (int)mlt;
|
||||||
|
*matchpos = matchPtr+back;
|
||||||
|
*startpos = ip+back;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const BYTE* matchPtr = dictBase + matchIndex;
|
||||||
|
if (LZ4_read32(matchPtr) == LZ4_read32(ip))
|
||||||
|
{
|
||||||
|
size_t mlt;
|
||||||
|
int back=0;
|
||||||
|
const BYTE* vLimit = ip + (dictLimit - matchIndex);
|
||||||
|
if (vLimit > iHighLimit) vLimit = iHighLimit;
|
||||||
|
mlt = LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;
|
||||||
|
if ((ip+mlt == vLimit) && (vLimit < iHighLimit))
|
||||||
|
mlt += LZ4_count(ip+mlt, base+dictLimit, iHighLimit);
|
||||||
|
while ((ip+back > iLowLimit) && (matchIndex+back > lowLimit) && (ip[back-1] == matchPtr[back-1])) back--;
|
||||||
|
mlt -= back;
|
||||||
|
if ((int)mlt > longest) { longest = (int)mlt; *matchpos = base + matchIndex + back; *startpos = ip+back; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matchIndex -= DELTANEXTU16(matchIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return longest;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum { noLimit = 0, limitedOutput = 1 } limitedOutput_directive;
|
||||||
|
|
||||||
|
#define LZ4HC_DEBUG 0
|
||||||
|
#if LZ4HC_DEBUG
|
||||||
|
static unsigned debug = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FORCE_INLINE int LZ4HC_encodeSequence (
|
||||||
|
const BYTE** ip,
|
||||||
|
BYTE** op,
|
||||||
|
const BYTE** anchor,
|
||||||
|
int matchLength,
|
||||||
|
const BYTE* const match,
|
||||||
|
limitedOutput_directive limitedOutputBuffer,
|
||||||
|
BYTE* oend)
|
||||||
|
{
|
||||||
|
int length;
|
||||||
|
BYTE* token;
|
||||||
|
|
||||||
|
#if LZ4HC_DEBUG
|
||||||
|
if (debug) printf("literal : %u -- match : %u -- offset : %u\n", (U32)(*ip - *anchor), (U32)matchLength, (U32)(*ip-match));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Encode Literal length */
|
||||||
|
length = (int)(*ip - *anchor);
|
||||||
|
token = (*op)++;
|
||||||
|
if ((limitedOutputBuffer) && ((*op + (length>>8) + length + (2 + 1 + LASTLITERALS)) > oend)) return 1; /* Check output limit */
|
||||||
|
if (length>=(int)RUN_MASK) { int len; *token=(RUN_MASK<<ML_BITS); len = length-RUN_MASK; for(; len > 254 ; len-=255) *(*op)++ = 255; *(*op)++ = (BYTE)len; }
|
||||||
|
else *token = (BYTE)(length<<ML_BITS);
|
||||||
|
|
||||||
|
/* Copy Literals */
|
||||||
|
LZ4_wildCopy(*op, *anchor, (*op) + length);
|
||||||
|
*op += length;
|
||||||
|
|
||||||
|
/* Encode Offset */
|
||||||
|
LZ4_writeLE16(*op, (U16)(*ip-match)); *op += 2;
|
||||||
|
|
||||||
|
/* Encode MatchLength */
|
||||||
|
length = (int)(matchLength-MINMATCH);
|
||||||
|
if ((limitedOutputBuffer) && (*op + (length>>8) + (1 + LASTLITERALS) > oend)) return 1; /* Check output limit */
|
||||||
|
if (length>=(int)ML_MASK) { *token+=ML_MASK; length-=ML_MASK; for(; length > 509 ; length-=510) { *(*op)++ = 255; *(*op)++ = 255; } if (length > 254) { length-=255; *(*op)++ = 255; } *(*op)++ = (BYTE)length; }
|
||||||
|
else *token += (BYTE)(length);
|
||||||
|
|
||||||
|
/* Prepare next loop */
|
||||||
|
*ip += matchLength;
|
||||||
|
*anchor = *ip;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int LZ4HC_compress_generic (
|
||||||
|
void* ctxvoid,
|
||||||
|
const char* source,
|
||||||
|
char* dest,
|
||||||
|
int inputSize,
|
||||||
|
int maxOutputSize,
|
||||||
|
int compressionLevel,
|
||||||
|
limitedOutput_directive limit
|
||||||
|
)
|
||||||
|
{
|
||||||
|
LZ4HC_Data_Structure* ctx = (LZ4HC_Data_Structure*) ctxvoid;
|
||||||
|
const BYTE* ip = (const BYTE*) source;
|
||||||
|
const BYTE* anchor = ip;
|
||||||
|
const BYTE* const iend = ip + inputSize;
|
||||||
|
const BYTE* const mflimit = iend - MFLIMIT;
|
||||||
|
const BYTE* const matchlimit = (iend - LASTLITERALS);
|
||||||
|
|
||||||
|
BYTE* op = (BYTE*) dest;
|
||||||
|
BYTE* const oend = op + maxOutputSize;
|
||||||
|
|
||||||
|
unsigned maxNbAttempts;
|
||||||
|
int ml, ml2, ml3, ml0;
|
||||||
|
const BYTE* ref=NULL;
|
||||||
|
const BYTE* start2=NULL;
|
||||||
|
const BYTE* ref2=NULL;
|
||||||
|
const BYTE* start3=NULL;
|
||||||
|
const BYTE* ref3=NULL;
|
||||||
|
const BYTE* start0;
|
||||||
|
const BYTE* ref0;
|
||||||
|
|
||||||
|
|
||||||
|
/* init */
|
||||||
|
if (compressionLevel > g_maxCompressionLevel) compressionLevel = g_maxCompressionLevel;
|
||||||
|
if (compressionLevel < 1) compressionLevel = LZ4HC_compressionLevel_default;
|
||||||
|
maxNbAttempts = 1 << (compressionLevel-1);
|
||||||
|
ctx->end += inputSize;
|
||||||
|
|
||||||
|
ip++;
|
||||||
|
|
||||||
|
/* Main Loop */
|
||||||
|
while (ip < mflimit)
|
||||||
|
{
|
||||||
|
ml = LZ4HC_InsertAndFindBestMatch (ctx, ip, matchlimit, (&ref), maxNbAttempts);
|
||||||
|
if (!ml) { ip++; continue; }
|
||||||
|
|
||||||
|
/* saved, in case we would skip too much */
|
||||||
|
start0 = ip;
|
||||||
|
ref0 = ref;
|
||||||
|
ml0 = ml;
|
||||||
|
|
||||||
|
_Search2:
|
||||||
|
if (ip+ml < mflimit)
|
||||||
|
ml2 = LZ4HC_InsertAndGetWiderMatch(ctx, ip + ml - 2, ip + 1, matchlimit, ml, &ref2, &start2, maxNbAttempts);
|
||||||
|
else ml2 = ml;
|
||||||
|
|
||||||
|
if (ml2 == ml) /* No better match */
|
||||||
|
{
|
||||||
|
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start0 < ip)
|
||||||
|
{
|
||||||
|
if (start2 < ip + ml0) /* empirical */
|
||||||
|
{
|
||||||
|
ip = start0;
|
||||||
|
ref = ref0;
|
||||||
|
ml = ml0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Here, start0==ip */
|
||||||
|
if ((start2 - ip) < 3) /* First Match too small : removed */
|
||||||
|
{
|
||||||
|
ml = ml2;
|
||||||
|
ip = start2;
|
||||||
|
ref =ref2;
|
||||||
|
goto _Search2;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Search3:
|
||||||
|
/*
|
||||||
|
* Currently we have :
|
||||||
|
* ml2 > ml1, and
|
||||||
|
* ip1+3 <= ip2 (usually < ip1+ml1)
|
||||||
|
*/
|
||||||
|
if ((start2 - ip) < OPTIMAL_ML)
|
||||||
|
{
|
||||||
|
int correction;
|
||||||
|
int new_ml = ml;
|
||||||
|
if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML;
|
||||||
|
if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH;
|
||||||
|
correction = new_ml - (int)(start2 - ip);
|
||||||
|
if (correction > 0)
|
||||||
|
{
|
||||||
|
start2 += correction;
|
||||||
|
ref2 += correction;
|
||||||
|
ml2 -= correction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */
|
||||||
|
|
||||||
|
if (start2 + ml2 < mflimit)
|
||||||
|
ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3, maxNbAttempts);
|
||||||
|
else ml3 = ml2;
|
||||||
|
|
||||||
|
if (ml3 == ml2) /* No better match : 2 sequences to encode */
|
||||||
|
{
|
||||||
|
/* ip & ref are known; Now for ml */
|
||||||
|
if (start2 < ip+ml) ml = (int)(start2 - ip);
|
||||||
|
/* Now, encode 2 sequences */
|
||||||
|
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
|
||||||
|
ip = start2;
|
||||||
|
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml2, ref2, limit, oend)) return 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start3 < ip+ml+3) /* Not enough space for match 2 : remove it */
|
||||||
|
{
|
||||||
|
if (start3 >= (ip+ml)) /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */
|
||||||
|
{
|
||||||
|
if (start2 < ip+ml)
|
||||||
|
{
|
||||||
|
int correction = (int)(ip+ml - start2);
|
||||||
|
start2 += correction;
|
||||||
|
ref2 += correction;
|
||||||
|
ml2 -= correction;
|
||||||
|
if (ml2 < MINMATCH)
|
||||||
|
{
|
||||||
|
start2 = start3;
|
||||||
|
ref2 = ref3;
|
||||||
|
ml2 = ml3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
|
||||||
|
ip = start3;
|
||||||
|
ref = ref3;
|
||||||
|
ml = ml3;
|
||||||
|
|
||||||
|
start0 = start2;
|
||||||
|
ref0 = ref2;
|
||||||
|
ml0 = ml2;
|
||||||
|
goto _Search2;
|
||||||
|
}
|
||||||
|
|
||||||
|
start2 = start3;
|
||||||
|
ref2 = ref3;
|
||||||
|
ml2 = ml3;
|
||||||
|
goto _Search3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OK, now we have 3 ascending matches; let's write at least the first one
|
||||||
|
* ip & ref are known; Now for ml
|
||||||
|
*/
|
||||||
|
if (start2 < ip+ml)
|
||||||
|
{
|
||||||
|
if ((start2 - ip) < (int)ML_MASK)
|
||||||
|
{
|
||||||
|
int correction;
|
||||||
|
if (ml > OPTIMAL_ML) ml = OPTIMAL_ML;
|
||||||
|
if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH;
|
||||||
|
correction = ml - (int)(start2 - ip);
|
||||||
|
if (correction > 0)
|
||||||
|
{
|
||||||
|
start2 += correction;
|
||||||
|
ref2 += correction;
|
||||||
|
ml2 -= correction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ml = (int)(start2 - ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) return 0;
|
||||||
|
|
||||||
|
ip = start2;
|
||||||
|
ref = ref2;
|
||||||
|
ml = ml2;
|
||||||
|
|
||||||
|
start2 = start3;
|
||||||
|
ref2 = ref3;
|
||||||
|
ml2 = ml3;
|
||||||
|
|
||||||
|
goto _Search3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode Last Literals */
|
||||||
|
{
|
||||||
|
int lastRun = (int)(iend - anchor);
|
||||||
|
if ((limit) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */
|
||||||
|
if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<<ML_BITS); lastRun-=RUN_MASK; for(; lastRun > 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; }
|
||||||
|
else *op++ = (BYTE)(lastRun<<ML_BITS);
|
||||||
|
memcpy(op, anchor, iend - anchor);
|
||||||
|
op += iend-anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End */
|
||||||
|
return (int) (((char*)op)-dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int LZ4_sizeofStateHC(void) { return sizeof(LZ4HC_Data_Structure); }
|
||||||
|
|
||||||
|
int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel)
|
||||||
|
{
|
||||||
|
if (((size_t)(state)&(sizeof(void*)-1)) != 0) return 0; /* Error : state is not aligned for pointers (32 or 64 bits) */
|
||||||
|
LZ4HC_init ((LZ4HC_Data_Structure*)state, (const BYTE*)src);
|
||||||
|
if (maxDstSize < LZ4_compressBound(srcSize))
|
||||||
|
return LZ4HC_compress_generic (state, src, dst, srcSize, maxDstSize, compressionLevel, limitedOutput);
|
||||||
|
else
|
||||||
|
return LZ4HC_compress_generic (state, src, dst, srcSize, maxDstSize, compressionLevel, noLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LZ4_compress_HC(const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel)
|
||||||
|
{
|
||||||
|
LZ4HC_Data_Structure state;
|
||||||
|
return LZ4_compress_HC_extStateHC(&state, src, dst, srcSize, maxDstSize, compressionLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Streaming Functions
|
||||||
|
**************************************/
|
||||||
|
/* allocation */
|
||||||
|
LZ4_streamHC_t* LZ4_createStreamHC(void) { return (LZ4_streamHC_t*)malloc(sizeof(LZ4_streamHC_t)); }
|
||||||
|
int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) { free(LZ4_streamHCPtr); return 0; }
|
||||||
|
|
||||||
|
|
||||||
|
/* initialization */
|
||||||
|
void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)
|
||||||
|
{
|
||||||
|
LZ4_STATIC_ASSERT(sizeof(LZ4HC_Data_Structure) <= sizeof(LZ4_streamHC_t)); /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */
|
||||||
|
((LZ4HC_Data_Structure*)LZ4_streamHCPtr)->base = NULL;
|
||||||
|
((LZ4HC_Data_Structure*)LZ4_streamHCPtr)->compressionLevel = (unsigned)compressionLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, const char* dictionary, int dictSize)
|
||||||
|
{
|
||||||
|
LZ4HC_Data_Structure* ctxPtr = (LZ4HC_Data_Structure*) LZ4_streamHCPtr;
|
||||||
|
if (dictSize > 64 KB)
|
||||||
|
{
|
||||||
|
dictionary += dictSize - 64 KB;
|
||||||
|
dictSize = 64 KB;
|
||||||
|
}
|
||||||
|
LZ4HC_init (ctxPtr, (const BYTE*)dictionary);
|
||||||
|
if (dictSize >= 4) LZ4HC_Insert (ctxPtr, (const BYTE*)dictionary +(dictSize-3));
|
||||||
|
ctxPtr->end = (const BYTE*)dictionary + dictSize;
|
||||||
|
return dictSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* compression */
|
||||||
|
|
||||||
|
static void LZ4HC_setExternalDict(LZ4HC_Data_Structure* ctxPtr, const BYTE* newBlock)
|
||||||
|
{
|
||||||
|
if (ctxPtr->end >= ctxPtr->base + 4)
|
||||||
|
LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */
|
||||||
|
/* Only one memory segment for extDict, so any previous extDict is lost at this stage */
|
||||||
|
ctxPtr->lowLimit = ctxPtr->dictLimit;
|
||||||
|
ctxPtr->dictLimit = (U32)(ctxPtr->end - ctxPtr->base);
|
||||||
|
ctxPtr->dictBase = ctxPtr->base;
|
||||||
|
ctxPtr->base = newBlock - ctxPtr->dictLimit;
|
||||||
|
ctxPtr->end = newBlock;
|
||||||
|
ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int LZ4_compressHC_continue_generic (LZ4HC_Data_Structure* ctxPtr,
|
||||||
|
const char* source, char* dest,
|
||||||
|
int inputSize, int maxOutputSize, limitedOutput_directive limit)
|
||||||
|
{
|
||||||
|
/* auto-init if forgotten */
|
||||||
|
if (ctxPtr->base == NULL)
|
||||||
|
LZ4HC_init (ctxPtr, (const BYTE*) source);
|
||||||
|
|
||||||
|
/* Check overflow */
|
||||||
|
if ((size_t)(ctxPtr->end - ctxPtr->base) > 2 GB)
|
||||||
|
{
|
||||||
|
size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->base) - ctxPtr->dictLimit;
|
||||||
|
if (dictSize > 64 KB) dictSize = 64 KB;
|
||||||
|
|
||||||
|
LZ4_loadDictHC((LZ4_streamHC_t*)ctxPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if blocks follow each other */
|
||||||
|
if ((const BYTE*)source != ctxPtr->end)
|
||||||
|
LZ4HC_setExternalDict(ctxPtr, (const BYTE*)source);
|
||||||
|
|
||||||
|
/* Check overlapping input/dictionary space */
|
||||||
|
{
|
||||||
|
const BYTE* sourceEnd = (const BYTE*) source + inputSize;
|
||||||
|
const BYTE* dictBegin = ctxPtr->dictBase + ctxPtr->lowLimit;
|
||||||
|
const BYTE* dictEnd = ctxPtr->dictBase + ctxPtr->dictLimit;
|
||||||
|
if ((sourceEnd > dictBegin) && ((const BYTE*)source < dictEnd))
|
||||||
|
{
|
||||||
|
if (sourceEnd > dictEnd) sourceEnd = dictEnd;
|
||||||
|
ctxPtr->lowLimit = (U32)(sourceEnd - ctxPtr->dictBase);
|
||||||
|
if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) ctxPtr->lowLimit = ctxPtr->dictLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LZ4HC_compress_generic (ctxPtr, source, dest, inputSize, maxOutputSize, ctxPtr->compressionLevel, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize)
|
||||||
|
{
|
||||||
|
if (maxOutputSize < LZ4_compressBound(inputSize))
|
||||||
|
return LZ4_compressHC_continue_generic ((LZ4HC_Data_Structure*)LZ4_streamHCPtr, source, dest, inputSize, maxOutputSize, limitedOutput);
|
||||||
|
else
|
||||||
|
return LZ4_compressHC_continue_generic ((LZ4HC_Data_Structure*)LZ4_streamHCPtr, source, dest, inputSize, maxOutputSize, noLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* dictionary saving */
|
||||||
|
|
||||||
|
int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize)
|
||||||
|
{
|
||||||
|
LZ4HC_Data_Structure* streamPtr = (LZ4HC_Data_Structure*)LZ4_streamHCPtr;
|
||||||
|
int prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit));
|
||||||
|
if (dictSize > 64 KB) dictSize = 64 KB;
|
||||||
|
if (dictSize < 4) dictSize = 0;
|
||||||
|
if (dictSize > prefixSize) dictSize = prefixSize;
|
||||||
|
memmove(safeBuffer, streamPtr->end - dictSize, dictSize);
|
||||||
|
{
|
||||||
|
U32 endIndex = (U32)(streamPtr->end - streamPtr->base);
|
||||||
|
streamPtr->end = (const BYTE*)safeBuffer + dictSize;
|
||||||
|
streamPtr->base = streamPtr->end - endIndex;
|
||||||
|
streamPtr->dictLimit = endIndex - dictSize;
|
||||||
|
streamPtr->lowLimit = endIndex - dictSize;
|
||||||
|
if (streamPtr->nextToUpdate < streamPtr->dictLimit) streamPtr->nextToUpdate = streamPtr->dictLimit;
|
||||||
|
}
|
||||||
|
return dictSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***********************************
|
||||||
|
* Deprecated Functions
|
||||||
|
***********************************/
|
||||||
|
/* Deprecated compression functions */
|
||||||
|
/* These functions are planned to start generate warnings by r131 approximately */
|
||||||
|
int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); }
|
||||||
|
int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); }
|
||||||
|
int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }
|
||||||
|
int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); }
|
||||||
|
int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); }
|
||||||
|
int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); }
|
||||||
|
int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }
|
||||||
|
int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); }
|
||||||
|
int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); }
|
||||||
|
int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); }
|
||||||
|
|
||||||
|
|
||||||
|
/* Deprecated streaming functions */
|
||||||
|
/* These functions currently generate deprecation warnings */
|
||||||
|
int LZ4_sizeofStreamStateHC(void) { return LZ4_STREAMHCSIZE; }
|
||||||
|
|
||||||
|
int LZ4_resetStreamStateHC(void* state, char* inputBuffer)
|
||||||
|
{
|
||||||
|
if ((((size_t)state) & (sizeof(void*)-1)) != 0) return 1; /* Error : pointer is not aligned for pointer (32 or 64 bits) */
|
||||||
|
LZ4HC_init((LZ4HC_Data_Structure*)state, (const BYTE*)inputBuffer);
|
||||||
|
((LZ4HC_Data_Structure*)state)->inputBuffer = (BYTE*)inputBuffer;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* LZ4_createHC (char* inputBuffer)
|
||||||
|
{
|
||||||
|
void* hc4 = ALLOCATOR(1, sizeof(LZ4HC_Data_Structure));
|
||||||
|
if (hc4 == NULL) return NULL; /* not enough memory */
|
||||||
|
LZ4HC_init ((LZ4HC_Data_Structure*)hc4, (const BYTE*)inputBuffer);
|
||||||
|
((LZ4HC_Data_Structure*)hc4)->inputBuffer = (BYTE*)inputBuffer;
|
||||||
|
return hc4;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LZ4_freeHC (void* LZ4HC_Data)
|
||||||
|
{
|
||||||
|
FREEMEM(LZ4HC_Data);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel)
|
||||||
|
{
|
||||||
|
return LZ4HC_compress_generic (LZ4HC_Data, source, dest, inputSize, 0, compressionLevel, noLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel)
|
||||||
|
{
|
||||||
|
return LZ4HC_compress_generic (LZ4HC_Data, source, dest, inputSize, maxOutputSize, compressionLevel, limitedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* LZ4_slideInputBufferHC(void* LZ4HC_Data)
|
||||||
|
{
|
||||||
|
LZ4HC_Data_Structure* hc4 = (LZ4HC_Data_Structure*)LZ4HC_Data;
|
||||||
|
int dictSize = LZ4_saveDictHC((LZ4_streamHC_t*)LZ4HC_Data, (char*)(hc4->inputBuffer), 64 KB);
|
||||||
|
return (char*)(hc4->inputBuffer + dictSize);
|
||||||
|
}
|
13
contrib/libmetrohash/CMakeLists.txt
Normal file
13
contrib/libmetrohash/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
include_directories (${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
IF (NOT AARCH64) # Не используется. Портировать не сложно.
|
||||||
|
SET(SOURCES_ONLY_ON_X86_64 src/metrohash128crc.cpp)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
add_library(metrohash
|
||||||
|
src/metrohash.h
|
||||||
|
src/testvector.h
|
||||||
|
|
||||||
|
src/metrohash64.cpp
|
||||||
|
src/metrohash128.cpp
|
||||||
|
${SOURCES_ONLY_ON_X86_64})
|
22
contrib/libmetrohash/LICENSE
Normal file
22
contrib/libmetrohash/LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 J. Andrew Rogers
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
24
contrib/libmetrohash/README.md
Normal file
24
contrib/libmetrohash/README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
## MetroHash: Faster, Better Hash Functions
|
||||||
|
|
||||||
|
MetroHash is a set of state-of-the-art hash functions for *non-cryptographic* use cases. They are notable for being algorithmically generated in addition to their exceptional performance. The set of published hash functions may be expanded in the future, having been selected from a very large set of hash functions that have been constructed this way.
|
||||||
|
|
||||||
|
* Fastest general-purpose functions for bulk hashing.
|
||||||
|
* Fastest general-purpose functions for small, variable length keys.
|
||||||
|
* Robust statistical bias profile, similar to the MD5 cryptographic hash.
|
||||||
|
* 64-bit, 128-bit, and 128-bit CRC variants currently available.
|
||||||
|
* Optimized for modern x86-64 microarchitectures.
|
||||||
|
* Elegant, compact, readable functions.
|
||||||
|
|
||||||
|
You can read more about the design and history [here](http://www.jandrewrogers.com/2015/05/27/metrohash/).
|
||||||
|
|
||||||
|
Six hash functions have been included in the initial release:
|
||||||
|
|
||||||
|
* 64-bit hash functions, "metrohash64_1" and "metrohash64_2"
|
||||||
|
* 128-bit hash functions, "metrohash128_1" and "metrohash128_2"
|
||||||
|
* 128-bit hash functions using CRC instructions, "metrohash128crc_1" and "metrohash128crc_2"
|
||||||
|
|
||||||
|
Hash functions in the same family are effectively statistically unique. In other words, if you need two hash functions for a bloom filter, you can use "metrohash64_1" and "metrohash64_2" in the same implementation without issue. An unbounded set of statistically unique functions can be generated in each family. The functions in this repo were generated specifically for public release.
|
||||||
|
|
||||||
|
The hash function generation software made no effort toward portability. While these hash functions should be easily portable to big-endian microarchitectures, they have not been tested on them and the performance optimization algorithms were not targeted at them. ARM64 microarchitectures might be a worthwhile hash function generation targets if I had the hardware.
|
||||||
|
|
||||||
|
|
7
contrib/libmetrohash/VERSION
Normal file
7
contrib/libmetrohash/VERSION
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
origin: git@github.com:jandrewrogers/MetroHash.git
|
||||||
|
commit d9dee18a54a8a6766e24c1950b814ac7ca9d1a89
|
||||||
|
Merge: 761e8a4 3d06b24
|
||||||
|
Author: J. Andrew Rogers <andrew@jarbox.org>
|
||||||
|
Date: Sat Jun 6 16:12:06 2015 -0700
|
||||||
|
|
||||||
|
modified README
|
73
contrib/libmetrohash/src/metrohash.h
Normal file
73
contrib/libmetrohash/src/metrohash.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// metrohash.h
|
||||||
|
//
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 J. Andrew Rogers
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef METROHASH_METROHASH_H
|
||||||
|
#define METROHASH_METROHASH_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// MetroHash 64-bit hash functions
|
||||||
|
void metrohash64_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out);
|
||||||
|
void metrohash64_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out);
|
||||||
|
|
||||||
|
// MetroHash 128-bit hash functions
|
||||||
|
void metrohash128_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out);
|
||||||
|
void metrohash128_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out);
|
||||||
|
|
||||||
|
// MetroHash 128-bit hash functions using CRC instruction
|
||||||
|
void metrohash128crc_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out);
|
||||||
|
void metrohash128crc_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out);
|
||||||
|
|
||||||
|
|
||||||
|
/* rotate right idiom recognized by compiler*/
|
||||||
|
inline static uint64_t rotate_right(uint64_t v, unsigned k)
|
||||||
|
{
|
||||||
|
return (v >> k) | (v << (64 - k));
|
||||||
|
}
|
||||||
|
|
||||||
|
// unaligned reads, fast and safe on Nehalem and later microarchitectures
|
||||||
|
inline static uint64_t read_u64(const void * const ptr)
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(*reinterpret_cast<const uint64_t*>(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static uint64_t read_u32(const void * const ptr)
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static uint64_t read_u16(const void * const ptr)
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(*reinterpret_cast<const uint16_t*>(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static uint64_t read_u8 (const void * const ptr)
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(*reinterpret_cast<const uint8_t *>(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // #ifndef METROHASH_METROHASH_H
|
178
contrib/libmetrohash/src/metrohash128.cpp
Normal file
178
contrib/libmetrohash/src/metrohash128.cpp
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
// metrohash128.cpp
|
||||||
|
//
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 J. Andrew Rogers
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "metrohash.h"
|
||||||
|
|
||||||
|
void metrohash128_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out)
|
||||||
|
{
|
||||||
|
static const uint64_t k0 = 0xC83A91E1;
|
||||||
|
static const uint64_t k1 = 0x8648DBDB;
|
||||||
|
static const uint64_t k2 = 0x7BDEC03B;
|
||||||
|
static const uint64_t k3 = 0x2F5870A5;
|
||||||
|
|
||||||
|
const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key);
|
||||||
|
const uint8_t * const end = ptr + len;
|
||||||
|
|
||||||
|
uint64_t v[4];
|
||||||
|
|
||||||
|
v[0] = ((static_cast<uint64_t>(seed) - k0) * k3) + len;
|
||||||
|
v[1] = ((static_cast<uint64_t>(seed) + k1) * k2) + len;
|
||||||
|
|
||||||
|
if (len >= 32)
|
||||||
|
{
|
||||||
|
v[2] = ((static_cast<uint64_t>(seed) + k0) * k2) + len;
|
||||||
|
v[3] = ((static_cast<uint64_t>(seed) - k1) * k3) + len;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2];
|
||||||
|
v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3];
|
||||||
|
v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0];
|
||||||
|
v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1];
|
||||||
|
}
|
||||||
|
while (ptr <= (end - 32));
|
||||||
|
|
||||||
|
v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 26) * k1;
|
||||||
|
v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 26) * k0;
|
||||||
|
v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 26) * k1;
|
||||||
|
v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 30) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 16)
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],33) * k3;
|
||||||
|
v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],33) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 17) * k1;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 17) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 8)
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],33) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 20) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 4)
|
||||||
|
{
|
||||||
|
v[1] += read_u32(ptr) * k2; ptr += 4; v[1] = rotate_right(v[1],33) * k3;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 18) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 2)
|
||||||
|
{
|
||||||
|
v[0] += read_u16(ptr) * k2; ptr += 2; v[0] = rotate_right(v[0],33) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 24) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 1)
|
||||||
|
{
|
||||||
|
v[1] += read_u8 (ptr) * k2; v[1] = rotate_right(v[1],33) * k3;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 24) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
v[0] += rotate_right((v[0] * k0) + v[1], 13);
|
||||||
|
v[1] += rotate_right((v[1] * k1) + v[0], 37);
|
||||||
|
v[0] += rotate_right((v[0] * k2) + v[1], 13);
|
||||||
|
v[1] += rotate_right((v[1] * k3) + v[0], 37);
|
||||||
|
|
||||||
|
memcpy(out, v, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void metrohash128_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out)
|
||||||
|
{
|
||||||
|
static const uint64_t k0 = 0xD6D018F5;
|
||||||
|
static const uint64_t k1 = 0xA2AA033B;
|
||||||
|
static const uint64_t k2 = 0x62992FC1;
|
||||||
|
static const uint64_t k3 = 0x30BC5B29;
|
||||||
|
|
||||||
|
const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key);
|
||||||
|
const uint8_t * const end = ptr + len;
|
||||||
|
|
||||||
|
uint64_t v[4];
|
||||||
|
|
||||||
|
v[0] = ((static_cast<uint64_t>(seed) - k0) * k3) + len;
|
||||||
|
v[1] = ((static_cast<uint64_t>(seed) + k1) * k2) + len;
|
||||||
|
|
||||||
|
if (len >= 32)
|
||||||
|
{
|
||||||
|
v[2] = ((static_cast<uint64_t>(seed) + k0) * k2) + len;
|
||||||
|
v[3] = ((static_cast<uint64_t>(seed) - k1) * k3) + len;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2];
|
||||||
|
v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3];
|
||||||
|
v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0];
|
||||||
|
v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1];
|
||||||
|
}
|
||||||
|
while (ptr <= (end - 32));
|
||||||
|
|
||||||
|
v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 33) * k1;
|
||||||
|
v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 33) * k0;
|
||||||
|
v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 33) * k1;
|
||||||
|
v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 33) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 16)
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],29) * k3;
|
||||||
|
v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],29) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 29) * k1;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 29) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 8)
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],29) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 29) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 4)
|
||||||
|
{
|
||||||
|
v[1] += read_u32(ptr) * k2; ptr += 4; v[1] = rotate_right(v[1],29) * k3;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 25) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 2)
|
||||||
|
{
|
||||||
|
v[0] += read_u16(ptr) * k2; ptr += 2; v[0] = rotate_right(v[0],29) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 30) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 1)
|
||||||
|
{
|
||||||
|
v[1] += read_u8 (ptr) * k2; v[1] = rotate_right(v[1],29) * k3;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 18) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
v[0] += rotate_right((v[0] * k0) + v[1], 33);
|
||||||
|
v[1] += rotate_right((v[1] * k1) + v[0], 33);
|
||||||
|
v[0] += rotate_right((v[0] * k2) + v[1], 33);
|
||||||
|
v[1] += rotate_right((v[1] * k3) + v[0], 33);
|
||||||
|
|
||||||
|
memcpy(out, v, 16);
|
||||||
|
}
|
||||||
|
|
180
contrib/libmetrohash/src/metrohash128crc.cpp
Normal file
180
contrib/libmetrohash/src/metrohash128crc.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// metrohash128crc.cpp
|
||||||
|
//
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 J. Andrew Rogers
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include "metrohash.h"
|
||||||
|
#include <nmmintrin.h>
|
||||||
|
|
||||||
|
|
||||||
|
void metrohash128crc_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out)
|
||||||
|
{
|
||||||
|
static const uint64_t k0 = 0xC83A91E1;
|
||||||
|
static const uint64_t k1 = 0x8648DBDB;
|
||||||
|
static const uint64_t k2 = 0x7BDEC03B;
|
||||||
|
static const uint64_t k3 = 0x2F5870A5;
|
||||||
|
|
||||||
|
const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key);
|
||||||
|
const uint8_t * const end = ptr + len;
|
||||||
|
|
||||||
|
uint64_t v[4];
|
||||||
|
|
||||||
|
v[0] = ((static_cast<uint64_t>(seed) - k0) * k3) + len;
|
||||||
|
v[1] = ((static_cast<uint64_t>(seed) + k1) * k2) + len;
|
||||||
|
|
||||||
|
if (len >= 32)
|
||||||
|
{
|
||||||
|
v[2] = ((static_cast<uint64_t>(seed) + k0) * k2) + len;
|
||||||
|
v[3] = ((static_cast<uint64_t>(seed) - k1) * k3) + len;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
v[0] ^= _mm_crc32_u64(v[0], read_u64(ptr)); ptr += 8;
|
||||||
|
v[1] ^= _mm_crc32_u64(v[1], read_u64(ptr)); ptr += 8;
|
||||||
|
v[2] ^= _mm_crc32_u64(v[2], read_u64(ptr)); ptr += 8;
|
||||||
|
v[3] ^= _mm_crc32_u64(v[3], read_u64(ptr)); ptr += 8;
|
||||||
|
}
|
||||||
|
while (ptr <= (end - 32));
|
||||||
|
|
||||||
|
v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 34) * k1;
|
||||||
|
v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 37) * k0;
|
||||||
|
v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 34) * k1;
|
||||||
|
v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 37) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 16)
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],34) * k3;
|
||||||
|
v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],34) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 30) * k1;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 30) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 8)
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],36) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 23) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 4)
|
||||||
|
{
|
||||||
|
v[1] ^= _mm_crc32_u64(v[0], read_u32(ptr)); ptr += 4;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 19) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 2)
|
||||||
|
{
|
||||||
|
v[0] ^= _mm_crc32_u64(v[1], read_u16(ptr)); ptr += 2;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 13) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 1)
|
||||||
|
{
|
||||||
|
v[1] ^= _mm_crc32_u64(v[0], read_u8 (ptr));
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 17) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
v[0] += rotate_right((v[0] * k0) + v[1], 11);
|
||||||
|
v[1] += rotate_right((v[1] * k1) + v[0], 26);
|
||||||
|
v[0] += rotate_right((v[0] * k0) + v[1], 11);
|
||||||
|
v[1] += rotate_right((v[1] * k1) + v[0], 26);
|
||||||
|
|
||||||
|
memcpy(out, v, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void metrohash128crc_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out)
|
||||||
|
{
|
||||||
|
static const uint64_t k0 = 0xEE783E2F;
|
||||||
|
static const uint64_t k1 = 0xAD07C493;
|
||||||
|
static const uint64_t k2 = 0x797A90BB;
|
||||||
|
static const uint64_t k3 = 0x2E4B2E1B;
|
||||||
|
|
||||||
|
const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key);
|
||||||
|
const uint8_t * const end = ptr + len;
|
||||||
|
|
||||||
|
uint64_t v[4];
|
||||||
|
|
||||||
|
v[0] = ((static_cast<uint64_t>(seed) - k0) * k3) + len;
|
||||||
|
v[1] = ((static_cast<uint64_t>(seed) + k1) * k2) + len;
|
||||||
|
|
||||||
|
if (len >= 32)
|
||||||
|
{
|
||||||
|
v[2] = ((static_cast<uint64_t>(seed) + k0) * k2) + len;
|
||||||
|
v[3] = ((static_cast<uint64_t>(seed) - k1) * k3) + len;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
v[0] ^= _mm_crc32_u64(v[0], read_u64(ptr)); ptr += 8;
|
||||||
|
v[1] ^= _mm_crc32_u64(v[1], read_u64(ptr)); ptr += 8;
|
||||||
|
v[2] ^= _mm_crc32_u64(v[2], read_u64(ptr)); ptr += 8;
|
||||||
|
v[3] ^= _mm_crc32_u64(v[3], read_u64(ptr)); ptr += 8;
|
||||||
|
}
|
||||||
|
while (ptr <= (end - 32));
|
||||||
|
|
||||||
|
v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 12) * k1;
|
||||||
|
v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 19) * k0;
|
||||||
|
v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 12) * k1;
|
||||||
|
v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 19) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 16)
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],41) * k3;
|
||||||
|
v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],41) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 10) * k1;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 10) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 8)
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],34) * k3;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 22) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 4)
|
||||||
|
{
|
||||||
|
v[1] ^= _mm_crc32_u64(v[0], read_u32(ptr)); ptr += 4;
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 14) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 2)
|
||||||
|
{
|
||||||
|
v[0] ^= _mm_crc32_u64(v[1], read_u16(ptr)); ptr += 2;
|
||||||
|
v[0] ^= rotate_right((v[0] * k2) + v[1], 15) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 1)
|
||||||
|
{
|
||||||
|
v[1] ^= _mm_crc32_u64(v[0], read_u8 (ptr));
|
||||||
|
v[1] ^= rotate_right((v[1] * k3) + v[0], 18) * k0;
|
||||||
|
}
|
||||||
|
|
||||||
|
v[0] += rotate_right((v[0] * k0) + v[1], 15);
|
||||||
|
v[1] += rotate_right((v[1] * k1) + v[0], 27);
|
||||||
|
v[0] += rotate_right((v[0] * k0) + v[1], 15);
|
||||||
|
v[1] += rotate_right((v[1] * k1) + v[0], 27);
|
||||||
|
|
||||||
|
memcpy(out, v, 16);
|
||||||
|
}
|
182
contrib/libmetrohash/src/metrohash64.cpp
Normal file
182
contrib/libmetrohash/src/metrohash64.cpp
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
// metrohash64.cpp
|
||||||
|
//
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 J. Andrew Rogers
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "metrohash.h"
|
||||||
|
|
||||||
|
void metrohash64_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out)
|
||||||
|
{
|
||||||
|
static const uint64_t k0 = 0xC83A91E1;
|
||||||
|
static const uint64_t k1 = 0x8648DBDB;
|
||||||
|
static const uint64_t k2 = 0x7BDEC03B;
|
||||||
|
static const uint64_t k3 = 0x2F5870A5;
|
||||||
|
|
||||||
|
const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key);
|
||||||
|
const uint8_t * const end = ptr + len;
|
||||||
|
|
||||||
|
uint64_t hash = ((static_cast<uint64_t>(seed) + k2) * k0) + len;
|
||||||
|
|
||||||
|
if (len >= 32)
|
||||||
|
{
|
||||||
|
uint64_t v[4];
|
||||||
|
v[0] = hash;
|
||||||
|
v[1] = hash;
|
||||||
|
v[2] = hash;
|
||||||
|
v[3] = hash;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2];
|
||||||
|
v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3];
|
||||||
|
v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0];
|
||||||
|
v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1];
|
||||||
|
}
|
||||||
|
while (ptr <= (end - 32));
|
||||||
|
|
||||||
|
v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 33) * k1;
|
||||||
|
v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 33) * k0;
|
||||||
|
v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 33) * k1;
|
||||||
|
v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 33) * k0;
|
||||||
|
hash += v[0] ^ v[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 16)
|
||||||
|
{
|
||||||
|
uint64_t v0 = hash + (read_u64(ptr) * k0); ptr += 8; v0 = rotate_right(v0,33) * k1;
|
||||||
|
uint64_t v1 = hash + (read_u64(ptr) * k1); ptr += 8; v1 = rotate_right(v1,33) * k2;
|
||||||
|
v0 ^= rotate_right(v0 * k0, 35) + v1;
|
||||||
|
v1 ^= rotate_right(v1 * k3, 35) + v0;
|
||||||
|
hash += v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 8)
|
||||||
|
{
|
||||||
|
hash += read_u64(ptr) * k3; ptr += 8;
|
||||||
|
hash ^= rotate_right(hash, 33) * k1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 4)
|
||||||
|
{
|
||||||
|
hash += read_u32(ptr) * k3; ptr += 4;
|
||||||
|
hash ^= rotate_right(hash, 15) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 2)
|
||||||
|
{
|
||||||
|
hash += read_u16(ptr) * k3; ptr += 2;
|
||||||
|
hash ^= rotate_right(hash, 13) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 1)
|
||||||
|
{
|
||||||
|
hash += read_u8 (ptr) * k3;
|
||||||
|
hash ^= rotate_right(hash, 25) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash ^= rotate_right(hash, 33);
|
||||||
|
hash *= k0;
|
||||||
|
hash ^= rotate_right(hash, 33);
|
||||||
|
|
||||||
|
memcpy(out, &hash, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void metrohash64_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out)
|
||||||
|
{
|
||||||
|
static const uint64_t k0 = 0xD6D018F5;
|
||||||
|
static const uint64_t k1 = 0xA2AA033B;
|
||||||
|
static const uint64_t k2 = 0x62992FC1;
|
||||||
|
static const uint64_t k3 = 0x30BC5B29;
|
||||||
|
|
||||||
|
const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key);
|
||||||
|
const uint8_t * const end = ptr + len;
|
||||||
|
|
||||||
|
uint64_t hash = ((static_cast<uint64_t>(seed) + k2) * k0) + len;
|
||||||
|
|
||||||
|
if (len >= 32)
|
||||||
|
{
|
||||||
|
uint64_t v[4];
|
||||||
|
v[0] = hash;
|
||||||
|
v[1] = hash;
|
||||||
|
v[2] = hash;
|
||||||
|
v[3] = hash;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2];
|
||||||
|
v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3];
|
||||||
|
v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0];
|
||||||
|
v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1];
|
||||||
|
}
|
||||||
|
while (ptr <= (end - 32));
|
||||||
|
|
||||||
|
v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 30) * k1;
|
||||||
|
v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 30) * k0;
|
||||||
|
v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 30) * k1;
|
||||||
|
v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 30) * k0;
|
||||||
|
hash += v[0] ^ v[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 16)
|
||||||
|
{
|
||||||
|
uint64_t v0 = hash + (read_u64(ptr) * k2); ptr += 8; v0 = rotate_right(v0,29) * k3;
|
||||||
|
uint64_t v1 = hash + (read_u64(ptr) * k2); ptr += 8; v1 = rotate_right(v1,29) * k3;
|
||||||
|
v0 ^= rotate_right(v0 * k0, 34) + v1;
|
||||||
|
v1 ^= rotate_right(v1 * k3, 34) + v0;
|
||||||
|
hash += v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 8)
|
||||||
|
{
|
||||||
|
hash += read_u64(ptr) * k3; ptr += 8;
|
||||||
|
hash ^= rotate_right(hash, 36) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 4)
|
||||||
|
{
|
||||||
|
hash += read_u32(ptr) * k3; ptr += 4;
|
||||||
|
hash ^= rotate_right(hash, 15) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 2)
|
||||||
|
{
|
||||||
|
hash += read_u16(ptr) * k3; ptr += 2;
|
||||||
|
hash ^= rotate_right(hash, 15) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((end - ptr) >= 1)
|
||||||
|
{
|
||||||
|
hash += read_u8 (ptr) * k3;
|
||||||
|
hash ^= rotate_right(hash, 23) * k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash ^= rotate_right(hash, 28);
|
||||||
|
hash *= k0;
|
||||||
|
hash ^= rotate_right(hash, 29);
|
||||||
|
|
||||||
|
memcpy(out, &hash, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
70
contrib/libmetrohash/src/testvector.h
Normal file
70
contrib/libmetrohash/src/testvector.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// testvector.h
|
||||||
|
//
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 J. Andrew Rogers
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef METROHASH_TESTVECTOR_H
|
||||||
|
#define METROHASH_TESTVECTOR_H
|
||||||
|
|
||||||
|
#include "metrohash.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef void (*HashFunction) (const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * hash);
|
||||||
|
|
||||||
|
struct TestVectorData
|
||||||
|
{
|
||||||
|
HashFunction function;
|
||||||
|
uint32_t bits;
|
||||||
|
const char * key;
|
||||||
|
uint32_t seed;
|
||||||
|
uint8_t hash[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
// The test vector string is selected such that it will properly exercise every
|
||||||
|
// internal branch of the hash function. Currently that requires a string with
|
||||||
|
// a length of (at least) 63 bytes.
|
||||||
|
|
||||||
|
static const char * test_key_63 = "012345678901234567890123456789012345678901234567890123456789012";
|
||||||
|
|
||||||
|
const TestVectorData TestVector [] =
|
||||||
|
{
|
||||||
|
// seed = 0
|
||||||
|
{ metrohash64_1, 64, test_key_63, 0, "658F044F5C730E40" },
|
||||||
|
{ metrohash64_2, 64, test_key_63, 0, "073CAAB960623211" },
|
||||||
|
{ metrohash128_1, 128, test_key_63, 0, "ED9997ED9D0A8B0FF3F266399477788F" },
|
||||||
|
{ metrohash128_2, 128, test_key_63, 0, "7BBA6FE119CF35D45507EDF3505359AB" },
|
||||||
|
{ metrohash128crc_1, 128, test_key_63, 0, "B329ED67831604D3DFAC4E4876D8262F" },
|
||||||
|
{ metrohash128crc_2, 128, test_key_63, 0, "0502A67E257BBD77206BBCA6BBEF2653" },
|
||||||
|
|
||||||
|
// seed = 1
|
||||||
|
{ metrohash64_1, 64, test_key_63, 1, "AE49EBB0A856537B" },
|
||||||
|
{ metrohash64_2, 64, test_key_63, 1, "CF518E9CF58402C0" },
|
||||||
|
{ metrohash128_1, 128, test_key_63, 1, "DDA6BA67F7DE755EFDF6BEABECCFD1F4" },
|
||||||
|
{ metrohash128_2, 128, test_key_63, 1, "2DA6AF149A5CDBC12B09DB0846D69EF0" },
|
||||||
|
{ metrohash128crc_1, 128, test_key_63, 1, "E8FAB51AF19F18A7B10D0A57D4276DF2" },
|
||||||
|
{ metrohash128crc_2, 128, test_key_63, 1, "2D54F87181A0CF64B02C50D95692BC19" },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // #ifndef METROHASH_TESTVECTOR_H
|
2360
contrib/libpoco/CHANGELOG
Normal file
2360
contrib/libpoco/CHANGELOG
Normal file
File diff suppressed because it is too large
Load Diff
330
contrib/libpoco/CMakeLists.txt
Normal file
330
contrib/libpoco/CMakeLists.txt
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8.0)
|
||||||
|
|
||||||
|
# POCO_BUILD_TYPE
|
||||||
|
# POCO_STATIC
|
||||||
|
# POCO_UNBUNDLED
|
||||||
|
# POCO_NO_LOCALE
|
||||||
|
#
|
||||||
|
# ENABLE_{COMPONENT}
|
||||||
|
# ENABLE_TESTS
|
||||||
|
|
||||||
|
project(Poco)
|
||||||
|
|
||||||
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/libversion" SHARED_LIBRARY_VERSION)
|
||||||
|
|
||||||
|
# Read the version information from the VERSION file
|
||||||
|
file (STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" PACKAGE_VERSION )
|
||||||
|
message(STATUS "Poco package version: ${PACKAGE_VERSION}")
|
||||||
|
string(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" CPACK_PACKAGE_VERSION_MAJOR ${PACKAGE_VERSION})
|
||||||
|
string(REGEX REPLACE "[0-9]+\\.([0-9])+\\.[0-9]+.*" "\\1" CPACK_PACKAGE_VERSION_MINOR ${PACKAGE_VERSION})
|
||||||
|
string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" CPACK_PACKAGE_VERSION_PATCH ${PACKAGE_VERSION})
|
||||||
|
|
||||||
|
set(COMPLETE_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH})
|
||||||
|
set(RELEASE_NAME "Unstable-trunk")
|
||||||
|
set(PROJECT_VERSION ${COMPLETE_VERSION})
|
||||||
|
|
||||||
|
# Put the libaries and binaries that get built into directories at the
|
||||||
|
# top of the build tree rather than in hard-to-find leaf
|
||||||
|
# directories. This simplifies manual testing and the use of the build
|
||||||
|
# tree rather than installed Boost libraries.
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
|
||||||
|
# Windows DLLs are "runtime" for CMake. Output them to "bin" like the Visual Studio projects do.
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
# Append our module directory to CMake
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||||
|
|
||||||
|
#################################################################################
|
||||||
|
# Setup C/C++ compiler options
|
||||||
|
#################################################################################
|
||||||
|
|
||||||
|
if(NOT MSVC_IDE)
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
|
||||||
|
"Choose the type of build, options are: None Debug Release" FORCE)
|
||||||
|
endif()
|
||||||
|
message(STATUS "Setting Poco build type - ${CMAKE_BUILD_TYPE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "")
|
||||||
|
set( CMAKE_BUILD_TYPE "RelWithDebInfo" )
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# http://www.cmake.org/Wiki/CMake_Useful_Variables :
|
||||||
|
# CMAKE_BUILD_TYPE
|
||||||
|
# Choose the type of build. CMake has default flags for these:
|
||||||
|
#
|
||||||
|
# * None (CMAKE_C_FLAGS or CMAKE_CXX_FLAGS used)
|
||||||
|
# * Debug (CMAKE_C_FLAGS_DEBUG or CMAKE_CXX_FLAGS_DEBUG)
|
||||||
|
# * Release (CMAKE_C_FLAGS_RELEASE or CMAKE_CXX_FLAGS_RELEASE)
|
||||||
|
# * RelWithDebInfo (CMAKE_C_FLAGS_RELWITHDEBINFO or CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||||
|
# * MinSizeRel (CMAKE_C_FLAGS_MINSIZEREL or CMAKE_CXX_FLAGS_MINSIZEREL)
|
||||||
|
|
||||||
|
# For Debug build types, append a "d" to the library names.
|
||||||
|
set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Set debug library postfix" FORCE)
|
||||||
|
|
||||||
|
# Include some common macros to simpilfy the Poco CMake files
|
||||||
|
include(PocoMacros)
|
||||||
|
|
||||||
|
# Allow enabling and disabling components
|
||||||
|
option(ENABLE_XML "Enable the XML" ON)
|
||||||
|
option(ENABLE_JSON "Enable the JSON" ON)
|
||||||
|
option(ENABLE_MONGODB "Enable MongoDB" ON)
|
||||||
|
option(ENABLE_PDF "Enable PDF" OFF)
|
||||||
|
option(ENABLE_UTIL "Enable Util" ON)
|
||||||
|
option(ENABLE_NET "Enable Net" ON)
|
||||||
|
option(ENABLE_NETSSL "Enable NetSSL" ON)
|
||||||
|
option(ENABLE_NETSSL_WIN "Enable NetSSL Windows" OFF)
|
||||||
|
option(ENABLE_CRYPTO "Enable Crypto" ON)
|
||||||
|
option(ENABLE_DATA "Enable Data" ON)
|
||||||
|
option(ENABLE_DATA_SQLITE "Enable Data SQlite" ON)
|
||||||
|
option(ENABLE_DATA_MYSQL "Enable Data MySQL" ON)
|
||||||
|
option(ENABLE_DATA_ODBC "Enable Data ODBC" ON)
|
||||||
|
option(ENABLE_SEVENZIP "Enable SevenZip" OFF)
|
||||||
|
option(ENABLE_ZIP "Enable Zip" ON)
|
||||||
|
option(ENABLE_APACHECONNECTOR "Enable ApacheConnector" OFF)
|
||||||
|
option(ENABLE_CPPPARSER "Enable C++ parser" OFF)
|
||||||
|
option(ENABLE_POCODOC "Enable Poco Documentation Generator" OFF)
|
||||||
|
option(ENABLE_PAGECOMPILER "Enable PageCompiler" ON)
|
||||||
|
option(ENABLE_PAGECOMPILER_FILE2PAGE "Enable File2Page" ON)
|
||||||
|
|
||||||
|
option(FORCE_OPENSSL "Force usage of OpenSSL even under windows" OFF)
|
||||||
|
|
||||||
|
option(ENABLE_TESTS
|
||||||
|
"Set to OFF|ON (default is OFF) to control build of POCO tests & samples" OFF)
|
||||||
|
|
||||||
|
option(POCO_STATIC
|
||||||
|
"Set to OFF|ON (default is ON) to control build of POCO as STATIC library" ON)
|
||||||
|
|
||||||
|
option(POCO_UNBUNDLED
|
||||||
|
"Set to OFF|ON (default is OFF) to control linking dependencies as external" OFF)
|
||||||
|
|
||||||
|
# Uncomment from next two lines to force statitc or dynamic library, default is autodetection
|
||||||
|
if(POCO_STATIC)
|
||||||
|
add_definitions( -DPOCO_STATIC -DPOCO_NO_AUTOMATIC_LIBS)
|
||||||
|
set( LIB_MODE STATIC )
|
||||||
|
message(STATUS "Building static libraries")
|
||||||
|
else(POCO_STATIC)
|
||||||
|
set( LIB_MODE SHARED )
|
||||||
|
message(STATUS "Building dynamic libraries")
|
||||||
|
endif(POCO_STATIC)
|
||||||
|
|
||||||
|
if (ENABLE_TESTS)
|
||||||
|
include(CTest)
|
||||||
|
enable_testing()
|
||||||
|
message(STATUS "Building with unittests & samples")
|
||||||
|
else ()
|
||||||
|
message(STATUS "Building without tests & samples")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (POCO_UNBUNDLED)
|
||||||
|
add_definitions( -DPOCO_UNBUNDLED)
|
||||||
|
message(STATUS "Build with using external sqlite, libz, pcre, expat ...")
|
||||||
|
else ()
|
||||||
|
message(STATUS "Build with using internal copy of sqlite, libz, pcre, expat, ...")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
include(CheckTypeSize)
|
||||||
|
find_package(Cygwin)
|
||||||
|
|
||||||
|
# OS Detection
|
||||||
|
if(WIN32)
|
||||||
|
add_definitions( -DPOCO_OS_FAMILY_WINDOWS -DUNICODE -D_UNICODE)
|
||||||
|
#set(SYSLIBS iphlpapi gdi32 odbc32)
|
||||||
|
endif(WIN32)
|
||||||
|
|
||||||
|
if (UNIX AND NOT ANDROID )
|
||||||
|
add_definitions( -DPOCO_OS_FAMILY_UNIX )
|
||||||
|
add_definitions( -Wno-unused-private-field -Wno-unused-local-typedef -Wno-for-loop-analysis -Wno-unknown-pragmas )
|
||||||
|
# Standard 'must be' defines
|
||||||
|
if (APPLE)
|
||||||
|
add_definitions( -DPOCO_HAVE_IPv6 -DPOCO_NO_STAT64)
|
||||||
|
set(SYSLIBS dl)
|
||||||
|
else (APPLE)
|
||||||
|
add_definitions(-D_XOPEN_SOURCE=500 -D_REENTRANT -D_THREAD_SAFE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -DPOCO_HAVE_FD_EPOLL -DPOCO_HAVE_IPv6)
|
||||||
|
set(SYSLIBS pthread dl rt)
|
||||||
|
endif (APPLE)
|
||||||
|
endif(UNIX AND NOT ANDROID )
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM MATCHES "SunOS")
|
||||||
|
add_definitions( -DPOCO_OS_FAMILY_UNIX )
|
||||||
|
# Standard 'must be' defines
|
||||||
|
add_definitions( -D_XOPEN_SOURCE=500 -D_REENTRANT -D_THREAD_SAFE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 )
|
||||||
|
set(SYSLIBS pthread socket xnet nsl resolv rt dl)
|
||||||
|
endif(CMAKE_SYSTEM MATCHES "SunOS")
|
||||||
|
|
||||||
|
if (CMAKE_COMPILER_IS_MINGW)
|
||||||
|
add_definitions(-DWC_NO_BEST_FIT_CHARS=0x400 -DPOCO_WIN32_UTF8)
|
||||||
|
add_definitions(-D_WIN32 -DMINGW32 -DWINVER=0x500 -DODBCVER=0x0300 -DPOCO_THREAD_STACK_SIZE)
|
||||||
|
endif (CMAKE_COMPILER_IS_MINGW)
|
||||||
|
|
||||||
|
if (CYGWIN)
|
||||||
|
# add_definitions(-DWC_NO_BEST_FIT_CHARS=0x400 -DPOCO_WIN32_UTF8)
|
||||||
|
endif (CYGWIN)
|
||||||
|
|
||||||
|
# SunPro C++
|
||||||
|
if (${CMAKE_CXX_COMPILER_ID} MATCHES "SunPro")
|
||||||
|
add_definitions( -D_BSD_SOURCE -library=stlport4)
|
||||||
|
endif (${CMAKE_CXX_COMPILER_ID} MATCHES "SunPro")
|
||||||
|
|
||||||
|
# iOS
|
||||||
|
if (IOS)
|
||||||
|
add_definitions( -DPOCO_HAVE_IPv6 -DPOCO_NO_FPENVIRONMENT -DPOCO_NO_STAT64 -DPOCO_NO_SHAREDLIBS -DPOCO_NO_NET_IFTYPES )
|
||||||
|
endif(IOS)
|
||||||
|
|
||||||
|
#Android
|
||||||
|
if (ANDROID)
|
||||||
|
add_definitions( -DPOCO_ANDROID -DPOCO_NO_FPENVIRONMENT -DPOCO_NO_WSTRING -DPOCO_NO_SHAREDMEMORY )
|
||||||
|
endif(ANDROID)
|
||||||
|
|
||||||
|
|
||||||
|
# Collect the built libraries and include dirs, the will be used to create the PocoConfig.cmake file
|
||||||
|
set(Poco_COMPONENTS "")
|
||||||
|
|
||||||
|
if (ENABLE_TESTS)
|
||||||
|
add_subdirectory(CppUnit)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_subdirectory(Foundation)
|
||||||
|
if(ENABLE_XML)
|
||||||
|
add_subdirectory(XML)
|
||||||
|
list(APPEND Poco_COMPONENTS "XML")
|
||||||
|
endif()
|
||||||
|
if(ENABLE_JSON)
|
||||||
|
add_subdirectory(JSON)
|
||||||
|
list(APPEND Poco_COMPONENTS "JSON")
|
||||||
|
endif()
|
||||||
|
if(ENABLE_MONGODB)
|
||||||
|
add_subdirectory(MongoDB)
|
||||||
|
list(APPEND Poco_COMPONENTS "MongoDB")
|
||||||
|
endif()
|
||||||
|
if(ENABLE_PDF)
|
||||||
|
add_subdirectory(PDF)
|
||||||
|
list(APPEND Poco_COMPONENTS "PDF")
|
||||||
|
endif()
|
||||||
|
if(ENABLE_UTIL)
|
||||||
|
add_subdirectory(Util)
|
||||||
|
list(APPEND Poco_COMPONENTS "Util")
|
||||||
|
endif()
|
||||||
|
if(ENABLE_NET)
|
||||||
|
add_subdirectory(Net)
|
||||||
|
list(APPEND Poco_COMPONENTS "Net")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#NetSSL
|
||||||
|
|
||||||
|
|
||||||
|
if(WIN32 AND ENABLE_NETSSL_WIN)
|
||||||
|
add_subdirectory(NetSSL_Win)
|
||||||
|
list(APPEND Poco_COMPONENTS "NetSSL_Win")
|
||||||
|
endif(WIN32 AND ENABLE_NETSSL_WIN)
|
||||||
|
|
||||||
|
find_package(OpenSSL)
|
||||||
|
if(OPENSSL_FOUND)
|
||||||
|
include_directories("${OPENSSL_INCLUDE_DIR}")
|
||||||
|
if(ENABLE_NETSSL)
|
||||||
|
add_subdirectory(NetSSL_OpenSSL)
|
||||||
|
list(APPEND Poco_COMPONENTS "NetSSL_OpenSSL")
|
||||||
|
endif()
|
||||||
|
if(ENABLE_CRYPTO)
|
||||||
|
add_subdirectory(Crypto)
|
||||||
|
list(APPEND Poco_COMPONENTS "Crypto")
|
||||||
|
endif()
|
||||||
|
endif(OPENSSL_FOUND)
|
||||||
|
|
||||||
|
if(ENABLE_DATA)
|
||||||
|
add_subdirectory(Data)
|
||||||
|
list(APPEND Poco_COMPONENTS "Data")
|
||||||
|
endif()
|
||||||
|
if(ENABLE_SEVENZIP)
|
||||||
|
add_subdirectory(SevenZip)
|
||||||
|
list(APPEND Poco_COMPONENTS "SevenZip")
|
||||||
|
endif()
|
||||||
|
if(ENABLE_ZIP)
|
||||||
|
add_subdirectory(Zip)
|
||||||
|
list(APPEND Poco_COMPONENTS "Zip")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(APR)
|
||||||
|
find_package(Apache2)
|
||||||
|
if(APRUTIL_FOUND AND APACHE_FOUND)
|
||||||
|
include_directories( "${APACHE_INCLUDE_DIR}" "${APRUTIL_INCLUDE_DIR}" )
|
||||||
|
if(ENABLE_APACHECONNECTOR)
|
||||||
|
add_subdirectory(ApacheConnector)
|
||||||
|
list(APPEND Poco_COMPONENTS "ApacheConnector")
|
||||||
|
endif()
|
||||||
|
endif(APRUTIL_FOUND AND APACHE_FOUND)
|
||||||
|
|
||||||
|
if(ENABLE_CPPPARSER)
|
||||||
|
add_subdirectory(CppParser)
|
||||||
|
list(APPEND Poco_COMPONENTS "CppParser")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_POCODOC)
|
||||||
|
add_subdirectory(PocoDoc)
|
||||||
|
list(APPEND Poco_COMPONENTS "PocoDoc")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_PAGECOMPILER)
|
||||||
|
add_subdirectory(PageCompiler)
|
||||||
|
list(APPEND Poco_COMPONENTS "PageCompiler")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_PAGECOMPILER_FILE2PAGE)
|
||||||
|
add_subdirectory(PageCompiler/File2Page)
|
||||||
|
list(APPEND Poco_COMPONENTS "File2Page")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Uninstall stuff see: http://www.vtk.org/Wiki/CMake_FAQ
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||||
|
IMMEDIATE @ONLY)
|
||||||
|
|
||||||
|
add_custom_target(uninstall
|
||||||
|
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# Enable packaging
|
||||||
|
|
||||||
|
include(InstallRequiredSystemLibraries)
|
||||||
|
|
||||||
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Poco Libraries")
|
||||||
|
set(CPACK_PACKAGE_VENDOR "Applied Informatics Software Engineering GmbH")
|
||||||
|
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README")
|
||||||
|
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||||
|
set(CPACK_PACKAGE_INSTALL_DIRECTORY "/usr/local")
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
# cmake config files
|
||||||
|
|
||||||
|
configure_file(cmake/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake" @ONLY)
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}ConfigVersion.cmake
|
||||||
|
DESTINATION
|
||||||
|
"lib/cmake/${PROJECT_NAME}"
|
||||||
|
COMPONENT
|
||||||
|
Devel
|
||||||
|
)
|
||||||
|
|
||||||
|
# in tree build settings
|
||||||
|
#configure_file(PocoBuildTreeSettings.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/PocoBuildTreeSettings.cmake @ONLY)
|
||||||
|
|
||||||
|
|
||||||
|
message(STATUS "CMake ${CMAKE_VERSION} successfully configured ${PROJECT_NAME} using ${CMAKE_GENERATOR} generator")
|
||||||
|
message(STATUS "Installation target path: ${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
|
message(STATUS "C_FLAGS: =${CMAKE_C_FLAGS}")
|
||||||
|
message(STATUS "CXX_FLAGS:=${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
|
foreach(component ${Poco_COMPONENTS})
|
||||||
|
message(STATUS "Building: ${component}")
|
||||||
|
endforeach()
|
||||||
|
|
53
contrib/libpoco/CONTRIBUTORS
Normal file
53
contrib/libpoco/CONTRIBUTORS
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
Guenter Obiltschnig
|
||||||
|
Alex Fabijanic
|
||||||
|
Peter Schojer
|
||||||
|
Ferdinand Beyer
|
||||||
|
Krzysztof Burghardt
|
||||||
|
Claus Dabringer
|
||||||
|
Caleb Epstein
|
||||||
|
Eran Hammer-Lahav
|
||||||
|
Chris Johnson
|
||||||
|
Sergey Kholodilov
|
||||||
|
Ryan Kraay
|
||||||
|
Larry Lewis
|
||||||
|
Andrew J. P. Maclean
|
||||||
|
Andrew Marlow
|
||||||
|
Paschal Mushubi
|
||||||
|
Jiang Shan
|
||||||
|
David Shawley
|
||||||
|
Sergey Skorokhodov
|
||||||
|
Tom Tan
|
||||||
|
Sergey N. Yatskevich
|
||||||
|
Marc Chevrier
|
||||||
|
Philippe Cuvillier
|
||||||
|
Marian Krivos
|
||||||
|
Franky Braem
|
||||||
|
Philip Prindeville
|
||||||
|
Anton Yabchinskiy
|
||||||
|
Rangel Reale
|
||||||
|
Fabrizio Duhem
|
||||||
|
Patrick White
|
||||||
|
Mike Naquin
|
||||||
|
Roger Meier
|
||||||
|
Mathaus Mendel
|
||||||
|
Arturo Castro
|
||||||
|
Adrian Imboden
|
||||||
|
Matej Knopp
|
||||||
|
Patrice Tarabbia
|
||||||
|
Lucas Clemente
|
||||||
|
Karl Reid
|
||||||
|
Pascal Bach
|
||||||
|
Cristian Thiago Moecke
|
||||||
|
Sergei Nikulov
|
||||||
|
Aaron Kaluszka
|
||||||
|
Iyed Bennour
|
||||||
|
Scott Davis
|
||||||
|
Kristin Cowalcijk
|
||||||
|
Yuval Kashtan
|
||||||
|
Christopher Baker
|
||||||
|
Scott Davis
|
||||||
|
Jeff Adams
|
||||||
|
Martin Osborne
|
||||||
|
Björn Schramke
|
||||||
|
--
|
||||||
|
$Id$
|
@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// WinTestRunner.h
|
||||||
|
//
|
||||||
|
// $Id: //poco/1.4/CppUnit/WinTestRunner/include/WinTestRunner/WinTestRunner.h#1 $
|
||||||
|
//
|
||||||
|
// Application shell for CppUnit's TestRunner dialog.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef WinTestRunner_H_INCLUDED
|
||||||
|
#define WinTestRunner_H_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(POCO_STATIC)
|
||||||
|
#if defined(WinTestRunner_EXPORTS)
|
||||||
|
#define WinTestRunner_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define WinTestRunner_API __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define WinTestRunner_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include "CppUnit/CppUnit.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <afxwin.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace CppUnit {
|
||||||
|
|
||||||
|
|
||||||
|
class Test;
|
||||||
|
|
||||||
|
|
||||||
|
class WinTestRunner_API WinTestRunner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WinTestRunner();
|
||||||
|
~WinTestRunner();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
void addTest(Test* pTest);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Test*> _tests;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class WinTestRunner_API WinTestRunnerApp: public CWinApp
|
||||||
|
/// A simple application class that hosts the TestRunner dialog.
|
||||||
|
/// Create a subclass and override the TestMain() method.
|
||||||
|
///
|
||||||
|
/// WinTestRunnerApp supports a batch mode, which runs the
|
||||||
|
/// test using the standard text-based TestRunner from CppUnit.
|
||||||
|
/// To enable batch mode, start the application with the "/b"
|
||||||
|
/// or "/B" argument. Optionally, a filename may be specified
|
||||||
|
/// where the test output will be written to: "/b:<path>" or
|
||||||
|
/// "/B:<path>".
|
||||||
|
///
|
||||||
|
/// When run in batch mode, the exit code of the application
|
||||||
|
/// will denote test success (0) or failure (1).
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual BOOL InitInstance();
|
||||||
|
|
||||||
|
virtual void TestMain() = 0;
|
||||||
|
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace CppUnit
|
||||||
|
|
||||||
|
|
||||||
|
#endif // WinTestRunner_H_INCLUDED
|
||||||
|
|
27
contrib/libpoco/CppUnit/WinTestRunner/res/Resource.h
Normal file
27
contrib/libpoco/CppUnit/WinTestRunner/res/Resource.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Visual C++ generated include file.
|
||||||
|
// Used by TestRunner.rc
|
||||||
|
//
|
||||||
|
#define IDD_DIALOG_TESTRUNNER 129
|
||||||
|
#define IDC_LIST 1000
|
||||||
|
#define ID_RUN 1001
|
||||||
|
#define ID_STOP 1002
|
||||||
|
#define IDC_PROGRESS 1003
|
||||||
|
#define IDC_INDICATOR 1004
|
||||||
|
#define IDC_COMBO_TEST 1005
|
||||||
|
#define IDC_STATIC_RUNS 1007
|
||||||
|
#define IDC_STATIC_ERRORS 1008
|
||||||
|
#define IDC_STATIC_FAILURES 1009
|
||||||
|
#define IDC_EDIT_TIME 1010
|
||||||
|
#define IDC_CHK_AUTORUN 1013
|
||||||
|
|
||||||
|
// Next default values for new objects
|
||||||
|
//
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
#define _APS_NEXT_RESOURCE_VALUE 131
|
||||||
|
#define _APS_NEXT_COMMAND_VALUE 32771
|
||||||
|
#define _APS_NEXT_CONTROL_VALUE 1014
|
||||||
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
#endif
|
||||||
|
#endif
|
175
contrib/libpoco/CppUnit/WinTestRunner/res/WinTestRunner.rc
Normal file
175
contrib/libpoco/CppUnit/WinTestRunner/res/WinTestRunner.rc
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// Microsoft Visual C++ generated resource script.
|
||||||
|
//
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
#define APSTUDIO_READONLY_SYMBOLS
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 2 resource.
|
||||||
|
//
|
||||||
|
#include "afxres.h"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// English (U.S.) resources
|
||||||
|
|
||||||
|
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||||
|
#ifdef _WIN32
|
||||||
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
#pragma code_page(1252)
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TEXTINCLUDE
|
||||||
|
//
|
||||||
|
|
||||||
|
1 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"resource.h\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
2 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"#include ""afxres.h""\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
3 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
|
||||||
|
"#define _AFX_NO_OLE_RESOURCES\r\n"
|
||||||
|
"#define _AFX_NO_TRACKER_RESOURCES\r\n"
|
||||||
|
"#define _AFX_NO_PROPERTY_RESOURCES\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
|
||||||
|
"#ifdef _WIN32\r\n"
|
||||||
|
"LANGUAGE 9, 1\r\n"
|
||||||
|
"#pragma code_page(1252)\r\n"
|
||||||
|
"#endif\r\n"
|
||||||
|
"#include ""..\\res\\WinTestRunner.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
|
||||||
|
"#include ""afxres.rc"" // Standard components\r\n"
|
||||||
|
"#endif\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Version
|
||||||
|
//
|
||||||
|
|
||||||
|
VS_VERSION_INFO VERSIONINFO
|
||||||
|
FILEVERSION 1,0,0,1
|
||||||
|
PRODUCTVERSION 1,0,0,1
|
||||||
|
FILEFLAGSMASK 0x3fL
|
||||||
|
#ifdef _DEBUG
|
||||||
|
FILEFLAGS 0x1L
|
||||||
|
#else
|
||||||
|
FILEFLAGS 0x0L
|
||||||
|
#endif
|
||||||
|
FILEOS 0x4L
|
||||||
|
FILETYPE 0x2L
|
||||||
|
FILESUBTYPE 0x0L
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904b0"
|
||||||
|
BEGIN
|
||||||
|
VALUE "FileDescription", "CppUnit WinTestRunner DLL"
|
||||||
|
VALUE "FileVersion", "1, 0, 0, 1"
|
||||||
|
VALUE "InternalName", "WinTestRunner"
|
||||||
|
VALUE "LegalCopyright", "Copyright (c) 2005"
|
||||||
|
VALUE "OriginalFilename", "TestRunner.dll"
|
||||||
|
VALUE "ProductName", "CppUnit WinTestRunner Dynamic Link Library"
|
||||||
|
VALUE "ProductVersion", "1, 0, 0, 1"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x409, 1200
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Dialog
|
||||||
|
//
|
||||||
|
|
||||||
|
IDD_DIALOG_TESTRUNNER DIALOGEX 0, 0, 512, 300
|
||||||
|
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
|
CAPTION "CppUnit WinTestRunner"
|
||||||
|
FONT 8, "MS Sans Serif", 0, 0, 0x0
|
||||||
|
BEGIN
|
||||||
|
COMBOBOX IDC_COMBO_TEST,7,20,424,273,CBS_DROPDOWNLIST |
|
||||||
|
WS_VSCROLL | WS_TABSTOP
|
||||||
|
DEFPUSHBUTTON "Run",ID_RUN,455,7,50,14
|
||||||
|
DEFPUSHBUTTON "Stop",ID_STOP,455,24,50,14
|
||||||
|
CONTROL "List1",IDC_LIST,"SysListView32",LVS_REPORT | WS_BORDER |
|
||||||
|
WS_TABSTOP,7,110,498,160
|
||||||
|
PUSHBUTTON "Close",IDOK,455,279,50,14
|
||||||
|
LTEXT "Test Name:",IDC_STATIC,7,9,179,9
|
||||||
|
LTEXT "Progress:",IDC_STATIC,7,55,49,9
|
||||||
|
LTEXT "Errors and Failures:",IDC_STATIC,7,99,67,9
|
||||||
|
LTEXT "Runs:",IDC_STATIC,457,54,26,10
|
||||||
|
LTEXT "Failures:",IDC_STATIC,457,80,26,10
|
||||||
|
LTEXT "Errors:",IDC_STATIC,457,67,26,10
|
||||||
|
RTEXT "0",IDC_STATIC_RUNS,487,54,16,10
|
||||||
|
RTEXT "0",IDC_STATIC_ERRORS,487,67,16,10
|
||||||
|
RTEXT "0",IDC_STATIC_FAILURES,487,80,16,10
|
||||||
|
EDITTEXT IDC_EDIT_TIME,7,281,440,12,ES_AUTOHSCROLL | ES_READONLY |
|
||||||
|
NOT WS_BORDER
|
||||||
|
LTEXT "",IDC_PROGRESS,7,67,424,20,SS_SUNKEN | NOT WS_VISIBLE
|
||||||
|
CONTROL "Auto Run",IDC_CHK_AUTORUN,"Button",BS_AUTOCHECKBOX |
|
||||||
|
BS_LEFTTEXT | WS_TABSTOP,383,38,46,10
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// DESIGNINFO
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
GUIDELINES DESIGNINFO
|
||||||
|
BEGIN
|
||||||
|
IDD_DIALOG_TESTRUNNER, DIALOG
|
||||||
|
BEGIN
|
||||||
|
LEFTMARGIN, 7
|
||||||
|
RIGHTMARGIN, 505
|
||||||
|
TOPMARGIN, 7
|
||||||
|
BOTTOMMARGIN, 293
|
||||||
|
END
|
||||||
|
END
|
||||||
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
#endif // English (U.S.) resources
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 3 resource.
|
||||||
|
//
|
||||||
|
#define _AFX_NO_SPLITTER_RESOURCES
|
||||||
|
#define _AFX_NO_OLE_RESOURCES
|
||||||
|
#define _AFX_NO_TRACKER_RESOURCES
|
||||||
|
#define _AFX_NO_PROPERTY_RESOURCES
|
||||||
|
|
||||||
|
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||||
|
#ifdef _WIN32
|
||||||
|
LANGUAGE 9, 1
|
||||||
|
#pragma code_page(1252)
|
||||||
|
#endif
|
||||||
|
#include "afxres.rc" // Standard components
|
||||||
|
#endif
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#endif // not APSTUDIO_INVOKED
|
||||||
|
|
44
contrib/libpoco/CppUnit/WinTestRunner/src/ActiveTest.cpp
Normal file
44
contrib/libpoco/CppUnit/WinTestRunner/src/ActiveTest.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// ActiveTest.cpp
|
||||||
|
//
|
||||||
|
// $Id: //poco/1.4/CppUnit/WinTestRunner/src/ActiveTest.cpp#1 $
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include <afxwin.h>
|
||||||
|
#include "ActiveTest.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace CppUnit {
|
||||||
|
|
||||||
|
|
||||||
|
// Spawn a thread to a test
|
||||||
|
void ActiveTest::run(TestResult* result)
|
||||||
|
{
|
||||||
|
CWinThread* thread;
|
||||||
|
|
||||||
|
setTestResult(result);
|
||||||
|
_runCompleted.ResetEvent();
|
||||||
|
|
||||||
|
thread = AfxBeginThread(threadFunction, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
|
||||||
|
DuplicateHandle(GetCurrentProcess(), thread->m_hThread, GetCurrentProcess(), &_threadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||||
|
|
||||||
|
thread->ResumeThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Simple execution thread. Assuming that an ActiveTest instance
|
||||||
|
// only creates one of these at a time.
|
||||||
|
UINT ActiveTest::threadFunction(LPVOID thisInstance)
|
||||||
|
{
|
||||||
|
ActiveTest* test = (ActiveTest*) thisInstance;
|
||||||
|
|
||||||
|
test->run();
|
||||||
|
test->_runCompleted.SetEvent();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace CppUnit
|
||||||
|
|
89
contrib/libpoco/CppUnit/WinTestRunner/src/ActiveTest.h
Normal file
89
contrib/libpoco/CppUnit/WinTestRunner/src/ActiveTest.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// ActiveTest.h
|
||||||
|
//
|
||||||
|
// $Id: //poco/1.4/CppUnit/WinTestRunner/src/ActiveTest.h#1 $
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ActiveTest_INCLUDED
|
||||||
|
#define ActiveTest_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
#include "CppUnit/CppUnit.h"
|
||||||
|
#include "CppUnit/TestDecorator.h"
|
||||||
|
#include <afxmt.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace CppUnit {
|
||||||
|
|
||||||
|
|
||||||
|
/* A Microsoft-specific active test
|
||||||
|
*
|
||||||
|
* An active test manages its own
|
||||||
|
* thread of execution. This one
|
||||||
|
* is very simple and only sufficient
|
||||||
|
* for the limited use we put it through
|
||||||
|
* in the TestRunner. It spawns a thread
|
||||||
|
* on run (TestResult *) and signals
|
||||||
|
* completion of the test.
|
||||||
|
*
|
||||||
|
* We assume that only one thread
|
||||||
|
* will be active at once for each
|
||||||
|
* instance.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ActiveTest: public TestDecorator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ActiveTest(Test* test);
|
||||||
|
~ActiveTest();
|
||||||
|
|
||||||
|
void run(TestResult* result);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HANDLE _threadHandle;
|
||||||
|
CEvent _runCompleted;
|
||||||
|
TestResult* _currentTestResult;
|
||||||
|
|
||||||
|
void run ();
|
||||||
|
void setTestResult(TestResult* result);
|
||||||
|
static UINT threadFunction(LPVOID thisInstance);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Construct the active test
|
||||||
|
inline ActiveTest::ActiveTest(Test *test): TestDecorator(test)
|
||||||
|
{
|
||||||
|
_currentTestResult = NULL;
|
||||||
|
_threadHandle = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Pend until the test has completed
|
||||||
|
inline ActiveTest::~ActiveTest()
|
||||||
|
{
|
||||||
|
CSingleLock(&_runCompleted, TRUE);
|
||||||
|
CloseHandle(_threadHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Set the test result that we are to run
|
||||||
|
inline void ActiveTest::setTestResult(TestResult* result)
|
||||||
|
{
|
||||||
|
_currentTestResult = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Run our test result
|
||||||
|
inline void ActiveTest::run()
|
||||||
|
{
|
||||||
|
TestDecorator::run(_currentTestResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace CppUnit
|
||||||
|
|
||||||
|
|
||||||
|
#endif // ActiveTest_INCLUDED
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user