Skip to content

Conversation

thinlang
Copy link

@thinlang thinlang commented Aug 26, 2025

Description

When parsing strings, the code sometimes oversteps the string iterator to end or past end and then dereferences without checking if it's valid to do so. This results in an exception being thrown in newer versions of Visual Studio.

The change involves modifying functions that take a current position and end iterator. Instead, they're passed an object that tracks both of those and ensures that:

  1. The iterator never steps past end
  2. The iterator is only dereferenced when it is safe to do so

This change also includes some cleanup of warnings on Windows

Related Issue

#205

Motivation and Context

String parsing should not throw exceptions for reasonable or badly formed files.

How Has This Been Tested?

I ran the test suite locally on Windows per the readme instructions.

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • I have signed the Adobe Open Source CLA.
  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

When parsing strings, the code sometimes oversteps the string iterator to end or past end and then dereferences without checking if it's valid to do so.
This results in an exception being thrown in newer versions of Visual Studio.

The change involves moving the code from passing a current position and end iterator to various functions. Instead, they're passed an object that tracks both of those and ensures that:
1. The iterator never steps past end
2. The iterator is only dereferenced when it is safe to do so

This change also includes some cleanup of warnings on Windows
@thinlang thinlang requested a review from dirkschulze as a code owner August 26, 2025 16:15
@thinlang thinlang changed the title This is a fix for the string parser code overstepping to (or past) "end()" Fix for issue 205: the string parser code overstepping to (or past) "end()" Aug 26, 2025
@thinlang thinlang closed this Aug 27, 2025
@thinlang thinlang reopened this Aug 27, 2025
@thinlang thinlang closed this Aug 27, 2025
@thinlang thinlang reopened this Aug 27, 2025
@thinlang thinlang closed this Aug 27, 2025
@thinlang thinlang reopened this Aug 27, 2025
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4290 /wd4335 /wd4355 /wd4814 /wd4091 /TP")
if (NOT USE_SKIA)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++14")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to have an interoperable library that is able to go back to C++11. So a requirement for C++14 should be avoided.

We do have code that allows to switch to C++17 but keeping C++11 compatibility. See PR #194.

}

SVG_ASSERT(radiusX == radiusY); // untested
//SVG_ASSERT(radiusX == radiusY); // untested
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this assertion? Float comparison should include epsilon, but in general?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assert fired on my machine when running the tests on Windows locally

template <typename T>
auto operator-(T) = delete;

[[nodiscard]] explicit operator bool() const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not compatible to C++11. If you think we should add it regardless, think about compatible ways like with such macro setup:

#if defined(__has_cpp_attribute)
#  if __has_cpp_attribute(nodiscard) && __cplusplus >= 201703L
#    define NODISCARD [[nodiscard]]
#  elif defined(__GNUC__) || defined(__clang__)
#    define NODISCARD __attribute__((warn_unused_result))
#  elif defined(_MSC_VER)
#    define NODISCARD _Check_return_
#  else
#    define NODISCARD
#  endif
#else
#  if defined(__GNUC__) || defined(__clang__)
#    define NODISCARD __attribute__((warn_unused_result))
#  elif defined(_MSC_VER)
#    define NODISCARD _Check_return_
#  else
#    define NODISCARD
#  endif
#endif

return c >= '0' && c <= '9';
}
template <typename T>
auto operator+(T) = delete;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is C++20, no? So not valid in C++11 either.

namespace SVGStringParser
{
using CharIt = std::string::const_iterator;
class CharPos final {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I do like avoiding the endPos check, this can lead to a bigger performance degradation in a very performance critical path. We should run performance tests with huge path strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants