Merge pull request #2338 from BogdanovKirill/httpwritefix3
[mono.git] / mcs / class / corlib / System / MulticastDelegate.cs
1 //
2 // System.MultiCastDelegate.cs
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Daniel Stodden (stodden@in.tum.de)
7 //
8 // (C) Ximian, Inc.  http://www.ximian.com
9 //
10
11 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Reflection;
37 using System.Runtime.Serialization;
38 using System.Runtime.InteropServices;
39
40 namespace System
41 {
42         [System.Runtime.InteropServices.ComVisible (true)]
43         [Serializable]
44         [StructLayout (LayoutKind.Sequential)]
45         public abstract class MulticastDelegate : Delegate
46         {
47                 Delegate[] delegates;
48
49                 protected MulticastDelegate (object target, string method)
50                         : base (target, method)
51                 {
52                 }
53
54                 protected MulticastDelegate (Type target, string method)
55                         : base (target, method)
56                 {
57                 }
58                 
59                 public override void GetObjectData (SerializationInfo info, StreamingContext context)
60                 {
61                         base.GetObjectData  (info, context);
62                 }
63
64                 protected sealed override object DynamicInvokeImpl (object[] args)
65                 {
66                         if (delegates == null) {
67                                 return base.DynamicInvokeImpl (args);
68                         } else {
69                                 object r;
70                                 int i = 0, len = delegates.Length;
71                                 do {
72                                         r = delegates [i].DynamicInvoke (args);
73                                 } while (++i < len);
74                                 return r;
75                         }
76                 }
77
78                 // Some high-performance applications use this internal property
79                 // to avoid using a slow path to determine if there is more than one handler
80                 // This brings an API that we removed in f410e545e2db0e0dc338673a6b10a5cfd2d3340f
81                 // which some users depeneded on
82                 //
83                 // This is an example of code that used this:
84                 // https://gist.github.com/migueldeicaza/cd99938c2a4372e7e5d5
85                 //
86                 // Do not remove this API
87                 internal bool HasSingleTarget {
88                         get { return delegates == null; }
89                 }
90
91                 // <remarks>
92                 //   Equals: two multicast delegates are equal if their base is equal
93                 //   and their invocations list is equal.
94                 // </remarks>
95                 public sealed override bool Equals (object obj)
96                 {
97                         if (!base.Equals (obj))
98                                 return false;
99
100                         MulticastDelegate d = obj as MulticastDelegate;
101                         if (d == null)
102                                 return false;
103
104                         if (delegates == null && d.delegates == null) {
105                                 return true;
106                         } else if (delegates == null ^ d.delegates == null) {
107                                 return false;
108                         } else {
109                                 if (delegates.Length != d.delegates.Length)
110                                         return false;
111
112                                 for (int i = 0; i < delegates.Length; ++i) {
113                                         if (!delegates [i].Equals (d.delegates [i]))
114                                                 return false;
115                                 }
116
117                                 return true;
118                         }
119                 }
120
121                 //
122                 // FIXME: This could use some improvements.
123                 //
124                 public sealed override int GetHashCode ()
125                 {
126                         return base.GetHashCode ();
127                 }
128
129                 protected override MethodInfo GetMethodImpl ()
130                 {
131                         if (delegates != null)
132                                 return delegates [delegates.Length - 1].Method;
133
134                         return base.GetMethodImpl ();
135                 }
136
137                 // <summary>
138                 //   Return, in order of invocation, the invocation list
139                 //   of a MulticastDelegate
140                 // </summary>
141                 public sealed override Delegate[] GetInvocationList ()
142                 {
143                         if (delegates != null)
144                                 return (Delegate[]) delegates.Clone ();
145                         else
146                                 return new Delegate[1] { this };
147                 }
148
149                 // <summary>
150                 //   Combines this MulticastDelegate with the (Multicast)Delegate `follow'.
151                 //   This does _not_ combine with Delegates. ECMA states the whole delegate
152                 //   thing should have better been a simple System.Delegate class.
153                 //   Compiler generated delegates are always MulticastDelegates.
154                 // </summary>
155                 protected sealed override Delegate CombineImpl (Delegate follow)
156                 {
157                         if (follow == null)
158                                 return this;
159
160                         MulticastDelegate other = (MulticastDelegate) follow;
161
162                         MulticastDelegate ret = AllocDelegateLike_internal (this);
163
164                         if (delegates == null && other.delegates == null) {
165                                 ret.delegates = new Delegate [2] { this, other };
166                         } else if (delegates == null) {
167                                 ret.delegates = new Delegate [1 + other.delegates.Length];
168
169                                 ret.delegates [0] = this;
170                                 Array.Copy (other.delegates, 0, ret.delegates, 1, other.delegates.Length);
171                         } else if (other.delegates == null) {
172                                 ret.delegates = new Delegate [delegates.Length + 1];
173
174                                 Array.Copy (delegates, 0, ret.delegates, 0, delegates.Length);
175                                 ret.delegates [ret.delegates.Length - 1] = other;
176                         } else {
177                                 ret.delegates = new Delegate [delegates.Length + other.delegates.Length];
178
179                                 Array.Copy (delegates, 0, ret.delegates, 0, delegates.Length);
180                                 Array.Copy (other.delegates, 0, ret.delegates, delegates.Length, other.delegates.Length);
181                         }
182
183                         return ret;
184                 }
185
186                 /* Based on the Boyer–Moore string search algorithm */
187                 int LastIndexOf (Delegate[] haystack, Delegate[] needle)
188                 {
189                         if (haystack.Length < needle.Length)
190                                 return -1;
191
192                         if (haystack.Length == needle.Length) {
193                                 for (int i = 0; i < haystack.Length; ++i)
194                                         if (!haystack [i].Equals (needle [i]))
195                                                 return -1;
196
197                                 return 0;
198                         }
199
200                         for (int i = haystack.Length - needle.Length, j; i >= 0;) {
201                                 for (j = 0; needle [j].Equals (haystack [i]); ++i, ++j) {
202                                         if (j == needle.Length - 1)
203                                                 return i - j;
204                                 }
205
206                                 i -= j + 1;
207                         }
208
209                         return -1;
210                 }
211
212                 protected sealed override Delegate RemoveImpl (Delegate value)
213                 {
214                         if (value == null)
215                                 return this;
216
217                         MulticastDelegate other = (MulticastDelegate) value;
218
219                         if (delegates == null && other.delegates == null) {
220                                 /* if they are not equal and the current one is not
221                                  * a multicastdelegate then we cannot delete it */
222                                 return this.Equals (other) ? null : this;
223                         } else if (delegates == null) {
224                                 foreach (var d in other.delegates) {
225                                         if (this.Equals (d))
226                                                 return null;
227                                 }
228                                 return this;
229                         } else if (other.delegates == null) {
230                                 int idx = Array.LastIndexOf (delegates, other);
231                                 if (idx == -1)
232                                         return this;
233
234                                 if (delegates.Length <= 1) {
235                                         /* delegates.Length should never be equal or
236                                          * lower than 1, it should be 2 or greater */
237                                         throw new InvalidOperationException ();
238                                 }
239
240                                 if (delegates.Length == 2)
241                                         return delegates [idx == 0 ? 1 : 0];
242
243                                 MulticastDelegate ret = AllocDelegateLike_internal (this);
244                                 ret.delegates = new Delegate [delegates.Length - 1];
245
246                                 Array.Copy (delegates, ret.delegates, idx);
247                                 Array.Copy (delegates, idx + 1, ret.delegates, idx, delegates.Length - idx - 1);
248
249                                 return ret;
250                         } else {
251                                 /* wild case : remove MulticastDelegate from MulticastDelegate
252                                  * complexity is O(m + n), with n the number of elements in
253                                  * this.delegates and m the number of elements in other.delegates */
254
255                                 if (delegates.Equals (other.delegates))
256                                         return null;
257
258                                 /* we need to remove elements from the end to the beginning, as
259                                  * the addition and removal of delegates behaves like a stack */
260                                 int idx = LastIndexOf (delegates, other.delegates);
261                                 if (idx == -1)
262                                         return this;
263
264                                 MulticastDelegate ret = AllocDelegateLike_internal (this);
265                                 ret.delegates = new Delegate [delegates.Length - other.delegates.Length];
266
267                                 Array.Copy (delegates, ret.delegates, idx);
268                                 Array.Copy (delegates, idx + other.delegates.Length, ret.delegates, idx, delegates.Length - idx - other.delegates.Length);
269
270                                 return ret;
271                         }
272                 }
273
274                 public static bool operator == (MulticastDelegate d1, MulticastDelegate d2)
275                 {
276                         if (d1 == null)
277                                 return d2 == null;
278
279                         return d1.Equals (d2);
280                 }
281                 
282                 public static bool operator != (MulticastDelegate d1, MulticastDelegate d2)
283                 {
284                         if (d1 == null)
285                                 return d2 != null;
286
287                         return !d1.Equals (d2);
288                 }
289         }
290 }