Skip to content

Commit 508de11

Browse files
committed
Added faster implementation of long.TryParse(...)
Up to ~20% performance improvement when decoding torrent files.
1 parent 4740df8 commit 508de11

File tree

1 file changed

+66
-6
lines changed

1 file changed

+66
-6
lines changed

BencodeNET.Net45/Bencode.cs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Globalization;
42
using System.IO;
53
using System.Text;
64
using BencodeNET.Exceptions;
@@ -11,9 +9,9 @@ namespace BencodeNET
119
{
1210
public static class Bencode
1311
{
14-
private static Encoding _defaultEncoding = Encoding.UTF8;
12+
private const int Int64MaxDigits = 19;
1513

16-
private static readonly NumberFormatInfo _invariantNumberFormat = CultureInfo.InvariantCulture.NumberFormat;
14+
private static Encoding _defaultEncoding = Encoding.UTF8;
1715

1816
/// <summary>
1917
/// Gets or sets the default encoding used to convert strings to and from bytes
@@ -184,7 +182,7 @@ public static BString DecodeString(BencodeStream stream, Encoding encoding)
184182
}
185183

186184
long stringLength;
187-
if (!long.TryParse(lengthString.ToString(), NumberStyles.None, _invariantNumberFormat, out stringLength))
185+
if (!TryParseLongFast(lengthString.ToString(), out stringLength))
188186
{
189187
throw new BencodeDecodingException<BString>(string.Format("Invalid length of string '{0}'", lengthString), startPosition);
190188
}
@@ -278,7 +276,7 @@ public static BNumber DecodeNumber(BencodeStream stream)
278276
throw new BencodeDecodingException<BNumber>("Missing end character 'e'.", stream.Position);
279277

280278
long number;
281-
if (!long.TryParse(digits.ToString(), NumberStyles.AllowLeadingSign, _invariantNumberFormat, out number))
279+
if (!TryParseLongFast(digits.ToString(), out number))
282280
{
283281
throw new BencodeDecodingException<BNumber>(
284282
string.Format("The value '{0}' is invalid. Supported values range from {1:N0} to {2:N0}",
@@ -437,5 +435,67 @@ public static TorrentFile DecodeTorrentFile(BencodeStream stream, Encoding encod
437435
var bdictionary = DecodeDictionary(stream, encoding);
438436
return new TorrentFile(bdictionary);
439437
}
438+
439+
/// <summary>
440+
/// A faster implementation than <see cref="long.TryParse(string, out long)"/>
441+
/// because we skip some checks that are not needed.
442+
/// </summary>
443+
private static bool TryParseLongFast(string value, out long result)
444+
{
445+
result = 0;
446+
447+
if (value == null)
448+
return false;
449+
450+
var length = value.Length;
451+
452+
// Cannot parse empty string
453+
if (length == 0)
454+
return false;
455+
456+
var startIndex = 0;
457+
var isNegative = false;
458+
459+
// Check if negative and set startIndex accordingly
460+
if (value[0] == '-')
461+
{
462+
// Cannot parse just '-'
463+
if (length == 1)
464+
return false;
465+
466+
isNegative = true;
467+
startIndex = 1;
468+
}
469+
470+
// Cannot parse string longer than long.MaxValue
471+
if (length - startIndex > Int64MaxDigits)
472+
return false;
473+
474+
long parsedLong = 0;
475+
for (var i = startIndex; i < length; i++)
476+
{
477+
var character = value[i];
478+
if (!character.IsDigit())
479+
return false;
480+
481+
var digit = character - '0';
482+
483+
if (isNegative)
484+
parsedLong = 10 * parsedLong - digit;
485+
else
486+
parsedLong = 10 * parsedLong + digit;
487+
}
488+
489+
// Negative - should be less than zero
490+
if (isNegative && parsedLong >= 0)
491+
return false;
492+
493+
// Positive - should be equal to or greater than zero
494+
if (!isNegative && parsedLong < 0)
495+
return false;
496+
497+
result = parsedLong;
498+
return true;
499+
}
440500
}
441501
}

0 commit comments

Comments
 (0)