[w32file] Move MonoIO.Find{First,Next,Close} to managed
[mono.git] / mcs / mcs / support.cs
1 //
2 // support.cs: Support routines to work around the fact that System.Reflection.Emit
3 // can not introspect types that are being constructed
4 //
5 // Author:
6 //   Miguel de Icaza (miguel@ximian.com)
7 //   Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright 2001 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2003-2009 Novell, Inc
11 // Copyright 2011 Xamarin Inc
12 //
13
14 using System;
15 using System.IO;
16 using System.Text;
17 using System.Collections.Generic;
18
19 namespace Mono.CSharp {
20
21         sealed class ReferenceEquality<T> : IEqualityComparer<T> where T : class
22         {
23                 public static readonly IEqualityComparer<T> Default = new ReferenceEquality<T> ();
24
25                 private ReferenceEquality ()
26                 {
27                 }
28
29                 public bool Equals (T x, T y)
30                 {
31                         return ReferenceEquals (x, y);
32                 }
33
34                 public int GetHashCode (T obj)
35                 {
36                         return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode (obj);
37                 }
38         }
39
40         static class ArrayComparer
41         {
42                 public static bool IsEqual<T> (T[] array1, T[] array2)
43                 {
44                         if (array1 == null || array2 == null)
45                                 return array1 == array2;
46
47                         var eq = EqualityComparer<T>.Default;
48
49                         for (int i = 0; i < array1.Length; ++i) {
50                                 if (!eq.Equals (array1[i], array2[i])) {
51                                         return false;
52                                 }
53                         }
54
55                         return true;
56                 }
57         }
58
59         /// <summary>
60         ///   This is an arbitrarily seekable StreamReader wrapper.
61         ///
62         ///   It uses a self-tuning buffer to cache the seekable data,
63         ///   but if the seek is too far, it may read the underly
64         ///   stream all over from the beginning.
65         /// </summary>
66         public class SeekableStreamReader : IDisposable
67         {
68                 public const int DefaultReadAheadSize =
69 #if FULL_AST
70                         65536 / 2; // Large buffer because of ReadChars of large literal string
71 #else
72                         4096 / 2;
73 #endif
74
75                 StreamReader reader;
76                 public readonly Stream Stream;
77
78                 char[] buffer;
79                 int read_ahead_length;  // the length of read buffer
80                 int buffer_start;       // in chars
81                 int char_count;         // count of filled characters in buffer[]
82                 int pos;                // index into buffer[]
83
84                 public SeekableStreamReader (Stream stream, Encoding encoding, char[] sharedBuffer = null)
85                 {
86                         this.Stream = stream;
87                         this.buffer = sharedBuffer;
88
89                         InitializeStream (DefaultReadAheadSize);
90                         reader = new StreamReader (stream, encoding, true);
91                 }
92
93                 public void Dispose ()
94                 {
95                         // Needed to release stream reader buffers
96                         reader.Dispose ();
97                 }
98
99                 void InitializeStream (int read_length_inc)
100                 {
101                         read_ahead_length += read_length_inc;
102
103                         int required_buffer_size = read_ahead_length * 2;
104
105                         if (buffer == null || buffer.Length < required_buffer_size)
106                                 buffer = new char [required_buffer_size];
107
108                         Stream.Position = 0;
109                         buffer_start = char_count = pos = 0;
110                 }
111
112                 /// <remarks>
113                 ///   This value corresponds to the current position in a stream of characters.
114                 ///   The StreamReader hides its manipulation of the underlying byte stream and all
115                 ///   character set/decoding issues.  Thus, we cannot use this position to guess at
116                 ///   the corresponding position in the underlying byte stream even though there is
117                 ///   a correlation between them.
118                 /// </remarks>
119                 public int Position {
120                         get {
121                                 return buffer_start + pos;
122                         }
123
124                         set {
125                                 //
126                                 // If the lookahead was too small, re-read from the beginning. Increase the buffer size while we're at it
127                                 // This should never happen until we are parsing some weird source code
128                                 //
129                                 if (value < buffer_start) {
130                                         InitializeStream (read_ahead_length);
131
132                                         //
133                                         // Discard buffer data after underlying stream changed position
134                                         // Cannot use handy reader.DiscardBufferedData () because it for
135                                         // some strange reason resets encoding as well
136                                         //
137                                         reader = new StreamReader (Stream, reader.CurrentEncoding, true);
138                                 }
139
140                                 while (value > buffer_start + char_count) {
141                                         pos = char_count;
142                                         if (!ReadBuffer ())
143                                                 throw new InternalErrorException ("Seek beyond end of file: " + (buffer_start + char_count - value));
144                                 }
145
146                                 pos = value - buffer_start;
147                         }
148                 }
149
150                 bool ReadBuffer ()
151                 {
152                         int slack = buffer.Length - char_count;
153
154                         //
155                         // read_ahead_length is only half of the buffer to deal with
156                         // reads ahead and moves back without re-reading whole buffer
157                         //
158                         if (slack <= read_ahead_length) {
159                                 //
160                                 // shift the buffer to make room for read_ahead_length number of characters
161                                 //
162                                 int shift = read_ahead_length - slack;
163                                 Array.Copy (buffer, shift, buffer, 0, char_count - shift);
164
165                                 // Update all counters
166                                 pos -= shift;
167                                 char_count -= shift;
168                                 buffer_start += shift;
169                                 slack += shift;
170                         }
171
172                         char_count += reader.Read (buffer, char_count, slack);
173
174                         return pos < char_count;
175                 }
176
177                 public char[] ReadChars (int fromPosition, int toPosition)
178                 {
179                         char[] chars = new char[toPosition - fromPosition];
180                         if (buffer_start <= fromPosition && toPosition <= buffer_start + buffer.Length) {
181                                 Array.Copy (buffer, fromPosition - buffer_start, chars, 0, chars.Length);
182                         } else {
183                                 throw new NotImplementedException ();
184                         }
185
186                         return chars;
187                 }
188
189                 public int Peek ()
190                 {
191                         if ((pos >= char_count) && !ReadBuffer ())
192                                 return -1;
193
194                         return buffer [pos];
195                 }
196
197                 public int Read ()
198                 {
199                         if ((pos >= char_count) && !ReadBuffer ())
200                                 return -1;
201
202                         return buffer [pos++];
203                 }
204         }
205
206         public class UnixUtils {
207                 [System.Runtime.InteropServices.DllImport ("libc", EntryPoint="isatty")]
208                 extern static int _isatty (int fd);
209                         
210                 public static bool isatty (int fd)
211                 {
212                         try {
213                                 return _isatty (fd) == 1;
214                         } catch {
215                                 return false;
216                         }
217                 }
218         }
219
220         /// <summary>
221         ///   An exception used to terminate the compiler resolution phase and provide completions
222         /// </summary>
223         /// <remarks>
224         ///   This is thrown when we want to return the completions or
225         ///   terminate the completion process by AST nodes used in
226         ///   the completion process.
227         /// </remarks>
228         public class CompletionResult : Exception {
229                 string [] result;
230                 string base_text;
231                 
232                 public CompletionResult (string base_text, string [] res)
233                 {
234                         if (base_text == null)
235                                 throw new ArgumentNullException ("base_text");
236                         this.base_text = base_text;
237
238                         result = res;
239                         Array.Sort (result);
240                 }
241
242                 public string [] Result {
243                         get {
244                                 return result;
245                         }
246                 }
247
248                 public string BaseText {
249                         get {
250                                 return base_text;
251                         }
252                 }
253         }
254
255         struct TypeNameParser
256         {
257                 internal static string Escape(string name)
258                 {
259                         if (name == null) {
260                                 return null;
261                         }
262                         StringBuilder sb = null;
263                         for (int pos = 0; pos < name.Length; pos++) {
264                                 char c = name[pos];
265                                 switch (c) {
266                                         case '\\':
267                                         case '+':
268                                         case ',':
269                                         case '[':
270                                         case ']':
271                                         case '*':
272                                         case '&':
273                                                 if (sb == null) {
274                                                         sb = new StringBuilder(name, 0, pos, name.Length + 3);
275                                                 }
276                                                 sb.Append("\\").Append(c);
277                                                 break;
278                                         default:
279                                                 if (sb != null) {
280                                                         sb.Append(c);
281                                                 }
282                                                 break;
283                                 }
284                         }
285                         return sb != null ? sb.ToString() : name;
286                 }
287         }
288 }