Skip to content

Commit a4fff45

Browse files
authored
Merge pull request #40 from servicetitan/allocation-free-scopes
Allocation free scopes
2 parents f3a449a + a07cd60 commit a4fff45

File tree

4 files changed

+127
-77
lines changed

4 files changed

+127
-77
lines changed
Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2009-2020 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Alexis Kochetov
55
// Created: 2009.03.12
66

77
using System;
88
using System.Collections;
99
using System.Collections.Generic;
1010
using System.Diagnostics;
11-
using Xtensive.Core;
1211

1312

1413
namespace Xtensive.Collections
@@ -21,8 +20,50 @@ namespace Xtensive.Collections
2120
/// </summary>
2221
[Serializable]
2322
[DebuggerDisplay("Count = {Count}")]
24-
public class BindingCollection<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
23+
public class BindingCollection<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey, TValue>>
2524
{
25+
public readonly ref struct BindingScope
26+
{
27+
public static BindingScope Empty => new BindingScope();
28+
29+
private readonly BindingCollection<TKey, TValue> owner;
30+
private readonly TKey key;
31+
private readonly TValue prevValue;
32+
private readonly bool prevValueExists;
33+
34+
public void Dispose()
35+
{
36+
if (owner == null) {
37+
return;
38+
}
39+
40+
if (prevValueExists) {
41+
if (!owner.permanentBindings.Contains(key)) {
42+
owner.bindings[key] = prevValue;
43+
}
44+
}
45+
else {
46+
if (!owner.permanentBindings.Contains(key)) {
47+
owner.bindings.Remove(key);
48+
}
49+
}
50+
}
51+
52+
public BindingScope(BindingCollection<TKey, TValue> owner, TKey key) : this()
53+
{
54+
this.owner = owner;
55+
this.key = key;
56+
}
57+
58+
public BindingScope(BindingCollection<TKey, TValue> owner, TKey key, TValue prevValue)
59+
{
60+
this.owner = owner;
61+
this.key = key;
62+
this.prevValue = prevValue;
63+
prevValueExists = true;
64+
}
65+
}
66+
2667
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
2768
private readonly Dictionary<TKey, TValue> bindings = new Dictionary<TKey, TValue>();
2869
private readonly HashSet<TKey> permanentBindings = new HashSet<TKey>();
@@ -32,7 +73,7 @@ public class BindingCollection<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TV
3273
/// </summary>
3374
public virtual int Count {
3475
[DebuggerStepThrough]
35-
get { return bindings.Count; }
76+
get => bindings.Count;
3677
}
3778

3879
/// <summary>
@@ -41,7 +82,7 @@ public virtual int Count {
4182
/// <value></value>
4283
public virtual TValue this[TKey key] {
4384
[DebuggerStepThrough]
44-
get { return bindings[key]; }
85+
get => bindings[key];
4586
}
4687

4788
/// <summary>
@@ -51,24 +92,15 @@ public virtual TValue this[TKey key] {
5192
/// <param name="value">The value to bind.</param>
5293
/// <returns>Disposable object that will
5394
/// destroy the binding on its disposal.</returns>
54-
public virtual IDisposable Add(TKey key, TValue value)
95+
public virtual BindingScope Add(TKey key, TValue value)
5596
{
56-
TValue previous;
57-
58-
if (bindings.TryGetValue(key, out previous)) {
97+
if (bindings.TryGetValue(key, out var previous)) {
5998
bindings[key] = value;
60-
return new Disposable(isDisposing => {
61-
if (!permanentBindings.Contains(key))
62-
bindings[key] = previous;
63-
});
64-
}
65-
else {
66-
bindings.Add(key, value);
67-
return new Disposable(isDisposing => {
68-
if (!permanentBindings.Contains(key))
69-
bindings.Remove(key);
70-
});
99+
return new BindingScope(this, key, previous);
71100
}
101+
102+
bindings.Add(key, value);
103+
return new BindingScope(this, key);
72104
}
73105

74106
/// <summary>
@@ -80,8 +112,9 @@ public virtual IDisposable Add(TKey key, TValue value)
80112
public virtual void PermanentAdd(TKey key, TValue value)
81113
{
82114
bindings[key] = value;
83-
if (!permanentBindings.Contains(key))
115+
if (!permanentBindings.Contains(key)) {
84116
permanentBindings.Add(key);
117+
}
85118
}
86119

87120
/// <summary>
@@ -92,8 +125,10 @@ public virtual void PermanentAdd(TKey key, TValue value)
92125
/// <exception cref="KeyNotFoundException">Key isn't found.</exception>
93126
public virtual void ReplaceBound(TKey key, TValue value)
94127
{
95-
if (!bindings.ContainsKey(key))
128+
if (!bindings.ContainsKey(key)) {
96129
throw new KeyNotFoundException();
130+
}
131+
97132
bindings[key] = value;
98133
}
99134

@@ -108,45 +143,27 @@ public virtual void ReplaceBound(TKey key, TValue value)
108143
/// contains an element with the specified key;
109144
/// otherwise, <see langword="false" />.</returns>
110145
[DebuggerStepThrough]
111-
public virtual bool TryGetValue(TKey key, out TValue value)
112-
{
113-
return bindings.TryGetValue(key, out value);
114-
}
146+
public virtual bool TryGetValue(TKey key, out TValue value) => bindings.TryGetValue(key, out value);
115147

116148
/// <summary>
117149
/// Gets the sequence of bound keys.
118150
/// </summary>
119151
/// <returns>The sequence of bound keys.</returns>
120152
public virtual IEnumerable GetKeys()
121153
{
122-
foreach (var key in bindings.Keys)
154+
foreach (var key in bindings.Keys) {
123155
yield return key;
156+
}
124157
}
125158

126159
#region IEnumerable<...> methods
127160

128161
/// <inheritdoc/>
129-
public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
130-
{
131-
return bindings.GetEnumerator();
132-
}
162+
public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => bindings.GetEnumerator();
133163

134164
/// <inheritdoc/>
135-
IEnumerator IEnumerable.GetEnumerator()
136-
{
137-
return GetEnumerator();
138-
}
165+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
139166

140167
#endregion
141-
142-
143-
// Constructors
144-
145-
/// <summary>
146-
/// Initializes new instance of this type.
147-
/// </summary>
148-
public BindingCollection()
149-
{
150-
}
151168
}
152169
}
Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,67 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2009-2020 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Gamzov
55
// Created: 2009.06.30
66

77
using System;
88
using System.Collections.Generic;
99
using System.Linq.Expressions;
1010
using Xtensive.Collections;
11-
using Xtensive.Core;
1211
using Xtensive.Orm.Linq.Expressions;
1312

1413
namespace Xtensive.Orm.Linq
1514
{
1615
[Serializable]
1716
internal class LinqBindingCollection : BindingCollection<ParameterExpression, ProjectionExpression>
1817
{
18+
internal readonly ref struct ParameterScope
19+
{
20+
private readonly LinqBindingCollection owner;
21+
private readonly IReadOnlyCollection<ParameterExpression> parameters;
22+
23+
public void Dispose()
24+
{
25+
var linkedParameters = owner.linkedParameters;
26+
foreach (var parameter in parameters) {
27+
linkedParameters.Remove(parameter);
28+
}
29+
}
30+
31+
public ParameterScope(LinqBindingCollection owner, IReadOnlyCollection<ParameterExpression> parameters)
32+
{
33+
this.owner = owner;
34+
this.parameters = parameters;
35+
}
36+
}
37+
1938
private readonly Dictionary<ParameterExpression, IEnumerable<ParameterExpression>> linkedParameters
2039
= new Dictionary<ParameterExpression, IEnumerable<ParameterExpression>>();
2140

22-
public override IDisposable Add(ParameterExpression key, ProjectionExpression value)
41+
public override BindingScope Add(ParameterExpression key, ProjectionExpression value)
2342
{
24-
if (!key.Type.IsAssignableFrom(value.ItemProjector.Type))
25-
throw new ArgumentException(Strings.ExParameterExpressionMustHaveSameTypeAsProjectionExpressionItemProjector, "key");
43+
if (!key.Type.IsAssignableFrom(value.ItemProjector.Type)) {
44+
throw new ArgumentException(
45+
Strings.ExParameterExpressionMustHaveSameTypeAsProjectionExpressionItemProjector, nameof(key));
46+
}
47+
2648
return base.Add(key, value);
2749
}
2850

2951
public override void PermanentAdd(ParameterExpression key, ProjectionExpression value)
3052
{
31-
if (!key.Type.IsAssignableFrom(value.ItemProjector.Type))
32-
throw new ArgumentException(Strings.ExParameterExpressionMustHaveSameTypeAsProjectionExpressionItemProjector, "key");
53+
if (!key.Type.IsAssignableFrom(value.ItemProjector.Type)) {
54+
throw new ArgumentException(
55+
Strings.ExParameterExpressionMustHaveSameTypeAsProjectionExpressionItemProjector, nameof(key));
56+
}
57+
3358
base.PermanentAdd(key, value);
3459
}
3560

3661
public override void ReplaceBound(ParameterExpression key, ProjectionExpression value)
3762
{
3863
base.ReplaceBound(key, value);
39-
IEnumerable<ParameterExpression> parameters;
40-
if (linkedParameters.TryGetValue(key, out parameters)) {
64+
if (linkedParameters.TryGetValue(key, out var parameters)) {
4165
foreach (var parameter in parameters) {
4266
if (parameter!=key) {
4367
var projection = this[parameter];
@@ -52,16 +76,14 @@ public override void ReplaceBound(ParameterExpression key, ProjectionExpression
5276
}
5377
}
5478
}
55-
56-
public Disposable LinkParameters(IEnumerable<ParameterExpression> parameters)
79+
80+
public ParameterScope LinkParameters(IReadOnlyCollection<ParameterExpression> parameters)
5781
{
58-
foreach (var parameter in parameters)
82+
foreach (var parameter in parameters) {
5983
linkedParameters.Add(parameter, parameters);
60-
return new Disposable(isDisposing => {
61-
foreach (var parameter in parameters) {
62-
linkedParameters.Remove(parameter);
63-
}
64-
});
84+
}
85+
86+
return new ParameterScope(this, parameters);
6587
}
6688
}
6789
}

Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ private ProjectionExpression VisitSelectMany(Expression source, LambdaExpression
10751075
var visitedSource = Visit(source);
10761076
var sequence = VisitSequence(visitedSource);
10771077

1078-
IDisposable indexBinding = null;
1078+
var indexBinding = BindingCollection<ParameterExpression, ProjectionExpression>.BindingScope.Empty;
10791079
if (collectionSelector.Parameters.Count==2) {
10801080
var indexProjection = GetIndexBinding(collectionSelector, ref sequence);
10811081
indexBinding = context.Bindings.Add(collectionSelector.Parameters[1], indexProjection);
@@ -1194,7 +1194,7 @@ private ProjectionExpression VisitWhere(Expression expression, LambdaExpression
11941194
{
11951195
var parameter = le.Parameters[0];
11961196
ProjectionExpression visitedSource = VisitSequence(expression);
1197-
IDisposable indexBinding = null;
1197+
var indexBinding = BindingCollection<ParameterExpression, ProjectionExpression>.BindingScope.Empty;
11981198
if (le.Parameters.Count==2) {
11991199
var indexProjection = GetIndexBinding(le, ref visitedSource);
12001200
indexBinding = context.Bindings.Add(le.Parameters[1], indexProjection);

Orm/Xtensive.Orm/Orm/Rse/Transformation/Internals/SkipTakeRewriterState.cs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
1-
// Copyright (C) 2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2010-2020 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Gamzov
55
// Created: 2010.01.21
66

77
using System;
8-
using Xtensive.Core;
9-
108

119
namespace Xtensive.Orm.Rse.Transformation
1210
{
1311
internal sealed class SkipTakeRewriterState
1412
{
13+
internal readonly ref struct SkipTakeRewriterScope
14+
{
15+
private readonly SkipTakeRewriter rewriter;
16+
private readonly SkipTakeRewriterState prevState;
17+
18+
public void Dispose() => rewriter.State = prevState;
19+
20+
public SkipTakeRewriterScope(SkipTakeRewriter rewriter, SkipTakeRewriterState prevState)
21+
{
22+
this.rewriter = rewriter;
23+
this.prevState = prevState;
24+
}
25+
}
26+
1527
private readonly SkipTakeRewriter rewriter;
1628

1729
public Func<int> Skip { get; private set; }
@@ -56,12 +68,11 @@ private static Func<int> PositiveSelector(Func<int> valueSelector)
5668
};
5769
}
5870

59-
public IDisposable CreateScope()
71+
public SkipTakeRewriterScope CreateScope()
6072
{
6173
var currentState = rewriter.State;
62-
var newState = new SkipTakeRewriterState(currentState);
63-
rewriter.State = newState;
64-
return new Disposable(_ => rewriter.State = currentState);
74+
rewriter.State = new SkipTakeRewriterState(currentState);
75+
return new SkipTakeRewriterScope(rewriter, currentState);
6576
}
6677

6778

0 commit comments

Comments
 (0)