New tests.
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / BufferManager.cs
1 //
2 // BufferManager.cs:
3 //    This class suffers from an engineering problem in its
4 //    design: when this API is used to limit the total pool
5 //    size it will throw, but no user code is designed to
6 //    cope with that.
7 //
8 //    Instead of the Microsoft strategy, we allow allocation
9 //    to go as far as it wants to go and merely allow this
10 //    to be a pool that can be used recycle buffers.
11 //
12 //    This still gives us the main benefit of this class, while
13 //    avoiding the potential crashing scenarios and simplifies
14 //    the implementation significantly from what has been
15 //    document in the blogosphere.
16 //
17 //    There are a few problems: for example, if we do not find
18 //    a buffer of the proper size in the expected slot, say
19 //    a 31k buffer in the slot for [32k-64k] values, we will
20 //    allocate a new buffer, even if there might have been a
21 //    buffer for 128k.
22 //
23 // A few considerations:
24 //
25 //    The size of an empty array is either 16 on 32 bit systems
26 //    and 32 bytes in 64 bit systems.
27 //
28 //    We take this information into account for the minimum allocation
29 //    pools.
30 //
31 // Authors:
32 //   Atsushi Enomoto (atsushi@ximian.com)
33 //   Miguel de Icaza (miguel@gnome.org)
34 //
35 // Copyright (C) 2005, 2010 Novell, Inc (http://www.novell.com)
36 //
37 // Permission is hereby granted, free of charge, to any person obtaining
38 // a copy of this software and associated documentation files (the
39 // "Software"), to deal in the Software without restriction, including
40 // without limitation the rights to use, copy, modify, merge, publish,
41 // distribute, sublicense, and/or sell copies of the Software, and to
42 // permit persons to whom the Software is furnished to do so, subject to
43 // the following conditions:
44 // 
45 // The above copyright notice and this permission notice shall be
46 // included in all copies or substantial portions of the Software.
47 // 
48 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
49 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
50 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
51 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
52 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
53 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
54 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
55 //
56 using System;
57 using System.Collections.Generic;
58 using System.IO;
59 using System.Collections.ObjectModel;
60 using System.ServiceModel;
61
62 namespace System.ServiceModel.Channels
63 {
64         public abstract class BufferManager
65         {
66                 protected BufferManager ()
67                 {
68                 }
69
70                 public abstract void Clear ();
71
72                 public static BufferManager CreateBufferManager (
73                         long maxBufferPoolSize, int maxBufferSize)
74                 {
75                         return new DefaultBufferManager (maxBufferPoolSize, maxBufferSize);
76                 }
77
78                 public abstract void ReturnBuffer (byte[] buffer);
79
80                 public abstract byte[] TakeBuffer (int bufferSize);
81
82 #if DEBUG_BUFFER
83                 internal abstract void DumpStats ();
84 #endif
85                 
86                 class DefaultBufferManager : BufferManager
87                 {
88                         const int log_min = 5;   // Anything smaller than 1 << log_cut goes into the first bucket
89                         long max_pool_size;
90                         int max_size;
91                         List<byte []> [] buffers = new List<byte []> [32-log_min];
92
93 #if DEBUG_BUFFER
94                         internal override void DumpStats ()
95                         {
96                                 Console.WriteLine ("- hit={0} miss={1}-", hits, miss);
97                                 for (int i = 0; i < buffers.Length; i++){
98                                         if (buffers [i] == null)
99                                                 continue;
100                                         
101                                         Console.Write ("Slot {0} - {1} [", i, buffers [i].Count);
102                                         byte [][] arr = buffers [i].ToArray ();
103                                         
104                                         for (int j = 0; j < Math.Min (3, arr.Length); j++)
105                                                 Console.Write ("{0} ", arr [j].Length);
106                                         Console.WriteLine ("]");
107                                 }
108                         }
109 #endif
110                         
111                         static int log2 (uint n)
112                         {
113                                 int pos = 0;
114                                 if (n >= 1<<16) {
115                                         n >>= 16;
116                                         pos += 16;
117                                 }
118                                 if (n >= 1<< 8) {
119                                         n >>=  8;
120                                         pos +=  8;
121                                 }
122                                 if (n >= 1<< 4) {
123                                         n >>=  4;
124                                         pos +=  4;
125                                 }
126                                 if (n >= 1<< 2) {
127                                         n >>=  2;
128                                         pos +=  2;
129                                 }
130                                 if (n >= 1<< 1) 
131                                         pos +=  1;
132
133                                 return ((n == 0) ? (-1) : pos);
134                         }
135                         
136                         public DefaultBufferManager (long maxBufferPoolSize, int maxBufferSize)
137                         {
138                                 this.max_pool_size = maxBufferPoolSize;
139                                 this.max_size = maxBufferSize;
140                         }
141
142                         public override void Clear ()
143                         {
144                                 foreach (var stack in buffers){
145                                         if (stack == null)
146                                                 continue;
147                                         stack.Clear ();
148                                 }
149                                 Array.Clear (buffers, 0, buffers.Length);
150                         }
151
152                         public override void ReturnBuffer (byte [] buffer)
153                         {
154                                 if (buffer == null)
155                                         return;
156
157                                 uint size = (uint) buffer.Length;
158                                 int l2 = log2 (size);
159                                 if (l2 > log_min)
160                                         l2 -= log_min;
161
162                                 List<byte []> returned = buffers [l2];
163                                 if (returned == null)
164                                         returned = buffers [l2] = new List<byte []> ();
165
166                                 returned.Add (buffer);
167                         }
168
169                         int hits, miss;
170                         
171                         public override byte [] TakeBuffer (int bufferSize)
172                         {
173                                 if (bufferSize < 0 || (max_size >= 0 && bufferSize > max_size))
174                                         throw new ArgumentOutOfRangeException ();
175
176                                 int l2 = log2 ((uint) bufferSize);
177                                 if (l2 > log_min)
178                                         l2 -= log_min;
179
180                                 List<byte []> returned = buffers [l2];
181                                 if (returned == null || returned.Count == 0)
182                                         return new byte [bufferSize];
183                                 
184                                 foreach (var e in returned){
185                                         if (e.Length >= bufferSize){
186                                                 hits++;
187                                                 returned.Remove (e);
188                                                 return e;
189                                         }
190                                 }
191                                 return new byte [bufferSize];
192                         }
193                 }
194         }
195
196 #if DEBUG_BUFFER
197         class Foo {
198                 static void Main ()
199                 {
200                         var a = BufferManager.CreateBufferManager (1024*1024, 1024*1024);
201                         var rand = new Random (0);
202                         
203                         var buffs = new List<byte []> ();
204                         for (int i = 0; i < 4096; i++){
205                                 a.DumpStats ();
206                                 var request = rand.Next (1,1024*1024);
207                                 if ((i % 2) == 0)
208                                         request = rand.Next (1024, 4096);
209                                 
210                                 var x = a.TakeBuffer (request);
211                                 if (x.Length < request)
212                                         throw new Exception ();
213                                 Console.WriteLine ("Delta={2} Requested {0} got={1} bytes ", request, x.Length, x.Length-request);
214                                 if ((i % 3) == 0){
215                                         Console.WriteLine ("Return: {0}", x.Length);
216                                         a.ReturnBuffer (x);
217                                 }
218                                 else
219                                         buffs.Add (x);
220                         }
221                         a.DumpStats ();
222                 }
223         }
224 #endif
225 }