WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
Bug 213635
Convert SVG related parsers over to using StringParsingBuffer
https://bugs.webkit.org/show_bug.cgi?id=213635
Summary
Convert SVG related parsers over to using StringParsingBuffer
Sam Weinig
Reported
2020-06-25 20:49:05 PDT
Convert SVG related parsers over to using StringParsingBuffer
Attachments
Patch
(132.54 KB, patch)
2020-06-25 20:52 PDT
,
Sam Weinig
no flags
Details
Formatted Diff
Diff
Patch
(132.51 KB, patch)
2020-06-26 08:40 PDT
,
Sam Weinig
no flags
Details
Formatted Diff
Diff
Patch
(128.44 KB, patch)
2020-06-26 14:43 PDT
,
Sam Weinig
no flags
Details
Formatted Diff
Diff
Patch
(129.11 KB, patch)
2020-06-26 18:10 PDT
,
Sam Weinig
no flags
Details
Formatted Diff
Diff
Patch
(129.25 KB, patch)
2020-06-26 18:50 PDT
,
Sam Weinig
no flags
Details
Formatted Diff
Diff
Show Obsolete
(4)
View All
Add attachment
proposed patch, testcase, etc.
Sam Weinig
Comment 1
2020-06-25 20:52:49 PDT
Created
attachment 402846
[details]
Patch
Sam Weinig
Comment 2
2020-06-26 08:40:35 PDT
Created
attachment 402867
[details]
Patch
Sam Weinig
Comment 3
2020-06-26 14:43:44 PDT
Created
attachment 402906
[details]
Patch
Said Abou-Hallawa
Comment 4
2020-06-26 15:10:24 PDT
Comment on
attachment 402906
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=402906&action=review
> Source/WebCore/svg/SVGLengthList.cpp:75 > +String SVGLengthList::valueAsString() const > +{ > + StringBuilder builder; > + > + for (const auto& length : m_items) { > + if (builder.length()) > + builder.append(' '); > + > + builder.append(length->value().valueAsString()); > + } > + > + return builder.toString(); > +}
Since you are moving this function and others similar to it from the header files to cpp files, I would suggest removing all the valueAsString() methods from the SVG list classes and implement: 1. SVGValuePropertyList::valueAsString() 2. SVGPrimitiveList::valueAsString(). The loop in these two functions will the same as this one. The only difference is: 1. SVGValuePropertyList will append item->valueAsString() 2. SVGPrimitiveList will append SVGPropertyTraits<PropertyType>::toString(item). This will remove the duplicate code and will make the cpp files mainly for parsing.
Darin Adler
Comment 5
2020-06-26 15:49:29 PDT
Comment on
attachment 402906
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=402906&action=review
> Source/WebCore/svg/SVGAnimationElement.cpp:81 > +static Optional<Vector<UnitBezier>> parseKeySplines(const StringView& string)
Shouldn't we prefer StringView over const StringView&?
> Source/WebCore/svg/SVGAnimationElement.cpp:84 > + if (string.isEmpty()) > return WTF::nullopt;
Is this a valuable optimization? I don’t think it is. Or is it needed for correctness.
> Source/WebCore/svg/SVGFitToViewBox.cpp:88 > Optional<FloatRect> SVGFitToViewBox::parseViewBox(const StringView& value)
Shouldn't we prefer StringView over const StringView&?
> Source/WebCore/svg/SVGLengthList.h:55 > + bool parse(const String& value);
Any downside in changing this to take StringView?
> Source/WebCore/svg/SVGLengthValue.cpp:71 > + auto firstChar = *buffer; > + ++buffer;
Why not *buffer++?
> Source/WebCore/svg/SVGLengthValue.cpp:77 > + auto secondChar = *buffer; > + ++buffer;
Ditto.
> Source/WebCore/svg/SVGLengthValue.h:66 > + static Optional<SVGLengthValue> construct(SVGLengthMode, const StringView&); > + static SVGLengthValue construct(SVGLengthMode, const StringView&, SVGParsingError&, SVGLengthNegativeValuesMode = SVGLengthNegativeValuesMode::Allow);
StringView instead of const StringView&?
> Source/WebCore/svg/SVGLengthValue.h:90 > + ExceptionOr<void> setValueAsString(const StringView&); > + ExceptionOr<void> setValueAsString(const StringView&, SVGLengthMode);
StringView instead of const StringView&?
> Source/WebCore/svg/SVGNumberList.cpp:65 > + StringBuilder builder; > + > + for (const auto& number : m_items) { > + if (builder.length()) > + builder.append(' '); > + > + builder.append(number->value()); > + } > + > + return builder.toString();
Another idiom we should find a way to improve. I don’t like how we have to write these join loops.
> Source/WebCore/svg/SVGNumberList.h:53 > + bool parse(const String&);
StringView?
> Source/WebCore/svg/SVGNumberList.h:54 > + String valueAsString() const override;
final?
> Source/WebCore/svg/SVGParserUtilities.cpp:117 > + exponent += *buffer - '0'; > + ++buffer;
*buffer++?
> Source/WebCore/svg/SVGPathSource.h:22 > +#include "FloatPoint.h"
Makes me a tiny bit sad. Not sure what made this happen.
> Source/WebCore/svg/SVGPathStringSource.h:49 > + template<typename F> decltype(auto) parse(F&&);
I’d prefer a word rather than "F". Is this a function?
> Source/WebCore/svg/SVGPointList.h:53 > + bool parse(const String& value);
StringView?
> Source/WebCore/svg/SVGPreserveAspectRatioValue.cpp:365 > + case SVG_PRESERVEASPECTRATIO_NONE: > + return "none"; > + case SVG_PRESERVEASPECTRATIO_XMINYMIN: > + return "xMinYMin"; > + case SVG_PRESERVEASPECTRATIO_XMIDYMIN: > + return "xMidYMin"; > + case SVG_PRESERVEASPECTRATIO_XMAXYMIN: > + return "xMaxYMin"; > + case SVG_PRESERVEASPECTRATIO_XMINYMID: > + return "xMinYMid"; > + case SVG_PRESERVEASPECTRATIO_XMIDYMID: > + return "xMidYMid"; > + case SVG_PRESERVEASPECTRATIO_XMAXYMID: > + return "xMaxYMid"; > + case SVG_PRESERVEASPECTRATIO_XMINYMAX: > + return "xMinYMax"; > + case SVG_PRESERVEASPECTRATIO_XMIDYMAX: > + return "xMidYMax"; > + case SVG_PRESERVEASPECTRATIO_XMAXYMAX: > + return "xMaxYMax"; > + case SVG_PRESERVEASPECTRATIO_UNKNOWN: > + return "unknown";
Want some "_s" here I think. Unless the ASCIILiteral optimization is unimportant.
> Source/WebCore/svg/SVGPreserveAspectRatioValue.h:55 > + SVGPreserveAspectRatioValue(const StringView&);
StringView rather than const StringView&?
> Source/WebCore/svg/SVGPreserveAspectRatioValue.h:67 > + bool parse(const StringView&);
StringView rather than const StringView&?
> Source/WebCore/svg/SVGPreserveAspectRatioValue.h:78 > + template<typename CharacterType> > + bool parseInternal(StringParsingBuffer<CharacterType>&, bool validate);
On one line?
> Source/WebCore/svg/SVGStringList.h:54 > + bool parse(const String& data, UChar delimiter);
StringView
> Source/WebCore/svg/SVGTransformList.cpp:59 > + for (const auto& transform : m_items)
I would have omitted the const here.
> Source/WebCore/svg/SVGTransformList.h:61 > + void parse(const String&);
StringView
> Source/WebCore/svg/SVGTransformable.cpp:100 > +constexpr int requiredValuesForType[] = {0, 6, 1, 1, 1, 1, 1}; > +constexpr int optionalValuesForType[] = {0, 0, 1, 1, 2, 0, 0};
Remove the extra space before "{"? Not sure we should have removed static. Because constexpr does imply const, but not necessarily internal linkage. Unclear on the considerations.
> Source/WebCore/svg/SVGTransformable.h:37 > + static Optional<SVGTransformValue::SVGTransformType> parseTransformType(const StringView&);
StringView rather than const StringView&?
Sam Weinig
Comment 6
2020-06-26 16:08:22 PDT
(In reply to Said Abou-Hallawa from
comment #4
)
> Comment on
attachment 402906
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=402906&action=review
> > > Source/WebCore/svg/SVGLengthList.cpp:75 > > +String SVGLengthList::valueAsString() const > > +{ > > + StringBuilder builder; > > + > > + for (const auto& length : m_items) { > > + if (builder.length()) > > + builder.append(' '); > > + > > + builder.append(length->value().valueAsString()); > > + } > > + > > + return builder.toString(); > > +} > > Since you are moving this function and others similar to it from the header > files to cpp files, I would suggest removing all the valueAsString() methods > from the SVG list classes and implement: > > 1. SVGValuePropertyList::valueAsString() > 2. SVGPrimitiveList::valueAsString(). > > The loop in these two functions will the same as this one. The only > difference is: > > 1. SVGValuePropertyList will append item->valueAsString() > 2. SVGPrimitiveList will append > SVGPropertyTraits<PropertyType>::toString(item). > > This will remove the duplicate code and will make the cpp files mainly for > parsing.
I am not really convinced this is worth it. Doing this for all the classes would cause a pessimization of SVGNumberList and SVGPointList due to unnecessary extra allocations of Strings. There might be ways to avoid this, but I am not sure it is worth the complexity.
Sam Weinig
Comment 7
2020-06-26 18:10:22 PDT
Created
attachment 402936
[details]
Patch
Sam Weinig
Comment 8
2020-06-26 18:49:44 PDT
(In reply to Darin Adler from
comment #5
)
> Comment on
attachment 402906
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=402906&action=review
> > > Source/WebCore/svg/SVGAnimationElement.cpp:81 > > +static Optional<Vector<UnitBezier>> parseKeySplines(const StringView& string) > > Shouldn't we prefer StringView over const StringView&?
Fixed.
> > > Source/WebCore/svg/SVGAnimationElement.cpp:84 > > + if (string.isEmpty()) > > return WTF::nullopt; > > Is this a valuable optimization? I don’t think it is. Or is it needed for > correctness.
It's needed for correctness (or at least, to preserve the current behavior).
> > > Source/WebCore/svg/SVGFitToViewBox.cpp:88 > > Optional<FloatRect> SVGFitToViewBox::parseViewBox(const StringView& value) > > Shouldn't we prefer StringView over const StringView&?
Fixed,
> > > Source/WebCore/svg/SVGLengthList.h:55 > > + bool parse(const String& value); > > Any downside in changing this to take StringView?
Fixed.
> > > Source/WebCore/svg/SVGLengthValue.cpp:71 > > + auto firstChar = *buffer; > > + ++buffer; > > Why not *buffer++?
Fixed.
> > > Source/WebCore/svg/SVGLengthValue.cpp:77 > > + auto secondChar = *buffer; > > + ++buffer; > > Ditto.
Fixed.
> > > Source/WebCore/svg/SVGLengthValue.h:66 > > + static Optional<SVGLengthValue> construct(SVGLengthMode, const StringView&); > > + static SVGLengthValue construct(SVGLengthMode, const StringView&, SVGParsingError&, SVGLengthNegativeValuesMode = SVGLengthNegativeValuesMode::Allow); > > StringView instead of const StringView&?
Fixed.
> > > Source/WebCore/svg/SVGLengthValue.h:90 > > + ExceptionOr<void> setValueAsString(const StringView&); > > + ExceptionOr<void> setValueAsString(const StringView&, SVGLengthMode); > > StringView instead of const StringView&?
Fixed.
> > > Source/WebCore/svg/SVGNumberList.cpp:65 > > + StringBuilder builder; > > + > > + for (const auto& number : m_items) { > > + if (builder.length()) > > + builder.append(' '); > > + > > + builder.append(number->value()); > > + } > > + > > + return builder.toString(); > > Another idiom we should find a way to improve. I don’t like how we have to > write these join loops.
Yeah :(. This has been on my mind since I did the variadic append work a while back. I would like to solve it by generalizing StringTypeAdapter to be able to work in a lazier manner in some cases. Currently, StringTypeAdapter really wants to know the total length and is8Bit state upfront, or have you compute them potentially multiple times. A naive solution would end up looking something like this: builder.append(Join(m_items, ' ', [](auto itemToTransform) { itemToTransform->value() })); for: template<typename List, typename Delimiter, typename ListTransformer> struct Join { List& list; Delimiter delimiter; ListTransformer& listTransformer; }; template<typename List, typename Delimiter, typename ListTransformer> class StringTypeAdapter<Join<List, Delimiter, ListTransformer>> { public: StringTypeAdapter(const Join<List, Delimiter, ListTransformer>& join) : m_join { join } , m_delimiterAdapter { m_join.delimiter } { unsigned numberOfItems = std::size(m_join.list); unsigned numberOfDelimiters = numberOfItems > 1 ? numberOfItems - 1 : 0; m_length = numberOfDelimiters * m_delimiterAdapter.length(); m_is8Bit = m_delimiterAdapter.is8Bit(); for (auto& item : m_join.list) { auto transformedItem = m_join.listTransformer(item); StringTypeAdapter<decltype(transformedItem)> adaptor { transformedItem }; m_length += adaptor.length(); if (m_is8Bit && !adaptor.is8Bit()) m_is8Bit = false; } } unsigned length() const { return m_length; } bool is8Bit() const { return m_is8Bit; } template<typename CharacterType> void writeTo(CharacterType* destination) const { bool firstIteration = true; for (auto& item : m_join.list) { if (!firstIteration) { m_delimiterAdapter.writeTo(destination); destination += m_delimiterAdapter.length(); } auto transformedItem = m_join.listTransformer(item); StringTypeAdapter<decltype(transformedItem)> adaptor { transformedItem }; m_delimiterAdapter.writeTo(destination); destination += adaptor.length(); } } private: const Join<List, Delimiter, ListTransformer>& m_join; StringTypeAdapter<Delimiter> m_delimiterAdapter; unsigned m_length; bool m_is8Bit; }; (I could also see a version where the adaptor stored a Vector<StringTypeAdapter<decltype(m_join.listTransformer(m_join.list[0]))>>>) Which leads to the question whether the avoidance of resizes of the StringBuilder storage is worth iterating the loop multiple times. Other option would be to just add a StringBuilder::appendByJoining(Container&, Delimiter&, Transformer&), Maybe that would be a good improvement even if not perfect.
> > > Source/WebCore/svg/SVGNumberList.h:53 > > + bool parse(const String&); > > StringView?
Fixed.
> > > Source/WebCore/svg/SVGNumberList.h:54 > > + String valueAsString() const override; > > final?
Made the class final, since it wasn't but should be. Is our style to use final on functions as well as the class? I can't recall.
> > > Source/WebCore/svg/SVGParserUtilities.cpp:117 > > + exponent += *buffer - '0'; > > + ++buffer; > > *buffer++?
Fixed,
> > > Source/WebCore/svg/SVGPathSource.h:22 > > +#include "FloatPoint.h" > > Makes me a tiny bit sad. Not sure what made this happen.
Removed a different #include "FloatPoint.h". The header has FloatPoint member variables, so it's hard to avoid.
> > > Source/WebCore/svg/SVGPathStringSource.h:49 > > + template<typename F> decltype(auto) parse(F&&); > > I’d prefer a word rather than "F". Is this a function?
Fixed.
> > > Source/WebCore/svg/SVGPointList.h:53 > > + bool parse(const String& value); > > StringView?
Fixed.
> > > Source/WebCore/svg/SVGPreserveAspectRatioValue.cpp:365 > > + case SVG_PRESERVEASPECTRATIO_NONE: > > + return "none"; > > + case SVG_PRESERVEASPECTRATIO_XMINYMIN: > > + return "xMinYMin"; > > + case SVG_PRESERVEASPECTRATIO_XMIDYMIN: > > + return "xMidYMin"; > > + case SVG_PRESERVEASPECTRATIO_XMAXYMIN: > > + return "xMaxYMin"; > > + case SVG_PRESERVEASPECTRATIO_XMINYMID: > > + return "xMinYMid"; > > + case SVG_PRESERVEASPECTRATIO_XMIDYMID: > > + return "xMidYMid"; > > + case SVG_PRESERVEASPECTRATIO_XMAXYMID: > > + return "xMaxYMid"; > > + case SVG_PRESERVEASPECTRATIO_XMINYMAX: > > + return "xMinYMax"; > > + case SVG_PRESERVEASPECTRATIO_XMIDYMAX: > > + return "xMidYMax"; > > + case SVG_PRESERVEASPECTRATIO_XMAXYMAX: > > + return "xMaxYMax"; > > + case SVG_PRESERVEASPECTRATIO_UNKNOWN: > > + return "unknown"; > > Want some "_s" here I think. Unless the ASCIILiteral optimization is > unimportant.
Fixed.
> > > Source/WebCore/svg/SVGPreserveAspectRatioValue.h:55 > > + SVGPreserveAspectRatioValue(const StringView&); > > StringView rather than const StringView&?
Fixed.
> > > Source/WebCore/svg/SVGPreserveAspectRatioValue.h:67 > > + bool parse(const StringView&); > > StringView rather than const StringView&?
Fixed.
> > > Source/WebCore/svg/SVGPreserveAspectRatioValue.h:78 > > + template<typename CharacterType> > > + bool parseInternal(StringParsingBuffer<CharacterType>&, bool validate); > > On one line?
Fixed. Drat. Thought I caught all of them.
> > > Source/WebCore/svg/SVGStringList.h:54 > > + bool parse(const String& data, UChar delimiter); > > StringView
Fixed.
> > > Source/WebCore/svg/SVGTransformList.cpp:59 > > + for (const auto& transform : m_items) > > I would have omitted the const here.
Fixed.
> > > Source/WebCore/svg/SVGTransformList.h:61 > > + void parse(const String&); > > StringView
Fixed,
> > > Source/WebCore/svg/SVGTransformable.cpp:100 > > +constexpr int requiredValuesForType[] = {0, 6, 1, 1, 1, 1, 1}; > > +constexpr int optionalValuesForType[] = {0, 0, 1, 1, 2, 0, 0}; > > Remove the extra space before "{"? > > Not sure we should have removed static. Because constexpr does imply const, > but not necessarily internal linkage. Unclear on the considerations.
Will add back.
> > > Source/WebCore/svg/SVGTransformable.h:37 > > + static Optional<SVGTransformValue::SVGTransformType> parseTransformType(const StringView&); > > StringView rather than const StringView&?
Fixed.
Sam Weinig
Comment 9
2020-06-26 18:50:18 PDT
Created
attachment 402938
[details]
Patch
EWS
Comment 10
2020-06-27 08:29:36 PDT
Committed
r263617
: <
https://trac.webkit.org/changeset/263617
> All reviewed patches have been landed. Closing bug and clearing flags on
attachment 402938
[details]
.
Radar WebKit Bug Importer
Comment 11
2020-06-27 08:30:15 PDT
<
rdar://problem/64843777
>
Darin Adler
Comment 12
2020-06-28 12:54:09 PDT
(In reply to Sam Weinig from
comment #8
)
> > > Source/WebCore/svg/SVGNumberList.h:54 > > > + String valueAsString() const override; > > > > final? > > Made the class final, since it wasn't but should be. Is our style to use > final on functions as well as the class?
It is.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug