Skip to content

Commit

Permalink
Span bits
Browse files Browse the repository at this point in the history
  • Loading branch information
jas88 committed Jun 21, 2024
1 parent 41c1857 commit a9f4981
Show file tree
Hide file tree
Showing 9 changed files with 41 additions and 43 deletions.
5 changes: 3 additions & 2 deletions TypeGuesser/Deciders/BoolTypeDecider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Globalization;
using System;
using System.Globalization;
using System.Text.RegularExpressions;

namespace TypeGuesser.Deciders;
Expand All @@ -21,7 +22,7 @@ protected override IDecideTypesForStrings CloneImpl(CultureInfo newCulture)
}

/// <inheritdoc/>
protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? size)
protected override bool IsAcceptableAsTypeImpl(ReadOnlySpan<char> candidateString, IDataTypeSize? size)
{
// "Y" / "N" is boolean unless the settings say it can't
if (!Settings.CharCanBeBoolean && SingleCharacter.IsMatch(candidateString))
Expand Down
34 changes: 14 additions & 20 deletions TypeGuesser/Deciders/DateTimeTypeDecider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,13 @@ public void GuessDateFormat(IEnumerable<string> samples)
}

/// <inheritdoc />
public override bool IsAcceptableAsType(string candidateString, IDataTypeSize? size)
public override bool IsAcceptableAsType(ReadOnlySpan<char> candidateString, IDataTypeSize? size)
{
return IsExplicitDate(candidateString) || base.IsAcceptableAsType(candidateString, size);
}

/// <inheritdoc/>
protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? sizeRecord)
protected override bool IsAcceptableAsTypeImpl(ReadOnlySpan<char> candidateString, IDataTypeSize? sizeRecord)
{
//if it's a float then it isn't a date is it! thanks C# for thinking 1.1 is the first of January
if (_decimalChecker.IsAcceptableAsType(candidateString, sizeRecord))
Expand All @@ -226,52 +226,46 @@ protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataType
}
}

private readonly char[] _space = [' '];

private bool TryBruteParse(string? s, out DateTime dt)
private bool TryBruteParse(ReadOnlySpan<char> s, out DateTime dt)
{
//if it's legit according to the current culture
if (DateTime.TryParse(s, Culture, DateTimeStyles.None, out dt))
return true;

var split = s?.Split(_space, StringSplitOptions.RemoveEmptyEntries);

//if there are no tokens
if (split == null || split.Length == 0)
if (s.IsEmpty)
{
dt = DateTime.MinValue;
dt=DateTime.MinValue;
return false;
}

var sPoint = s.IndexOf(' ');

//if there is one token it is assumed either to be a date or a string
if (split.Length == 1)
if (TryGetTime(split[0], out dt))
return true;
else if (TryGetDate(split[0], out dt))
return true;
else
return false;
if (sPoint == -1)
{
return TryGetTime(s, out dt) || TryGetDate(s, out dt);
}

//if there are 2+ tokens then first token should be a date then the rest (concatenated) should be a time
//e.g. "28/2/1993 5:36:27 AM" gets evaluated as "28/2/1993" and then "5:36:27 AM"

if (TryGetDate(split[0], out dt) && TryGetTime(string.Join(" ", split.Skip(1)), out var time))
if (TryGetDate(s[..sPoint], out dt) && TryGetTime(s[(sPoint+1)..], out var time))
{
dt = new DateTime(dt.Year, dt.Month, dt.Day, time.Hour, time.Minute, time.Second, time.Millisecond);

return true;
}

dt = DateTime.MinValue;
return false;
}

private bool TryGetDate(string v, out DateTime date)
private bool TryGetDate(ReadOnlySpan<char> v, out DateTime date)
{
return DateTime.TryParseExact(v, _dateFormatToUse, Culture, DateTimeStyles.AllowInnerWhite, out date);
}

private bool TryGetTime(string v, out DateTime time)
private bool TryGetTime(ReadOnlySpan<char> v, out DateTime time)
{
return DateTime.TryParseExact(v, TimeFormats, Culture, DateTimeStyles.AllowInnerWhite, out time);
}
Expand Down
10 changes: 5 additions & 5 deletions TypeGuesser/Deciders/DecideTypesForStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ protected DecideTypesForStrings(CultureInfo culture, TypeCompatibilityGroup comp
}

/// <inheritdoc/>
public virtual bool IsAcceptableAsType(string candidateString,IDataTypeSize? size)
public virtual bool IsAcceptableAsType(ReadOnlySpan<char> candidateString, IDataTypeSize? size)
{
//we must preserve leading zeroes if it's not actually 0 -- if they have 010101 then we have to use string but if they have just 0 we can use decimal
return !IDecideTypesForStrings.ZeroPrefixedNumber.IsMatch(candidateString) && IsAcceptableAsTypeImpl(candidateString, size);
Expand All @@ -61,10 +61,10 @@ public virtual bool IsAcceptableAsType(string candidateString,IDataTypeSize? siz
/// </summary>
/// <param name="candidateString"></param>
/// <returns></returns>
protected bool IsExplicitDate(string candidateString)
protected bool IsExplicitDate(ReadOnlySpan<char> candidateString)
{
//if user has an explicit type format in mind and the candidate string is not null (which should hopefully be handled sensibly elsewhere)
if(Settings.ExplicitDateFormats != null && !string.IsNullOrWhiteSpace(candidateString))
if(Settings.ExplicitDateFormats != null && !candidateString.IsEmpty && !candidateString.IsWhiteSpace())
return DateTime.TryParseExact(candidateString,Settings.ExplicitDateFormats,Culture,DateTimeStyles.None,out _);

return false;
Expand Down Expand Up @@ -116,8 +116,8 @@ public IDecideTypesForStrings Clone()
/// <param name="candidateString"></param>
/// <param name="size"></param>
/// <returns></returns>
protected virtual bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeSize? size)
protected virtual bool IsAcceptableAsTypeImpl(ReadOnlySpan<char> candidateString, IDataTypeSize? size)
{
return candidateString.IsConvertibleTo<T>(Culture);
return candidateString.ToString().IsConvertibleTo<T>(Culture);
}
}
9 changes: 5 additions & 4 deletions TypeGuesser/Deciders/DecimalTypeDecider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Data.SqlTypes;
using System;
using System.Data.SqlTypes;
using System.Globalization;
using System.Linq;

Expand Down Expand Up @@ -30,7 +31,7 @@ protected override IDecideTypesForStrings CloneImpl(CultureInfo culture)
}

/// <inheritdoc/>
protected override bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeSize? sizeRecord)
protected override bool IsAcceptableAsTypeImpl(ReadOnlySpan<char> candidateString, IDataTypeSize? sizeRecord)
{
candidateString = TrimTrailingZeros(candidateString);

Expand All @@ -46,10 +47,10 @@ protected override bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeS
return true;
}

private string TrimTrailingZeros(string s)
private ReadOnlySpan<char> TrimTrailingZeros(ReadOnlySpan<char> s)
{
//don't trim 0 unless there's a decimal point e.g. don't trim from 1,000
if (!s.Contains(Culture.NumberFormat.NumberDecimalSeparator))
if (!s.Contains(_decimalIndicator))
return s;


Expand Down
4 changes: 2 additions & 2 deletions TypeGuesser/Deciders/IDecideTypesForStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public partial interface IDecideTypesForStrings
/// </summary>
/// <param name="candidateString">a string containing a value of unknown type (e.g. "fish" or "1")</param>
/// <param name="size">The current size estimate of floating point numbers (or null if not appropriate). This will be modified by the method
/// if appropriate to the data passed</param>
/// if appropriate to the data passed</param>
/// <returns>True if the <paramref name="candidateString"/> is a valid value for the <see cref="TypesSupported"/> by the decider</returns>
bool IsAcceptableAsType(string candidateString,IDataTypeSize? size);
bool IsAcceptableAsType(ReadOnlySpan<char> candidateString, IDataTypeSize? size);

/// <summary>
/// Converts the provided <paramref name="value"/> to an object of the Type modelled by this <see cref="IDecideTypesForStrings"/>.
Expand Down
7 changes: 4 additions & 3 deletions TypeGuesser/Deciders/IntTypeDecider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Globalization;
using System;
using System.Globalization;
using TB.ComponentModel;

namespace TypeGuesser.Deciders;
Expand All @@ -19,12 +20,12 @@ protected override IDecideTypesForStrings CloneImpl(CultureInfo newCulture)
}

/// <inheritdoc/>
protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? sizeRecord)
protected override bool IsAcceptableAsTypeImpl(ReadOnlySpan<char> candidateString, IDataTypeSize? sizeRecord)
{
if(IsExplicitDate(candidateString))
return false;

if (!candidateString.IsConvertibleTo(out int i, Culture))
if (!candidateString.ToString().IsConvertibleTo(out int i, Culture))
return false;

sizeRecord?.Size.IncreaseTo(i.ToString().Trim('-').Length,0);
Expand Down
2 changes: 1 addition & 1 deletion TypeGuesser/Deciders/NeverGuessedTypeDecider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public sealed class NeverGuessTheseTypeDecider(CultureInfo culture) : DecideType
protected override object ParseImpl(string value) => throw new NotSupportedException();

/// <inheritdoc/>
protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? sizeRecord) =>
protected override bool IsAcceptableAsTypeImpl(ReadOnlySpan<char> candidateString, IDataTypeSize? sizeRecord) =>
//strings should never be interpreted as byte arrays
false;
}
2 changes: 1 addition & 1 deletion TypeGuesser/Deciders/TimeSpanTypeDecider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public sealed class TimeSpanTypeDecider(CultureInfo culture) : DecideTypesForStr
protected override object ParseImpl(string value) => DateTime.Parse(value).TimeOfDay;

/// <inheritdoc/>
protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? sizeRecord)
protected override bool IsAcceptableAsTypeImpl(ReadOnlySpan<char> candidateString, IDataTypeSize? sizeRecord)
{
try
{
Expand Down
11 changes: 6 additions & 5 deletions TypeGuesser/Guesser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class Guesser

/// <summary>
/// Normally when measuring the lengths of strings something like "It’s" would be 4 but for Oracle it needs extra width. If this is
/// non zero then when <see cref="AdjustToCompensateForValue(object)"/> is a string then any non standard characters will have this number
/// non zero then when <see cref="AdjustToCompensateForValue(in object)"/> is a string then any non standard characters will have this number
/// added to the length predicted.
/// </summary>
public int ExtraLengthPerNonAsciiCharacter { get; init; }
Expand Down Expand Up @@ -110,17 +110,18 @@ public void AdjustToCompensateForValues(IEnumerable<object> collection)
}

/// <summary>
/// <para>Adjusts the current <see cref="Guess"/> based on the <paramref name="o"/>. All calls to this method for a given <see cref="Guesser"/>
/// <para>Adjusts the current <see cref="Guess"/> based on the <paramref name="originalO"/>. All calls to this method for a given <see cref="Guesser"/>
/// instance must be of the same Type e.g. string. If you pass a hard Typed value in (e.g. int) then the <see cref="Guess"/> will change
/// to the Type of the object but it will still calculate length/digits.
/// </para>
///
/// <para>Passing null / <see cref="DBNull.Value"/> is always allowed and never changes the <see cref="Guess"/></para>
/// </summary>
/// <exception cref="MixedTypingException">Thrown if you mix strings with hard Typed objects when supplying <paramref name="o"/></exception>
/// <param name="o"></param>
public void AdjustToCompensateForValue(object? o)
/// <exception cref="MixedTypingException">Thrown if you mix strings with hard Typed objects when supplying <paramref name="originalO"/></exception>
/// <param name="originalO"></param>
public void AdjustToCompensateForValue(in object? originalO)
{
var o = originalO;
while (true)
{
if (o == null) return;
Expand Down

0 comments on commit a9f4981

Please sign in to comment.