-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCOWList.cs
151 lines (124 loc) · 4.09 KB
/
COWList.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
[DataContract]
public class COWList<T> : IReadOnlyList<T>, IEnumerable<T>, IReadOnlyCollection<T>
{
public class SettingsType
{
public bool DoNotAllowDuplicates { get; init; } = false;
public bool FailedRemovalIsError { get; init; } = false;
}
[DataMember(EmitDefaultValue = false)] private T[] Data = Array.Empty<T>();
public T this[int index] => Data[index];
public int Count => Data.Length;
public bool IsReadOnly => true;
[DataMember(EmitDefaultValue = false)] public SettingsType Settings { get; init; }
public COWList(IEnumerable<T> col)
{
Data = col.ToArray();
}
public COWList(params T[] col)
{
Data = col.ToArray();
}
private COWList() { }
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)Data).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Data.GetEnumerator();
}
public static COWList<T> operator +(COWList<T> self, T other)
{
if (self.Settings?.DoNotAllowDuplicates == true && self.Contains(other)) throw new ArgumentException("This element is already in the list and duplicates are not allowed");
return new COWList<T>
{
Data = self.Data.Append(other).ToArray(),
Settings = self.Settings,
};
}
public static COWList<T> operator +(COWList<T> self, IEnumerable<T> other)
{
if (self.Settings?.DoNotAllowDuplicates == true && self.Data.Intersect(other).Any()) throw new ArgumentException("This element is already in the list and duplicates are not allowed");
return new COWList<T>
{
Data = self.Data.Concat(other).ToArray(),
Settings = self.Settings,
};
}
public static COWList<T> operator -(COWList<T> self, T other)
{
var tmp = self.Data.ToList();
if (tmp.Remove(other))
{
return new COWList<T>
{
Data = tmp.ToArray(),
Settings = self.Settings,
};
}
else
{
if (self.Settings?.FailedRemovalIsError == true) throw new Exception($"Tried to remove {other}, but it was not on the list");
return self;
}
}
public static COWList<T> operator -(COWList<T> self, IEnumerable<T> other)
{
if (self.Settings?.FailedRemovalIsError == true)
{
var foundInList = other.Count(it => self.Contains(it));
if (foundInList != other.Count())
{
throw new Exception($"Tried to remove {other}, but one or more elements were not on the list");
}
}
var tmp = self.Data.ToList();
tmp.RemoveAll(it => other.Contains(it));
return new COWList<T>
{
Data = tmp.ToArray(),
Settings = self.Settings,
};
}
public COWList<T> ReplaceAt(int idx, T obj)
{
var dataCopy = Data.ToArray();
dataCopy[idx] = obj;
return new COWList<T> { Data = dataCopy, Settings = Settings };
}
public COWList<T> WithSettings(SettingsType settings)
{
return new COWList<T> { Data = Data, Settings = settings };
}
public int IndexOf(T obj)
{
for (var i = 0; i < Data.Length; ++i) if (Data[i]?.Equals(obj) == true || (Data[i] == null && obj == null)) return i;
return -1;
}
public COWList<T> Replace(T oldObject, T newObject)
{
var idx = IndexOf(oldObject);
if (idx != -1) return ReplaceAt(idx, newObject);
return this;
}
public static readonly COWList<T> Empty = new();
public static COWList<T> OfDefaults(int count)
{
var ret = new List<T>();
for (var i = 0; i < count; ++i) ret.Add(default);
return new COWList<T> { Data = ret.ToArray() };
}
}
public static class COWListUtil
{
public static COWList<T> ToCOWList<T>(this IEnumerable<T> col)
{
return new COWList<T>(col);
}
}