Skip to content

Commit

Permalink
Use the fast_float library to parse floating-point numbers
Browse files Browse the repository at this point in the history
This commit adds Daniel Lemire's fast_float library as an external dependency,
and applies it to our uses of sscanf with %f as well as istringstream.

This library is locale-free, and does not incur the performance penalty of
using a thread locale RAII class or imbuing a locale into a
stringstream.

See:
    Daniel Lemire, Number Parsing at a Gigabyte per Second, Software:
    Pratice and Experience 51 (8), 2021.
    <https://arxiv.org/abs/2101.11408>

Fixes AcademySoftwareFoundation#297
Fixes AcademySoftwareFoundation#379
Fixes AcademySoftwareFoundation#1322

Signed-off-by: L. E. Segovia <13498015+amyspark@users.noreply.github.com>
  • Loading branch information
amyspark committed Oct 9, 2021
1 parent ac8290d commit 4937d08
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 8 deletions.
4 changes: 4 additions & 0 deletions share/cmake/modules/FindExtPackages.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ else()
set(OCIO_USE_IMATH_HALF "0" CACHE STRING "Whether 'half' type will be sourced from the Imath library (>=v3.0)" FORCE)
endif()

# fast_float
# https://github.com/fastfloat/fast_float
find_package(FastFloat 3.2.0 REQUIRED)

if(OCIO_BUILD_APPS)

# NOTE: Depending of the compiler version lcms2 2.2 does not compile with
Expand Down
128 changes: 128 additions & 0 deletions share/cmake/modules/FindFastFloat.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright Contributors to the OpenColorIO Project.
#
# Locate or install the fast_float library
#
# Variables defined by this module:
# fast_float_FOUND - If FALSE, do not try to include fast_float.h
# fast_float_INCLUDE_DIR - Where to find fast_float.h
# fast_float_VERSION - The version of the library (if available)
#
# Targets defined by this module:
# fast_float::fast_float - IMPORTED target, if found
#
# The library is include-only, there is no associated binary.
#
# If fast_float is not installed in a standard path, you can use the
# fast_float_ROOT variable to tell CMake where to find it. If it is not found
# and OCIO_INSTALL_EXT_PACKAGES is set to MISSING or ALL, fast_float will be
# downloaded, built, and statically-linked into libOpenColorIO at build time.
#

###############################################################################
### Try to find package ###

if(NOT OCIO_INSTALL_EXT_PACKAGES STREQUAL ALL)
set(_FastFloat_REQUIRED_VARS FastFloat_INCLUDE_DIR)

if(NOT DEFINED FastFloat_ROOT)
# Search for FastFloatConfig.cmake
# Do notice that the CMake Config module is "FastFloat"
# while the library is "fast_float"
# (capital letters replaced by lowercase and underscore)
find_package(FastFloat ${FastFloat_FIND_VERSION} CONFIG QUIET)
endif()

# There is no pkg-config support on fast_float.

# Override REQUIRED if package can be installed
if(OCIO_INSTALL_EXT_PACKAGES STREQUAL MISSING)
set(FastFloat_FIND_REQUIRED FALSE)
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FastFloat
REQUIRED_VARS
${_FastFloat_REQUIRED_VARS}
VERSION_VAR
FastFloat_VERSION
)
endif()

###############################################################################
### Create target

if (NOT TARGET fast_float::fast_float)
add_library(fast_float::fast_float INTERFACE IMPORTED GLOBAL)
set(_FastFloat_TARGET_CREATE TRUE)
endif()

###############################################################################
### Install package from source ###

if(NOT FastFloat_FOUND)
include(ExternalProject)
include(GNUInstallDirs)

set(_EXT_DIST_ROOT "${CMAKE_BINARY_DIR}/ext/dist")
set(_EXT_BUILD_ROOT "${CMAKE_BINARY_DIR}/ext/build")

# Set find_package standard args
set(FastFloat_FOUND TRUE)
set(FastFloat_VERSION ${FastFloat_FIND_VERSION})
set(FastFloat_INCLUDE_DIR "${_EXT_DIST_ROOT}/${CMAKE_INSTALL_INCLUDEDIR}")

# This library is include-only. No need to add further flags.

if(_FastFloat_TARGET_CREATE)
set(FastFloat_CMAKE_ARGS
${FastFloat_CMAKE_ARGS}
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_CXX_FLAGS=${FastFloat_CXX_FLAGS}
-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
-DCMAKE_INSTALL_MESSAGE=${CMAKE_INSTALL_MESSAGE}
-DCMAKE_INSTALL_PREFIX=${_EXT_DIST_ROOT}
-DCMAKE_OBJECT_PATH_MAX=${CMAKE_OBJECT_PATH_MAX}
-DFASTFLOAT_TEST=OFF
-DFASTFLOAT_SANITIZE=OFF
)

if(CMAKE_TOOLCHAIN_FILE)
set(FastFloat_CMAKE_ARGS
${FastFloat_CMAKE_ARGS} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE})
endif()

if(APPLE)
set(FastFloat_CMAKE_ARGS
${FastFloat_CMAKE_ARGS} -DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET})
endif()

# Hack to let imported target be built from ExternalProject_Add
file(MAKE_DIRECTORY ${FastFloat_INCLUDE_DIR})

ExternalProject_Add(FastFloat_install
GIT_REPOSITORY "https://github.com/fastfloat/fast_float.git"
GIT_TAG "v${FastFloat_VERSION}"
GIT_CONFIG advice.detachedHead=false
GIT_SHALLOW TRUE
PREFIX "${_EXT_BUILD_ROOT}/fast_float"
CMAKE_ARGS ${FastFloat_CMAKE_ARGS}
EXCLUDE_FROM_ALL TRUE
)

add_dependencies(fast_float::fast_float FastFloat_install)
message(STATUS "Installing fast_float: ${FastFloat_INCLUDE_DIR} (version \"${FastFloat_VERSION}\")")
endif()
endif()

###############################################################################
### Configure target ###

if(_FastFloat_TARGET_CREATE)
set_target_properties(fast_float::fast_float PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${FastFloat_INCLUDE_DIR}
)

mark_as_advanced(FastFloat_INCLUDE_DIR FastFloat_VERSION)
endif()
1 change: 1 addition & 0 deletions src/OpenColorIO/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ target_link_libraries(OpenColorIO
${OCIO_HALF_LIB}
pystring::pystring
sampleicc::sampleicc
fast_float::fast_float
utils::strings
yaml-cpp
)
Expand Down
10 changes: 5 additions & 5 deletions src/OpenColorIO/ParseUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <iostream>
#include <set>
#include <sstream>
#include <fast_float/fast_float.h>

#include <OpenColorIO/OpenColorIO.h>

Expand Down Expand Up @@ -548,9 +549,9 @@ bool StringToFloat(float * fval, const char * str)
{
if(!str) return false;

std::istringstream inputStringstream(str);
float x;
if(!(inputStringstream >> x))
const auto result = fast_float::from_chars(str, str + strlen(str), x);
if (result.ec != std::errc())
{
return false;
}
Expand Down Expand Up @@ -599,10 +600,9 @@ bool StringVecToFloatVec(std::vector<float> &floatArray, const StringUtils::Stri

for(unsigned int i=0; i<lineParts.size(); i++)
{
std::istringstream inputStringstream(lineParts[i]);
float x;
if(!(inputStringstream >> x))
{
const auto result = fast_float::from_chars(lineParts[i], lineParts[i] + strlen(lineParts[i]), x);
if (result.ec != std::errc()) {
return false;
}
floatArray[i] = x;
Expand Down
18 changes: 17 additions & 1 deletion src/OpenColorIO/fileformats/FileFormatSpi1D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <cstdio>
#include <sstream>
#include <fast_float/fast_float.h>

#include <OpenColorIO/OpenColorIO.h>

Expand Down Expand Up @@ -124,10 +125,25 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream,
}
else if(StringUtils::StartsWith(headerLine, "From"))
{
if (sscanf(lineBuffer, "From %f %f", &from_min, &from_max) != 2)
char fromMinS[64] = "";
char fromMaxS[64] = "";
#ifdef _WIN32
if (sscanf(lineBuffer, "From %s %s", fromMinS, 64, fromMaxS, 64) != 2)
#else
if (sscanf(lineBuffer, "From %s %s", fromMinS, fromMaxS) != 2)
#endif
{
ThrowErrorMessage("Invalid 'From' Tag", currentLine, headerLine);
}
else
{
const auto fromMinAnswer = fast_float::from_chars(fromMinS, fromMinS +64, from_min);
const auto fromMaxAnswer = fast_float::from_chars(fromMaxS, fromMaxS + 64, from_max);

if (fromMinAnswer.ec != std::errc() || fromMaxAnswer.ec != std::errc()) {
ThrowErrorMessage("Invalid 'From' Tag", currentLine, headerLine);
}
}
}
else if(StringUtils::StartsWith(headerLine, "Components"))
{
Expand Down
36 changes: 34 additions & 2 deletions src/OpenColorIO/fileformats/FileFormatSpi3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <cstdio>
#include <sstream>
#include <vector>
#include <fast_float/fast_float.h>

#include <OpenColorIO/OpenColorIO.h>

Expand Down Expand Up @@ -141,10 +142,41 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream,
{
istream.getline(lineBuffer, MAX_LINE_SIZE);

if (sscanf(lineBuffer, "%d %d %d %f %f %f",
char redValueS[64] = "";
char greenValueS[64] = "";
char blueValueS[64] = "";

#ifdef _WIN32
if (sscanf(lineBuffer,
"%d %d %d %s %s %s",
&rIndex, &gIndex, &bIndex,
redValueS, 64,
greenValueS, 64,
blueValueS, 64) == 6)
#else
if (sscanf(lineBuffer, "%d %d %d %s %s %s",
&rIndex, &gIndex, &bIndex,
&redValue, &greenValue, &blueValue) == 6)
redValueS, greenValueS, blueValueS) == 6)
#endif
{
const auto redValueAnswer = fast_float::from_chars(redValueS, redValueS + 64, redValue);
const auto greenValueAnswer = fast_float::from_chars(greenValueS, greenValueS + 64, greenValue);
const auto blueValueAnswer = fast_float::from_chars(blueValueS, blueValueS + 64, blueValue);

if (redValueAnswer.ec != std::errc()
|| greenValueAnswer.ec != std::errc()
|| blueValueAnswer.ec != std::errc()) {
std::ostringstream os;
os << "Error parsing .spi3d file (";
os << fileName;
os << "). ";
os << "Data is invalid. ";
os << "A color value is specified (";
os << redValueS << " " << greenValueS << " " << blueValueS;
os << ") that cannot be parsed as a floating-point triplet.";
throw Exception(os.str().c_str());
}

bool invalidIndex = false;
if (rIndex < 0 || rIndex >= rSize
|| gIndex < 0 || gIndex >= gSize
Expand Down
1 change: 1 addition & 0 deletions tests/cpu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function(add_ocio_test NAME SOURCES PRIVATE_INCLUDES)
${OCIO_HALF_LIB}
pystring::pystring
sampleicc::sampleicc
fast_float::fast_float
unittest_data
utils::strings
yaml-cpp
Expand Down

0 comments on commit 4937d08

Please sign in to comment.