[webkit-reviews] review denied: [Bug 228122] Add functionalities for parsing URL query string : [Attachment 434736] Patch

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Mon Aug 2 11:06:04 PDT 2021


Darin Adler <darin at apple.com> has denied Risul Islam <risul_islam at apple.com>'s
request for review:
Bug 228122: Add functionalities for parsing URL query string
https://bugs.webkit.org/show_bug.cgi?id=228122

Attachment 434736: Patch

https://bugs.webkit.org/attachment.cgi?id=434736&action=review




--- Comment #24 from Darin Adler <darin at apple.com> ---
Comment on attachment 434736
  --> https://bugs.webkit.org/attachment.cgi?id=434736
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=434736&action=review

I appreciate the work to do a sorting-based algorithm instead of a hash table.

> Source/WTF/wtf/HashFunctions.h:182
> +    template<typename T, typename U> struct KeyValuePairHash {
> +	   static unsigned hash(const KeyValuePair<T, U>& p)
> +	   {
> +	       return pairIntHash(DefaultHash<T>::hash(p.key),
DefaultHash<U>::hash(p.value));
> +	   }
> +	   static bool equal(const KeyValuePair<T, U>& a, const KeyValuePair<T,
U>& b)
> +	   {
> +	       return DefaultHash<T>::equal(a.key, b.key) &&
DefaultHash<U>::equal(a.value, b.value);
> +	   }
> +	   static constexpr bool safeToCompareToEmptyOrDeleted =
DefaultHash<T>::safeToCompareToEmptyOrDeleted &&
DefaultHash<U>::safeToCompareToEmptyOrDeleted;
> +    };

We aren’t using a KeyValuePair as a hash table key. Can we either not do this
at all at this time, or do it in a separate patch?

> Source/WTF/wtf/HashFunctions.h:282
> +    template<typename T, typename U> struct DefaultHash<KeyValuePair<T, U>>
: KeyValuePairHash<T, U> { };

We aren’t using a KeyValuePair as a hash table key. Can we either not do this
at all at this time, or do it in a separate patch?

> Source/WTF/wtf/HashTraits.h:431
> +using WTF::KeyValuePairHashTraits;

We aren’t using a KeyValuePair as a hash table key. Can we either not do this
at all at this time, or do it in a separate patch?

> Source/WTF/wtf/KeyValuePair.h:67
> +    template <typename K, typename V>
> +    bool operator==(const KeyValuePair<K, V>& other) const
> +    {
> +	   return key == other.key && value == other.value;
> +    }

I’d like to see this be done as a free function, after the class definition:

    template<typename KeyType, typename ValueType> constexpr bool
operator=(const KeyValuePair<KeyType, ValueType>& a, const
KeyValuePair<KeyType, ValueType>& b)
    {
	return a.key == b.key && a.value == b.value;
    }

But also, this change is no longer needed in this patch. We aren’t using a
KeyValuePair as a hash table key. Can we either not do this at all at this
time, or do it in a separate patch?

> Source/WTF/wtf/URL.cpp:1189
> +	   StringBuilder firstKeyValuePairAppended;
> +	   StringBuilder secondKeyValuePairAppended;
> +	   firstKeyValuePairAppended.append(firstKeyValuePair.key,
firstKeyValuePair.value);
> +	   secondKeyValuePairAppended.append(secondKeyValuePair.key,
secondKeyValuePair.value);
> +	   return firstKeyValuePairAppended.toString().utf8() <
secondKeyValuePairAppended.toString().utf8();

1) There is no reason to repeat this lambda twice! We can put it in a local
variable and use it twice. Also, we repeat the comparison below, and should
reuse for that too.

2) Concatenating strings using StringBuilder, then converting to UTF-8 is a
super-expensive way to compare and makes this unnecessarily inefficient. Seems
unlikely the sorting order matters at all:

Code should be more like this:

    auto compare = [&] (const KeyValuePair<String, String>& a, const
KeyValuePair<String, String>& b) {
	if (int result = codePointCompare(a.key, b.key))
	    return result;
	return codePointCompare(a.value, b.value);
    };
    auto comparesLessThan = [&] (const KeyValuePair<String, String>& a, const
KeyValuePair<String, String>& b) {
	return compare(a, b) < 0;
    };

    std::sort(firstQueryParameters.begin(), firstQueryParameters.end(),
comparesLessThan);
    std::sort(secondQueryParameters.begin(), secondQueryParameters.end(),
comparesLessThan);

> Source/WTF/wtf/URL.cpp:1219
> +	   if (tempParameterFromFirst.toString().utf8() <
tempParameterFromSecond.toString().utf8()) {
> +	      
differingQueryParameters.append(firstQueryParameters[indexInFirstQueryParameter
]);
> +	       indexInFirstQueryParameter++;
> +	   } else if (tempParameterFromSecond.toString().utf8() <
tempParameterFromFirst.toString().utf8()) {
> +	      
differingQueryParameters.append(secondQueryParameters[indexInSecondQueryParamet
er]);
> +	       indexInSecondQueryParameter++;
> +	   } else {
> +	       indexInFirstQueryParameter++;
> +	       indexInSecondQueryParameter++;
> +	   }

Same issue here, should not be building temporary strings and converting to
UTF-8 just compare. Also, since it’s important to use the same comparison
function so we do it consistently, this makes the refactoring above more
valuable.

    int comparison = compare(firstQueryParameters[indexInFirstQueryParameter],
secondQueryParameters[indexInSecondQueryParameter]);
    if (comparison < 0) {
	...
    } else if (comparison > 0) {
	...
    } else {
	...
    }

> Source/WTF/wtf/URL.cpp:1240
> +    return url.string()[url.pathEnd() - 1] == '/' ? url.pathEnd() - 1 :
url.pathEnd();

I don’t understand why this "/" removal is needed. Which test case falls if we
remove this logic?

> Source/WTF/wtf/URL.cpp:1251
> +bool isEqualIgnoringQueryAndFragments(const URL& firstURL, const URL&
secondURL)
> +{
> +    if (!firstURL.isValid() || !secondURL.isValid())
> +	   return false;
> +    
> +    auto lengthOfFirstURLIgnoringQueryAndFragments =
lengthOfURLIgnoringQueryAndFragments(firstURL);
> +    auto secondLengthOfSecondURLIgnoringQueryAndFragments =
lengthOfURLIgnoringQueryAndFragments(secondURL);
> +    return StringView(firstURL.string()).substring(0,
lengthOfFirstURLIgnoringQueryAndFragments) ==
StringView(secondURL.string()).substring(0,
secondLengthOfSecondURLIgnoringQueryAndFragments);
> +}

Seems a little peculiar that two invalid URLs always compare as unequal even if
they are identical. Is that really the behavior we want?

>From a factoring point of view, one more function and shorter argument names
can make this code much clearer and easier to read:

    static StringView substringIgnoringQueryAndFragments(const URL& url)
    {
	return StringView { url.string()
}.left(lengthOfURLIgnoringQueryAndFragments(url));
    }

    bool isEqualIgnoringQueryAndFragments(const URL& a, const URL& b)
    {
	return a.isValid() && b.isValid() &&
substringIgnoringQueryAndFragments(a) == substringIgnoringQueryAndFragments(b);
    }


More information about the webkit-reviews mailing list