-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51 from phnx47/feat/binformat
Replace BinaryFormatter with BinaryWriter
- Loading branch information
Showing
30 changed files
with
1,138 additions
and
449 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
*.user | ||
*.opencover.xml | ||
*.orig | ||
|
||
.idea | ||
|
||
bin | ||
obj | ||
BenchmarkDotNet.Artifacts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,158 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Runtime.Serialization.Formatters.Binary; | ||
using System.Security.Cryptography; | ||
|
||
namespace FingerprintBuilder | ||
namespace FingerprintBuilder; | ||
|
||
public class FingerprintBuilder<T> : IFingerprintBuilder<T> | ||
{ | ||
public class FingerprintBuilder<T> : IFingerprintBuilder<T> | ||
private readonly Func<byte[], byte[]> _computeHash; | ||
private readonly IDictionary<string, Func<T, object>> _fingerprints = new SortedDictionary<string, Func<T, object>>(StringComparer.OrdinalIgnoreCase); | ||
|
||
private readonly Type[] _supportedTypes = | ||
{ | ||
private readonly Func<byte[], byte[]> _computeHash; | ||
private readonly IDictionary<string, Func<T, object>> _fingerprints; | ||
typeof(bool), | ||
typeof(byte), | ||
typeof(sbyte), | ||
typeof(byte[]), | ||
typeof(char), | ||
typeof(char[]), | ||
typeof(double), | ||
typeof(decimal), | ||
typeof(short), | ||
typeof(ushort), | ||
typeof(int), | ||
typeof(uint), | ||
typeof(long), | ||
typeof(ulong), | ||
typeof(float), | ||
typeof(string) | ||
}; | ||
|
||
private FingerprintBuilder(Func<byte[], byte[]> computeHash) | ||
{ | ||
_computeHash = computeHash ?? throw new ArgumentNullException(nameof(computeHash)); | ||
_fingerprints = new SortedDictionary<string, Func<T, object>>(StringComparer.OrdinalIgnoreCase); | ||
} | ||
private FingerprintBuilder(Func<byte[], byte[]> computeHash) | ||
{ | ||
_computeHash = computeHash ?? throw new ArgumentNullException(nameof(computeHash)); | ||
} | ||
|
||
public static IFingerprintBuilder<T> Create(Func<byte[], byte[]> computeHash) => | ||
new FingerprintBuilder<T>(computeHash); | ||
public static IFingerprintBuilder<T> Create(HashAlgorithm hashAlgorithm) => Create(hashAlgorithm.ComputeHash); | ||
|
||
public IFingerprintBuilder<T> For<TProperty>(Expression<Func<T, TProperty>> expression) => | ||
For<TProperty>(expression, _ => _); | ||
public static IFingerprintBuilder<T> Create(Func<byte[], byte[]> computeHash) => new FingerprintBuilder<T>(computeHash); | ||
|
||
public IFingerprintBuilder<T> For<TProperty>(Expression<Func<T, TProperty>> expression, Expression<Func<TProperty, string>> fingerprint) => | ||
For<TProperty, string>(expression, fingerprint); | ||
public IFingerprintBuilder<T> For<TProperty>(Expression<Func<T, TProperty>> expression) => For(expression, f => f); | ||
|
||
public IFingerprintBuilder<T> For<TProperty>(Expression<Func<T, TProperty>> expression, Expression<Func<TProperty, TProperty>> fingerprint) | ||
public IFingerprintBuilder<T> For(Expression<Func<T, string>> expression, bool toLower, bool trim) | ||
{ | ||
var format = (Func<string, string>)(input => | ||
{ | ||
return For<TProperty, TProperty>(expression, fingerprint); | ||
} | ||
if (toLower) | ||
input = input.ToLowerInvariant(); | ||
private IFingerprintBuilder<T> For<TProperty, TPropertyType>(Expression<Func<T, TProperty>> expression, Expression<Func<TProperty, TPropertyType>> fingerprint) | ||
{ | ||
if (!(expression.Body is MemberExpression memberExpression)) | ||
throw new ArgumentException("Expression must be a member expression"); | ||
if (trim) | ||
input = input.Trim(); | ||
if (_fingerprints.ContainsKey(memberExpression.Member.Name)) | ||
throw new ArgumentException($"Member {memberExpression.Member.Name} has already been added."); | ||
return input; | ||
}); | ||
|
||
var getValue = expression.Compile(); | ||
var getFingerprint = fingerprint.Compile(); | ||
return For(expression, input => format(input)); | ||
} | ||
|
||
_fingerprints[memberExpression.Member.Name] = obj => | ||
{ | ||
var value = getValue(obj); | ||
return value == null ? default : getFingerprint(value); | ||
}; | ||
public IFingerprintBuilder<T> For<TProperty>(Expression<Func<T, TProperty>> expression, Expression<Func<TProperty, string>> fingerprint) => | ||
For<TProperty, string>(expression, fingerprint); | ||
|
||
return this; | ||
} | ||
private IFingerprintBuilder<T> For<TProperty, TReturnType>(Expression<Func<T, TProperty>> expression, Expression<Func<TProperty, TReturnType>> fingerprint) | ||
{ | ||
if (expression.Body is not MemberExpression memberExpression) | ||
throw new ArgumentException("Expression must be a member expression"); | ||
|
||
public Func<T, byte[]> Build() | ||
var memberName = memberExpression.Member.Name; | ||
|
||
if (_fingerprints.ContainsKey(memberExpression.Member.Name)) | ||
throw new ArgumentException("Member has already been added", memberName); | ||
|
||
var returnType = typeof(TReturnType); | ||
if (!_supportedTypes.Contains(typeof(TReturnType))) | ||
throw new ArgumentException($"Unsupported Type: {returnType.Name}", memberName); | ||
|
||
var getValue = expression.Compile(); | ||
var getFingerprint = fingerprint.Compile(); | ||
|
||
_fingerprints[memberExpression.Member.Name] = entity => | ||
{ | ||
var binaryFormatter = new BinaryFormatter(); | ||
var value = getValue(entity); | ||
return value == null ? default : getFingerprint(value); | ||
}; | ||
|
||
return obj => | ||
return this; | ||
} | ||
|
||
public Func<T, byte[]> Build() | ||
{ | ||
return entity => | ||
{ | ||
using var memory = new MemoryStream(); | ||
using var binaryWriter = new BinaryWriter(memory); | ||
foreach (var item in _fingerprints) | ||
{ | ||
using (var memory = new MemoryStream()) | ||
var value = item.Value(entity); | ||
switch (value) | ||
{ | ||
foreach (var item in _fingerprints) | ||
{ | ||
var graph = item.Value(obj); | ||
if (graph != null) | ||
binaryFormatter.Serialize(memory, graph); | ||
} | ||
var arr = memory.ToArray(); | ||
lock (_computeHash) | ||
return _computeHash(arr); | ||
case bool typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case byte typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case sbyte typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case byte[] typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case char typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case char[] typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case double typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case decimal typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case short typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case ushort typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case int typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case uint typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case long typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case ulong typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case float typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
case string typedValue: | ||
binaryWriter.Write(typedValue); | ||
break; | ||
} | ||
}; | ||
} | ||
} | ||
var bytes = memory.ToArray(); | ||
lock (_computeHash) | ||
return _computeHash(bytes); | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,35 @@ | ||
using System; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
|
||
namespace FingerprintBuilder | ||
namespace FingerprintBuilder; | ||
|
||
public static class FingerprintBuilderExtensions | ||
{ | ||
public static class FingerprintBuilderExtensions | ||
/// <summary> | ||
/// Convert to LowerCase Hexadecimal string | ||
/// </summary> | ||
public static string ToLowerHexString(this byte[] source) | ||
{ | ||
public static IFingerprintBuilder<T> For<T>(this IFingerprintBuilder<T> builder, Expression<Func<T, string>> expression, bool toLowerCase, bool ignoreWhiteSpace) | ||
{ | ||
var format = (Func<string, string>)(input => | ||
{ | ||
if (toLowerCase) | ||
input = input.ToLowerInvariant(); | ||
if (ignoreWhiteSpace) | ||
input = input.Trim(); | ||
return input; | ||
}); | ||
|
||
return builder.For(expression, input => format(input)); | ||
} | ||
|
||
/// <summary> | ||
/// Convert to LowerCase Hexadecimal string | ||
/// </summary> | ||
public static string ToLowerHexString(this byte[] source) | ||
{ | ||
return source.ToString("x2"); | ||
} | ||
return source.ToString("x2"); | ||
} | ||
|
||
/// <summary> | ||
/// Convert to UpperCase Hexadecimal string | ||
/// </summary> | ||
public static string ToUpperHexString(this byte[] source) | ||
{ | ||
return source.ToString("X2"); | ||
} | ||
/// <summary> | ||
/// Convert to UpperCase Hexadecimal string | ||
/// </summary> | ||
public static string ToUpperHexString(this byte[] source) | ||
{ | ||
return source.ToString("X2"); | ||
} | ||
|
||
/// <summary> | ||
/// Convert to string | ||
/// </summary> | ||
private static string ToString(this byte[] source, string format) | ||
{ | ||
if (source == null) | ||
throw new ArgumentNullException(nameof(source)); | ||
/// <summary> | ||
/// Convert to string | ||
/// </summary> | ||
private static string ToString(this byte[] source, string format) | ||
{ | ||
if (source == null) | ||
throw new ArgumentNullException(nameof(source)); | ||
|
||
return string.Join("", source.Select(ch => ch.ToString(format, CultureInfo.InvariantCulture))); | ||
} | ||
return string.Join("", source.Select(ch => ch.ToString(format, CultureInfo.InvariantCulture))); | ||
} | ||
} |
Oops, something went wrong.