Skip to content

Commit

Permalink
Simplify redirect builder constraint data structure (#9)
Browse files Browse the repository at this point in the history
* Simplify redirect builder constraint data structure

* Use ConcurrentStack for RedirectSwitch to avoid complicated concurrency logic
  • Loading branch information
devodo authored Jul 28, 2022
1 parent 4baa4c3 commit a521505
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 84 deletions.
2 changes: 1 addition & 1 deletion src/DivertR/IRedirectOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IRedirectOptionsBuilder<TTarget> where TTarget : class
IRedirectOptionsBuilder<TTarget> DisableSatisfyStrict(bool disableStrict = true);

IRedirectOptionsBuilder<TTarget> DecorateCallHandler(Func<ICallHandler<TTarget>, ICallHandler<TTarget>> decorator);
IRedirectOptionsBuilder<TTarget> DecorateCallConstraint(Func<ICallConstraint<TTarget>, ICallConstraint<TTarget>> decorator);
IRedirectOptionsBuilder<TTarget> AddCallConstraint(ICallConstraint<TTarget> callConstraint);

IRedirectOptionsBuilder<TTarget> Repeat(int repeatCount);
IRedirectOptionsBuilder<TTarget> Skip(int skipCount);
Expand Down
12 changes: 9 additions & 3 deletions src/DivertR/Internal/ActionRedirectBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using DivertR.Record;
using DivertR.Record.Internal;

Expand All @@ -12,6 +13,11 @@ public ActionRedirectBuilder(ICallValidator callValidator, ICallConstraint<TTarg
{
}

protected ActionRedirectBuilder(ICallValidator callValidator, ConcurrentBag<ICallConstraint<TTarget>> callConstraints)
: base(callValidator, callConstraints)
{
}

public new IActionRedirectBuilder<TTarget> AddConstraint(ICallConstraint<TTarget> callConstraint)
{
base.AddConstraint(callConstraint);
Expand Down Expand Up @@ -58,7 +64,7 @@ public IRedirect Build<TArgs>(Action<IActionRedirectCall<TTarget, TArgs>, TArgs>

public IActionRedirectBuilder<TTarget, TArgs> Args<TArgs>() where TArgs : struct, IStructuralComparable, IStructuralEquatable, IComparable
{
return new ActionRedirectBuilder<TTarget, TArgs>(CallValidator, CallConstraint);
return new ActionRedirectBuilder<TTarget, TArgs>(CallValidator, CallConstraints);
}
}

Expand All @@ -68,8 +74,8 @@ internal class ActionRedirectBuilder<TTarget, TArgs> : ActionRedirectBuilder<TTa
{
private readonly IValueTupleMapper _valueTupleMapper;

public ActionRedirectBuilder(ICallValidator callValidator, ICallConstraint<TTarget> callConstraint)
: base(callValidator, callConstraint)
public ActionRedirectBuilder(ICallValidator callValidator, ConcurrentBag<ICallConstraint<TTarget>> callConstraints)
: base(callValidator, callConstraints)
{
_valueTupleMapper = ValueTupleMapperFactory.Create<TArgs>();
CallValidator.Validate(_valueTupleMapper);
Expand Down
40 changes: 9 additions & 31 deletions src/DivertR/Internal/CompositeCallConstraint.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;

namespace DivertR.Internal
{
internal class CompositeCallConstraint<TTarget> : ICallConstraint<TTarget> where TTarget : class
{
private readonly ImmutableArray<ICallConstraint<TTarget>> _callConstraints;
private readonly ReadOnlyCollection<ICallConstraint<TTarget>> _callConstraints;

public static readonly CompositeCallConstraint<TTarget> Empty = new CompositeCallConstraint<TTarget>(ImmutableArray<ICallConstraint<TTarget>>.Empty);

private CompositeCallConstraint(ImmutableArray<ICallConstraint<TTarget>> callConstraints)
public CompositeCallConstraint(IEnumerable<ICallConstraint<TTarget>> callConstraints)
{
_callConstraints = callConstraints;
_callConstraints = Array.AsReadOnly(callConstraints.ToArray());
}

public CompositeCallConstraint<TTarget> AddCallConstraint(ICallConstraint<TTarget> callConstraint)
{
return new CompositeCallConstraint<TTarget>(_callConstraints.Add(callConstraint));
}

public CompositeCallConstraint<TTarget> AddCallConstraints(IEnumerable<ICallConstraint<TTarget>> callConstraints)
{
return new CompositeCallConstraint<TTarget>(_callConstraints.AddRange(callConstraints));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsMatch(ICallInfo<TTarget> callInfo)
{
Expand All @@ -35,25 +25,13 @@ public bool IsMatch(ICallInfo<TTarget> callInfo)

internal class CompositeCallConstraint : ICallConstraint
{
private readonly ImmutableArray<ICallConstraint> _callConstraints;

public static readonly CompositeCallConstraint Empty = new CompositeCallConstraint(ImmutableArray<ICallConstraint>.Empty);
private readonly ReadOnlyCollection<ICallConstraint> _callConstraints;

private CompositeCallConstraint(ImmutableArray<ICallConstraint> callConstraints)
public CompositeCallConstraint(IEnumerable<ICallConstraint> callConstraints)
{
_callConstraints = callConstraints;
_callConstraints = Array.AsReadOnly(callConstraints.ToArray());
}

public CompositeCallConstraint AddCallConstraint(ICallConstraint callConstraint)
{
return new CompositeCallConstraint(_callConstraints.Add(callConstraint));
}

public CompositeCallConstraint AddCallConstraints(IEnumerable<ICallConstraint> callConstraints)
{
return new CompositeCallConstraint(_callConstraints.AddRange(callConstraints));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsMatch(ICallInfo callInfo)
{
Expand Down
21 changes: 17 additions & 4 deletions src/DivertR/Internal/DelegateRedirectBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;

namespace DivertR.Internal
{
Expand All @@ -9,7 +10,13 @@ internal abstract class DelegateRedirectBuilder<TTarget> : RedirectBuilder<TTarg
protected DelegateRedirectBuilder(ICallValidator callValidator, ICallConstraint<TTarget> callConstraint)
: base(callConstraint)
{
CallValidator = callValidator ?? throw new ArgumentNullException(nameof(callValidator));
CallValidator = callValidator;
}

protected DelegateRedirectBuilder(ICallValidator callValidator, ConcurrentBag<ICallConstraint<TTarget>> callConstraints)
: base(callConstraints)
{
CallValidator = callValidator;
}

public IRedirect Build(Delegate redirectDelegate, Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null)
Expand All @@ -24,17 +31,23 @@ public IRedirect Build(Delegate redirectDelegate, Action<IRedirectOptionsBuilder

internal abstract class DelegateRedirectBuilder : RedirectBuilder, IDelegateRedirectBuilder
{
protected readonly ICallValidator CallValidator;
private readonly ICallValidator _callValidator;

protected DelegateRedirectBuilder(ICallValidator callValidator, ICallConstraint callConstraint)
: base(callConstraint)
{
CallValidator = callValidator ?? throw new ArgumentNullException(nameof(callValidator));
_callValidator = callValidator;
}

protected DelegateRedirectBuilder(ICallValidator callValidator, ConcurrentBag<ICallConstraint> callConstraints)
: base(callConstraints)
{
_callValidator = callValidator;
}

public IRedirect Build(Delegate redirectDelegate, Action<IRedirectOptionsBuilder>? optionsAction = null)
{
CallValidator.Validate(redirectDelegate);
_callValidator.Validate(redirectDelegate);
var fastDelegate = redirectDelegate.ToDelegate();
var callHandler = new CallHandler(call => fastDelegate.Invoke(call.Args.InternalArgs));

Expand Down
12 changes: 9 additions & 3 deletions src/DivertR/Internal/FuncRedirectBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Linq;
using DivertR.Record;
using DivertR.Record.Internal;
Expand All @@ -13,6 +14,11 @@ public FuncRedirectBuilder(ICallValidator callValidator, ICallConstraint<TTarget
{
}

protected FuncRedirectBuilder(ICallValidator callValidator, ConcurrentBag<ICallConstraint<TTarget>> callConstraints)
: base(callValidator, callConstraints)
{
}

public new IFuncRedirectBuilder<TTarget, TReturn> AddConstraint(ICallConstraint<TTarget> callConstraint)
{
base.AddConstraint(callConstraint);
Expand Down Expand Up @@ -65,7 +71,7 @@ public IRedirect Build<TArgs>(Func<IFuncRedirectCall<TTarget, TReturn, TArgs>, T

public IFuncRedirectBuilder<TTarget, TReturn, TArgs> Args<TArgs>() where TArgs : struct, IStructuralComparable, IStructuralEquatable, IComparable
{
return new FuncRedirectBuilder<TTarget, TReturn, TArgs>(CallValidator, CallConstraint);
return new FuncRedirectBuilder<TTarget, TReturn, TArgs>(CallValidator, CallConstraints);
}
}

Expand All @@ -75,8 +81,8 @@ internal class FuncRedirectBuilder<TTarget, TReturn, TArgs> : FuncRedirectBuilde
{
private readonly IValueTupleMapper _valueTupleMapper;

public FuncRedirectBuilder(ICallValidator callValidator, ICallConstraint<TTarget> callConstraint)
: base(callValidator, callConstraint)
public FuncRedirectBuilder(ICallValidator callValidator, ConcurrentBag<ICallConstraint<TTarget>> callConstraints)
: base(callValidator, callConstraints)
{
_valueTupleMapper = ValueTupleMapperFactory.Create<TArgs>();
CallValidator.Validate(_valueTupleMapper);
Expand Down
33 changes: 24 additions & 9 deletions src/DivertR/Internal/RedirectBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
using System;
using System.Collections.Concurrent;
using DivertR.Record;
using DivertR.Record.Internal;

namespace DivertR.Internal
{
internal class RedirectBuilder<TTarget> : IRedirectBuilder<TTarget> where TTarget : class
{
protected CompositeCallConstraint<TTarget> CallConstraint { get; private set; } = CompositeCallConstraint<TTarget>.Empty;
protected readonly ConcurrentBag<ICallConstraint<TTarget>> CallConstraints;

public RedirectBuilder(ICallConstraint<TTarget>? callConstraint = null)
{
CallConstraints = new ConcurrentBag<ICallConstraint<TTarget>>();

if (callConstraint != null)
{
CallConstraint = CallConstraint.AddCallConstraint(callConstraint);
CallConstraints.Add(callConstraint);
}
}

protected RedirectBuilder(ConcurrentBag<ICallConstraint<TTarget>> callConstraints)
{
CallConstraints = callConstraints;
}

public IRedirectBuilder<TTarget> AddConstraint(ICallConstraint<TTarget> callConstraint)
{
CallConstraint = CallConstraint.AddCallConstraint(callConstraint);
CallConstraints.Add(callConstraint);

return this;
}
Expand Down Expand Up @@ -54,14 +62,14 @@ public IRedirect Build(ICallHandler<TTarget> callHandler, Action<IRedirectOption

var redirectOptions = builder.BuildOptions();
callHandler = builder.BuildCallHandler(callHandler);
var callConstraint = builder.BuildCallConstraint(CallConstraint);
var callConstraint = builder.BuildCallConstraint(CallConstraints);

return new Redirect<TTarget>(callHandler, callConstraint, redirectOptions);
}

public IRedirect Build(ICallHandler<TTarget> callHandler, IRedirectOptions redirectOptions)
{
return new Redirect<TTarget>(callHandler, CallConstraint, redirectOptions);
return new Redirect<TTarget>(callHandler, new CompositeCallConstraint<TTarget>(CallConstraints), redirectOptions);
}

public IRecordRedirect<TTarget> Record(Action<IRedirectOptionsBuilder<TTarget>>? optionsAction = null)
Expand All @@ -75,19 +83,26 @@ public IRecordRedirect<TTarget> Record(Action<IRedirectOptionsBuilder<TTarget>>?

internal class RedirectBuilder : IRedirectBuilder
{
private CompositeCallConstraint CallConstraint { get; set; } = CompositeCallConstraint.Empty;
private readonly ConcurrentBag<ICallConstraint> _callConstraints;

public RedirectBuilder(ICallConstraint? callConstraint = null)
{
_callConstraints = new ConcurrentBag<ICallConstraint>();

if (callConstraint != null)
{
CallConstraint = CallConstraint.AddCallConstraint(callConstraint);
_callConstraints.Add(callConstraint);
}
}

protected RedirectBuilder(ConcurrentBag<ICallConstraint> callConstraints)
{
_callConstraints = callConstraints;
}

public IRedirectBuilder AddConstraint(ICallConstraint callConstraint)
{
CallConstraint = CallConstraint.AddCallConstraint(callConstraint);
_callConstraints.Add(callConstraint);

return this;
}
Expand Down Expand Up @@ -126,7 +141,7 @@ public IRedirect Build(ICallHandler callHandler, Action<IRedirectOptionsBuilder>

public IRedirect Build(ICallHandler callHandler, IRedirectOptions redirectOptions)
{
return new Redirect(callHandler, CallConstraint, redirectOptions);
return new Redirect(callHandler, new CompositeCallConstraint(_callConstraints), redirectOptions);
}

public IRecordRedirect Record(Action<IRedirectOptionsBuilder>? optionsAction = null)
Expand Down
39 changes: 12 additions & 27 deletions src/DivertR/Internal/RedirectOptionsBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

Expand Down Expand Up @@ -44,11 +45,10 @@ internal class RedirectOptionsBuilder<TTarget> : IRedirectOptionsBuilder<TTarget
private int? _orderWeight;
private bool? _disableSatisfyStrict;

private readonly List<Func<ICallHandler<TTarget>, ICallHandler<TTarget>>> _callHandlerDecorators =
new List<Func<ICallHandler<TTarget>, ICallHandler<TTarget>>>();
private readonly ConcurrentStack<Func<ICallHandler<TTarget>, ICallHandler<TTarget>>> _callHandlerDecorators =
new ConcurrentStack<Func<ICallHandler<TTarget>, ICallHandler<TTarget>>>();

private readonly List<Func<ICallConstraint<TTarget>, ICallConstraint<TTarget>>> _callConstraintDecorators =
new List<Func<ICallConstraint<TTarget>, ICallConstraint<TTarget>>>();
private readonly ConcurrentBag<ICallConstraint<TTarget>> _callConstraints = new ConcurrentBag<ICallConstraint<TTarget>>();

public IRedirectOptionsBuilder<TTarget> OrderWeight(int orderWeight)
{
Expand Down Expand Up @@ -76,14 +76,14 @@ public IRedirectOptionsBuilder<TTarget> DisableSatisfyStrict(bool disableStrict

public IRedirectOptionsBuilder<TTarget> DecorateCallHandler(Func<ICallHandler<TTarget>, ICallHandler<TTarget>> decorator)
{
_callHandlerDecorators.Add(decorator);
_callHandlerDecorators.Push(decorator);

return this;
}

public IRedirectOptionsBuilder<TTarget> DecorateCallConstraint(Func<ICallConstraint<TTarget>, ICallConstraint<TTarget>> decorator)
public IRedirectOptionsBuilder<TTarget> AddCallConstraint(ICallConstraint<TTarget> callConstraint)
{
_callConstraintDecorators.Add(decorator);
_callConstraints.Add(callConstraint);

return this;
}
Expand All @@ -100,14 +100,9 @@ public IRedirectOptionsBuilder<TTarget> Skip(int skipCount)

public IRedirectOptionsBuilder<TTarget> AddSwitch(IRedirectSwitch redirectSwitch)
{
ICallConstraint<TTarget> switchConstraint = new SwitchCallConstraint<TTarget>(redirectSwitch);
var switchConstraint = new SwitchCallConstraint<TTarget>(redirectSwitch);

ICallConstraint<TTarget> Decorator(ICallConstraint<TTarget> callConstraint)
{
return CompositeCallConstraint<TTarget>.Empty.AddCallConstraints(new[] { switchConstraint, callConstraint });
}

return DecorateCallConstraint(Decorator);
return AddCallConstraint(switchConstraint);
}

public IRedirectOptions BuildOptions()
Expand All @@ -122,27 +117,17 @@ public ICallHandler<TTarget> BuildCallHandler(ICallHandler<TTarget> callHandler)
return callHandler;
}

foreach (var decorator in _callHandlerDecorators)
foreach (var decorator in _callHandlerDecorators.Reverse())
{
callHandler = decorator.Invoke(callHandler);
}

return callHandler;
}

public ICallConstraint<TTarget> BuildCallConstraint(ICallConstraint<TTarget> callConstraint)
public ICallConstraint<TTarget> BuildCallConstraint(IEnumerable<ICallConstraint<TTarget>> callConstraints)
{
if (!_callConstraintDecorators.Any())
{
return callConstraint;
}

foreach (var decorator in _callConstraintDecorators)
{
callConstraint = decorator.Invoke(callConstraint);
}

return callConstraint;
return new CompositeCallConstraint<TTarget>(callConstraints.Concat(_callConstraints));
}
}
}
Loading

0 comments on commit a521505

Please sign in to comment.