mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-25 22:36:59 -04:00
@@ -16,8 +16,15 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
public static Dictionary<T1, T2> Merge<T1, T2>(this Dictionary<T1, T2> first, Dictionary<T1, T2> second)
|
||||
{
|
||||
if (first == null) throw new ArgumentNullException(nameof(first));
|
||||
if (second == null) throw new ArgumentNullException(nameof(second));
|
||||
if (first == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(first));
|
||||
}
|
||||
|
||||
if (second == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(second));
|
||||
}
|
||||
|
||||
var merged = new Dictionary<T1, T2>();
|
||||
first.ToList().ForEach(kv => merged[kv.Key] = kv.Value);
|
||||
|
||||
@@ -1,36 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
public static class ExceptionExtensions
|
||||
{
|
||||
public static T WithData<T>(this T ex, string key, string value) where T : Exception
|
||||
public static T WithData<T>(this T ex, string key, string value)
|
||||
where T : Exception
|
||||
{
|
||||
ex.AddData(key, value);
|
||||
|
||||
return ex;
|
||||
}
|
||||
public static T WithData<T>(this T ex, string key, int value) where T : Exception
|
||||
|
||||
public static T WithData<T>(this T ex, string key, int value)
|
||||
where T : Exception
|
||||
{
|
||||
ex.AddData(key, value.ToString());
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
public static T WithData<T>(this T ex, string key, Http.HttpUri value) where T : Exception
|
||||
public static T WithData<T>(this T ex, string key, Http.HttpUri value)
|
||||
where T : Exception
|
||||
{
|
||||
ex.AddData(key, value.ToString());
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
|
||||
public static T WithData<T>(this T ex, Http.HttpResponse response, int maxSampleLength = 512) where T : Exception
|
||||
public static T WithData<T>(this T ex, Http.HttpResponse response, int maxSampleLength = 512)
|
||||
where T : Exception
|
||||
{
|
||||
if (response == null || response.Content == null) return ex;
|
||||
if (response == null || response.Content == null)
|
||||
{
|
||||
return ex;
|
||||
}
|
||||
|
||||
var contentSample = response.Content.Substring(0, Math.Min(response.Content.Length, maxSampleLength));
|
||||
|
||||
@@ -50,16 +54,19 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
ex.AddData("ContentType", response.Headers.ContentType ?? string.Empty);
|
||||
}
|
||||
|
||||
ex.AddData("ContentLength", response.Content.Length.ToString());
|
||||
ex.AddData("ContentSample", contentSample);
|
||||
|
||||
return ex;
|
||||
}
|
||||
|
||||
|
||||
private static void AddData(this Exception ex, string key, string value)
|
||||
{
|
||||
if (value.IsNullOrWhiteSpace()) return;
|
||||
if (value.IsNullOrWhiteSpace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ex.Data[key] = value;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Diff Match and Patch
|
||||
* Copyright 2018 The diff-match-patch Authors.
|
||||
@@ -25,9 +25,8 @@ using System.Numerics;
|
||||
|
||||
namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
|
||||
public static class FuzzyContainsExtension {
|
||||
|
||||
public static class FuzzyContainsExtension
|
||||
{
|
||||
public static int FuzzyFind(this string text, string pattern, double matchProb)
|
||||
{
|
||||
return match(text, pattern, matchProb).Item1;
|
||||
@@ -38,7 +37,7 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
return match(text, pattern, 0.25).Item2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locate the best instance of 'pattern' in 'text'.
|
||||
* Returns (-1, 1) if no match found.
|
||||
@@ -46,11 +45,13 @@ namespace NzbDrone.Common.Extensions
|
||||
* @param pattern The pattern to search for.
|
||||
* @return Best match index or -1.
|
||||
*/
|
||||
private static Tuple<int, double> match(string text, string pattern, double matchThreshold = 0.5) {
|
||||
private static Tuple<int, double> match(string text, string pattern, double matchThreshold = 0.5)
|
||||
{
|
||||
// Check for null inputs not needed since null can't be passed in C#.
|
||||
if (text.Length == 0 || pattern.Length == 0) {
|
||||
if (text.Length == 0 || pattern.Length == 0)
|
||||
{
|
||||
// Nothing to match.
|
||||
return new Tuple<int, double> (-1, 0);
|
||||
return new Tuple<int, double>(-1, 0);
|
||||
}
|
||||
|
||||
if (pattern.Length <= text.Length)
|
||||
@@ -59,7 +60,7 @@ namespace NzbDrone.Common.Extensions
|
||||
if (loc != -1)
|
||||
{
|
||||
// Perfect match!
|
||||
return new Tuple<int, double> (loc, 1);
|
||||
return new Tuple<int, double>(loc, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,63 +75,81 @@ namespace NzbDrone.Common.Extensions
|
||||
* @param pattern The pattern to search for.
|
||||
* @return Best match index or -1.
|
||||
*/
|
||||
private static Tuple<int, double> match_bitap(string text, string pattern, double matchThreshold) {
|
||||
|
||||
private static Tuple<int, double> match_bitap(string text, string pattern, double matchThreshold)
|
||||
{
|
||||
// Initialise the alphabet.
|
||||
Dictionary<char, BigInteger> s = alphabet(pattern);
|
||||
|
||||
// don't keep creating new BigInteger(1)
|
||||
var big1 = new BigInteger(1);
|
||||
|
||||
// Lowest score belowe which we give up.
|
||||
var score_threshold = matchThreshold;
|
||||
|
||||
|
||||
// Initialise the bit arrays.
|
||||
var matchmask = big1 << (pattern.Length - 1);
|
||||
int best_loc = -1;
|
||||
|
||||
// Empty initialization added to appease C# compiler.
|
||||
var last_rd = new BigInteger[0];
|
||||
for (int d = 0; d < pattern.Length; d++) {
|
||||
for (int d = 0; d < pattern.Length; d++)
|
||||
{
|
||||
// Scan for the best match; each iteration allows for one more error.
|
||||
int start = 1;
|
||||
int finish = text.Length + pattern.Length;
|
||||
|
||||
var rd = new BigInteger[finish + 2];
|
||||
rd[finish + 1] = (big1 << d) - big1;
|
||||
for (int j = finish; j >= start; j--) {
|
||||
for (int j = finish; j >= start; j--)
|
||||
{
|
||||
BigInteger charMatch;
|
||||
if (text.Length <= j - 1 || !s.ContainsKey(text[j - 1])) {
|
||||
if (text.Length <= j - 1 || !s.ContainsKey(text[j - 1]))
|
||||
{
|
||||
// Out of range.
|
||||
charMatch = 0;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
charMatch = s[text[j - 1]];
|
||||
}
|
||||
if (d == 0) {
|
||||
|
||||
if (d == 0)
|
||||
{
|
||||
// First pass: exact match.
|
||||
rd[j] = ((rd[j + 1] << 1) | big1) & charMatch;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subsequent passes: fuzzy match.
|
||||
rd[j] = ((rd[j + 1] << 1) | big1) & charMatch
|
||||
| (((last_rd[j + 1] | last_rd[j]) << 1) | big1) | last_rd[j + 1];
|
||||
}
|
||||
if ((rd[j] & matchmask) != 0) {
|
||||
|
||||
if ((rd[j] & matchmask) != 0)
|
||||
{
|
||||
var score = bitapScore(d, pattern);
|
||||
|
||||
// This match will almost certainly be better than any existing
|
||||
// match. But check anyway.
|
||||
if (score >= score_threshold) {
|
||||
if (score >= score_threshold)
|
||||
{
|
||||
// Told you so.
|
||||
score_threshold = score;
|
||||
best_loc = j - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bitapScore(d + 1, pattern) < score_threshold) {
|
||||
|
||||
if (bitapScore(d + 1, pattern) < score_threshold)
|
||||
{
|
||||
// No hope for a (better) match at greater error levels.
|
||||
break;
|
||||
}
|
||||
|
||||
last_rd = rd;
|
||||
}
|
||||
return new Tuple<int, double> (best_loc, score_threshold);
|
||||
|
||||
return new Tuple<int, double>(best_loc, score_threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,8 +158,9 @@ namespace NzbDrone.Common.Extensions
|
||||
* @param pattern Pattern being sought.
|
||||
* @return Overall score for match (1.0 = good, 0.0 = bad).
|
||||
*/
|
||||
private static double bitapScore(int e, string pattern) {
|
||||
return 1.0 - (double)e / pattern.Length;
|
||||
private static double bitapScore(int e, string pattern)
|
||||
{
|
||||
return 1.0 - ((double)e / pattern.Length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,19 +168,25 @@ namespace NzbDrone.Common.Extensions
|
||||
* @param pattern The text to encode.
|
||||
* @return Hash of character locations.
|
||||
*/
|
||||
private static Dictionary<char, BigInteger> alphabet(string pattern) {
|
||||
private static Dictionary<char, BigInteger> alphabet(string pattern)
|
||||
{
|
||||
var s = new Dictionary<char, BigInteger>();
|
||||
char[] char_pattern = pattern.ToCharArray();
|
||||
foreach (char c in char_pattern) {
|
||||
if (!s.ContainsKey(c)) {
|
||||
foreach (char c in char_pattern)
|
||||
{
|
||||
if (!s.ContainsKey(c))
|
||||
{
|
||||
s.Add(c, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
foreach (char c in char_pattern) {
|
||||
foreach (char c in char_pattern)
|
||||
{
|
||||
s[c] = s[c] | (new BigInteger(1) << (pattern.Length - i - 1));
|
||||
i++;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
@@ -14,8 +13,10 @@ namespace NzbDrone.Common.Extensions
|
||||
return source.Where(element => knownKeys.Add(keySelector(element)));
|
||||
}
|
||||
|
||||
public static IEnumerable<TFirst> IntersectBy<TFirst, TSecond, TKey>(this IEnumerable<TFirst> first, Func<TFirst, TKey> firstKeySelector,
|
||||
IEnumerable<TSecond> second, Func<TSecond, TKey> secondKeySelector,
|
||||
public static IEnumerable<TFirst> IntersectBy<TFirst, TSecond, TKey>(this IEnumerable<TFirst> first,
|
||||
Func<TFirst, TKey> firstKeySelector,
|
||||
IEnumerable<TSecond> second,
|
||||
Func<TSecond, TKey> secondKeySelector,
|
||||
IEqualityComparer<TKey> keyComparer)
|
||||
{
|
||||
var keys = new HashSet<TKey>(second.Select(secondKeySelector), keyComparer);
|
||||
@@ -32,9 +33,11 @@ namespace NzbDrone.Common.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<TFirst> ExceptBy<TFirst, TSecond, TKey>(this IEnumerable<TFirst> first, Func<TFirst, TKey> firstKeySelector,
|
||||
IEnumerable<TSecond> second, Func<TSecond, TKey> secondKeySelector,
|
||||
IEqualityComparer<TKey> keyComparer)
|
||||
public static IEnumerable<TFirst> ExceptBy<TFirst, TSecond, TKey>(this IEnumerable<TFirst> first,
|
||||
Func<TFirst, TKey> firstKeySelector,
|
||||
IEnumerable<TSecond> second,
|
||||
Func<TSecond, TKey> secondKeySelector,
|
||||
IEqualityComparer<TKey> keyComparer)
|
||||
{
|
||||
var keys = new HashSet<TKey>(second.Select(secondKeySelector), keyComparer);
|
||||
var matchedKeys = new HashSet<TKey>();
|
||||
@@ -70,6 +73,7 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
throw new ArgumentNullException("source");
|
||||
}
|
||||
|
||||
if (predicate == null)
|
||||
{
|
||||
throw new ArgumentNullException("predicate");
|
||||
@@ -91,6 +95,7 @@ namespace NzbDrone.Common.Extensions
|
||||
result[key] = item;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -105,6 +110,7 @@ namespace NzbDrone.Common.Extensions
|
||||
result[key] = valueSelector(item);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,15 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
const int bytesInKb = 1024;
|
||||
|
||||
if (bytes < 0) return "-" + SizeSuffix(-bytes);
|
||||
if (bytes == 0) return "0 B";
|
||||
if (bytes < 0)
|
||||
{
|
||||
return "-" + SizeSuffix(-bytes);
|
||||
}
|
||||
|
||||
if (bytes == 0)
|
||||
{
|
||||
return "0 B";
|
||||
}
|
||||
|
||||
var mag = (int)Math.Log(bytes, bytesInKb);
|
||||
var adjustedSize = bytes / (decimal)Math.Pow(bytesInKb, mag);
|
||||
|
||||
@@ -6,9 +6,20 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
public static int LevenshteinDistance(this string text, string other, int costInsert = 1, int costDelete = 1, int costSubstitute = 1)
|
||||
{
|
||||
if (text == other) return 0;
|
||||
if (text.Length == 0) return other.Length * costInsert;
|
||||
if (other.Length == 0) return text.Length * costDelete;
|
||||
if (text == other)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (text.Length == 0)
|
||||
{
|
||||
return other.Length * costInsert;
|
||||
}
|
||||
|
||||
if (other.Length == 0)
|
||||
{
|
||||
return text.Length * costDelete;
|
||||
}
|
||||
|
||||
int[] matrix = new int[other.Length + 1];
|
||||
|
||||
@@ -47,4 +58,4 @@ namespace NzbDrone.Common.Extensions
|
||||
return expected.LevenshteinDistance(other, 1, 3, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
public static T JsonClone<T>(this T source) where T : new()
|
||||
public static T JsonClone<T>(this T source)
|
||||
where T : new()
|
||||
{
|
||||
var json = source.ToJson();
|
||||
return Json.Deserialize<T>(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
var info = new FileInfo(path.Trim());
|
||||
|
||||
if (OsInfo.IsWindows && info.FullName.StartsWith(@"\\")) //UNC
|
||||
//UNC
|
||||
if (OsInfo.IsWindows && info.FullName.StartsWith(@"\\"))
|
||||
{
|
||||
return info.FullName.TrimEnd('/', '\\', ' ');
|
||||
}
|
||||
@@ -58,7 +59,11 @@ namespace NzbDrone.Common.Extensions
|
||||
comparison = DiskProviderBase.PathStringComparison;
|
||||
}
|
||||
|
||||
if (firstPath.Equals(secondPath, comparison.Value)) return true;
|
||||
if (firstPath.Equals(secondPath, comparison.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return string.Equals(firstPath.CleanFilePath(), secondPath.CleanFilePath(), comparison.Value);
|
||||
}
|
||||
|
||||
@@ -92,6 +97,7 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
if (childPath != "/" && !parentPath.EndsWith(":\\"))
|
||||
{
|
||||
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
ms.Write(buffer, 0, read);
|
||||
}
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
public static object NullSafe(this object target)
|
||||
{
|
||||
if (target != null) return target;
|
||||
if (target != null)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
return "[NULL]";
|
||||
}
|
||||
|
||||
@@ -76,7 +80,9 @@ namespace NzbDrone.Common.Extensions
|
||||
public static string TrimEnd(this string text, string postfix)
|
||||
{
|
||||
if (text.EndsWith(postfix))
|
||||
{
|
||||
text = text.Substring(0, text.Length - postfix.Length);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
@@ -105,12 +111,12 @@ namespace NzbDrone.Common.Extensions
|
||||
{
|
||||
return text.StartsWith(startsWith, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
|
||||
public static bool EqualsIgnoreCase(this string text, string equals)
|
||||
{
|
||||
return text.Equals(equals, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
|
||||
public static bool ContainsIgnoreCase(this string text, string contains)
|
||||
{
|
||||
return text.IndexOf(contains, StringComparison.InvariantCultureIgnoreCase) > -1;
|
||||
@@ -129,7 +135,7 @@ namespace NzbDrone.Common.Extensions
|
||||
public static byte[] HexToByteArray(this string input)
|
||||
{
|
||||
return Enumerable.Range(0, input.Length)
|
||||
.Where(x => x%2 == 0)
|
||||
.Where(x => x % 2 == 0)
|
||||
.Select(x => Convert.ToByte(input.Substring(x, 2), 16))
|
||||
.ToArray();
|
||||
}
|
||||
@@ -146,9 +152,9 @@ namespace NzbDrone.Common.Extensions
|
||||
var first = int.Parse(octalValue.Substring(0, 1));
|
||||
var second = int.Parse(octalValue.Substring(1, 1));
|
||||
var third = int.Parse(octalValue.Substring(2, 1));
|
||||
var byteResult = (byte)((first << 6) | (second << 3) | (third));
|
||||
var byteResult = (byte)((first << 6) | (second << 3) | third);
|
||||
|
||||
return Encoding.ASCII.GetString(new [] { byteResult });
|
||||
return Encoding.ASCII.GetString(new[] { byteResult });
|
||||
}
|
||||
|
||||
public static string SplitCamelCase(this string input)
|
||||
@@ -193,15 +199,16 @@ namespace NzbDrone.Common.Extensions
|
||||
indexDistance = Math.Abs(i - x);
|
||||
}
|
||||
}
|
||||
sum += (1.0 - (double)indexDistance / weightDenom) * high;
|
||||
|
||||
sum += (1.0 - (indexDistance / weightDenom)) * high;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static double LevenshteinCoefficient(this string a, string b)
|
||||
{
|
||||
return 1.0 - (double)a.LevenshteinDistance(b) / Math.Max(a.Length, b.Length);
|
||||
return 1.0 - ((double)a.LevenshteinDistance(b) / Math.Max(a.Length, b.Length));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace NzbDrone.Common.Extensions
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Common.Extensions
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user