-
Notifications
You must be signed in to change notification settings - Fork 191
Conversation
@@ -208,6 +292,82 @@ public static bool TryParseInt64(string value, out long result) | |||
return long.TryParse(value, NumberStyles.None, NumberFormatInfo.InvariantInfo, out result); | |||
} | |||
|
|||
internal static unsafe bool TryParseInt32(StringSegment value, out int result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if we should use the same name as the older slower TryParseInt32/64
since there are a few behaviour differences. We parse only positive numbers for example, and we don't check for overflows. Also, it's hard to know without looking at the source that the two versions have different performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least add doc comments for the behavior expectations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the old one need to parse negatives? I'd be surprised. You can probably have the old one call the new one.
Add some overflow checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this internal and TryParseInt64 public?
Even tough this is in the HeaderUtilites class, I think that it should probably be renamed. If not, the doc comments should clearly explain why it's different from int.TryParse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea not sure why the old ParseInt32
is internal but I kept the same encapsulation. Should we make that public too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only if it needs to be
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok then, so TryParseInt64 is public because you're using it from Kestrel then? There should be doc comments on the public one explaining the limitations.
private static bool TryParseInt64FromHeaderValue(int startIndex, string headerValue, out long value) | ||
{ | ||
var found = false; | ||
while (startIndex != headerValue.Length) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could potentially turn this into unsafe code too. Not sure if that makes it any faster.
private const string PrivateString = "private"; | ||
private const string ProxyRevalidateString = "proxy-revalidate"; | ||
private const string PublicString = "public"; | ||
private const string SharedMaxAgeString = "s-maxage"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we learned nothing from #712!? I actually prefer const as you can see if you read the PR comments, but we should be consistent and use static readonly instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently we were using these values in a switch statement and they need to be const for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if David will be OK with this because of the switch statement. Like I said, I'm fine with consts given we don't mess up the values, but I would like consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified the switch statement. These strings are now public static readonly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👎 This regressed the code readability and maintainability. These constants aren't worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also avoiding a ToLower() which is required since switch on strings are case-sensitive. I'm seeing a one percent performance increase in ResponseCaching with it. Also it's code within a private method, so I'm not too concerned about its readability.
private const string ProxyRevalidateString = "proxy-revalidate"; | ||
private const string PublicString = "public"; | ||
private const string SharedMaxAgeString = "s-maxage"; | ||
public const string MaxAgeString = "max-age"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public static readonly string
@@ -208,6 +292,82 @@ public static bool TryParseInt64(string value, out long result) | |||
return long.TryParse(value, NumberStyles.None, NumberFormatInfo.InvariantInfo, out result); | |||
} | |||
|
|||
internal static unsafe bool TryParseInt32(StringSegment value, out int result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least add doc comments for the behavior expectations.
@@ -208,6 +292,82 @@ public static bool TryParseInt64(string value, out long result) | |||
return long.TryParse(value, NumberStyles.None, NumberFormatInfo.InvariantInfo, out result); | |||
} | |||
|
|||
internal static unsafe bool TryParseInt32(StringSegment value, out int result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the old one need to parse negatives? I'd be surprised. You can probably have the old one call the new one.
Add some overflow checks.
} | ||
} | ||
|
||
public static unsafe bool TryParseInt64(StringSegment value, out long result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was testing it through TryParseTimeSpan but I'll add some tests specific to this method.
Consume the new API from Request/Response.ContentLength |
if (ch != end) | ||
{ | ||
result = 0; | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
false
if (ch != end) | ||
{ | ||
result = 0; | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
false
🆙📅 |
🆙📅 @BrennanConroy @Tratcher @halter73 @davidfowl Here's some performance results tested with ResponseCaching: |
long value; | ||
var rawValue = headers[HeaderNames.ContentLength]; | ||
if (rawValue.Count == 1 && | ||
!string.IsNullOrWhiteSpace(rawValue[0]) && | ||
long.TryParse(rawValue[0], styles, CultureInfo.InvariantCulture, out value)) | ||
HeaderUtilities.TryParseInt64(new StringSegment(rawValue[0]).Trim(), out value)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should do SetContentLength while we're at it, but I don't know if we have any optimized code for this yet. Kestrel has one but it's for hex.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified the FormatInt64
implementation.
As a reference, formatting long num = 1234567890;
to "1234567890"
for 30000000 iterations:
Old: 2027ms using num.ToString(CultureInfo.InvariantCulture)
New: 1023ms using FormatInt64(num)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// <code>false</code>. | ||
/// </returns> | ||
// e.g. { "headerValue=10, targetHeaderValue=30" } | ||
public static bool TryParseTimeSpan(StringValues headerValues, string targetValue, out TimeSpan? value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name is misleading, you're not parsing a fully formatted time, only integer seconds. You just happen to represent the output as a TimeSpan. TryParseSeconds?
{ | ||
for (var i = 0; i < headerValues.Count; i++) | ||
{ | ||
var index = headerValues[i].IndexOf(targetValue, StringComparison.OrdinalIgnoreCase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will give you false positives for partial matches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not only partial matches, this also doesn't differentiate between name and value. i.e. header: foo=bar
would be considered to contain bar
where it should only contain foo
. This needs a bit of rework.
270cf93
to
bc7e27f
Compare
🆙📅 |
return false; | ||
} | ||
|
||
private static unsafe bool TryParseInt64FromHeaderValue(int startIndex, string headerValue, out long result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bleh...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the implementation.
{ | ||
// Consider using Math.DivRem() | ||
var quotient = value / 10; | ||
charBuffer[--position] = (char)(0x30 + (value - quotient * 10)); // 0x30 = '0' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a char* here so we don't need to load position
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave this a try but there isn't any improvement.
9945d6d
to
ae2afb2
Compare
- Add allocation free parsing of int32, int64 - Improve performance of converting int64 to string - Add parsing of seconds from header values - Add check for existence of cache directives - Expose CacheControlHeaderValue constants
ae2afb2
to
d50a241
Compare
#737
cc @BrennanConroy @Tratcher