2 // Mono.Unix/UnixMarshal.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004-2005 Jonathan Pryor
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Net.Sockets;
32 using System.Runtime.InteropServices;
33 using System.Runtime.Serialization;
39 // Scenario: We want to be able to translate an Error to a string.
40 // Problem: Thread-safety. Strerror(3) isn't thread safe (unless
41 // thread-local-variables are used, which is probably only
43 // Solution: Use strerror_r().
44 // Problem: strerror_r() isn't portable.
45 // (Apparently Solaris doesn't provide it.)
46 // Solution: Cry. Then introduce an intermediary, ErrorMarshal.
47 // ErrorMarshal exposes a single public delegate, Translator,
48 // which will convert an Error to a string. It's static
49 // constructor first tries using strerror_r(). If it works,
50 // great; use it in the future. If it doesn't work, fallback to
52 // This should be thread safe, since the check is done within the
53 // class constructor lock.
54 // Strerror(3) will be thread-safe from managed code, but won't
55 // be thread-safe between managed & unmanaged code.
56 internal class ErrorMarshal
58 internal delegate string ErrorTranslator (Native.Errno errno);
60 internal static readonly ErrorTranslator Translate;
62 static ErrorMarshal ()
65 Translate = new ErrorTranslator (strerror_r);
66 Translate (Native.Errno.ERANGE);
68 catch (EntryPointNotFoundException) {
69 Translate = new ErrorTranslator (strerror);
73 private static string strerror (Native.Errno errno)
75 return Native.Stdlib.strerror (errno);
78 private static string strerror_r (Native.Errno errno)
80 StringBuilder buf = new StringBuilder (16);
84 r = Native.Syscall.strerror_r (errno, buf);
85 } while (r == -1 && Native.Stdlib.GetLastError() == Native.Errno.ERANGE);
88 return "** Unknown error code: " + ((int) errno) + "**";
89 return buf.ToString();
93 public sealed /* static */ class UnixMarshal
95 private UnixMarshal () {}
97 [Obsolete ("Use GetErrorDescription (Mono.Unix.Native.Errno)")]
98 public static string GetErrorDescription (Error errno)
100 return ErrorMarshal.Translate ((Native.Errno) (int) errno);
103 [CLSCompliant (false)]
104 public static string GetErrorDescription (Native.Errno errno)
106 return ErrorMarshal.Translate (errno);
109 [Obsolete ("Use AllocHeap(long)")]
110 public static IntPtr Alloc (long size)
112 return AllocHeap (size);
115 public static IntPtr AllocHeap (long size)
118 throw new ArgumentOutOfRangeException ("size", "< 0");
119 return Stdlib.malloc ((ulong) size);
122 [Obsolete ("Use ReAllocHeap(long)")]
123 public static IntPtr ReAlloc (IntPtr ptr, long size)
125 return ReAllocHeap (ptr, size);
128 public static IntPtr ReAllocHeap (IntPtr ptr, long size)
131 throw new ArgumentOutOfRangeException ("size", "< 0");
132 return Stdlib.realloc (ptr, (ulong) size);
135 [Obsolete ("Use FreeHeap(IntPtr)")]
136 public static void Free (IntPtr ptr)
141 public static void FreeHeap (IntPtr ptr)
146 public static unsafe string PtrToStringUnix (IntPtr p)
148 if (p == IntPtr.Zero)
151 int len = checked ((int) Native.Stdlib.strlen (p));
152 return new string ((sbyte*) p, 0, len, UnixEncoding.Instance);
155 public static string PtrToString (IntPtr p)
157 if (p == IntPtr.Zero)
159 return PtrToString (p, UnixEncoding.Instance);
162 public static unsafe string PtrToString (IntPtr p, Encoding encoding)
164 if (p == IntPtr.Zero)
167 int len = GetStringByteLength (p, encoding);
169 // Due to variable-length encoding schemes, GetStringByteLength() may
170 // have returned multiple "null" characters. (For example, when
171 // encoding a string into UTF-8 there will be 4 terminating nulls.)
172 // We don't want these null's to be in the returned string, so strip
174 string s = new string ((sbyte*) p, 0, len, encoding);
176 while (len > 0 && s [len-1] == 0)
180 return s.Substring (0, len);
183 private static int GetStringByteLength (IntPtr p, Encoding encoding)
185 Type encodingType = encoding.GetType ();
189 // Encodings that will always end with a single null byte
190 if (typeof(UTF8Encoding).IsAssignableFrom (encodingType) ||
191 typeof(UTF7Encoding).IsAssignableFrom (encodingType) ||
192 typeof(UnixEncoding).IsAssignableFrom (encodingType) ||
193 typeof(ASCIIEncoding).IsAssignableFrom (encodingType)) {
194 len = checked ((int) Native.Stdlib.strlen (p));
196 // Encodings that will always end with a 0x0000 16-bit word
197 else if (typeof(UnicodeEncoding).IsAssignableFrom (encodingType)) {
198 len = GetInt16BufferLength (p);
200 // Some non-public encoding, such as Latin1 or a DBCS charset.
201 // Look for a sequence of encoding.GetMaxByteCount() bytes that are all
202 // 0, which should be the terminating null.
203 // This is "iffy", since it may fail for variable-width encodings; for
204 // example, UTF8Encoding.GetMaxByteCount(1) = 4, so this would read 3
205 // bytes past the end of the string, possibly into garbage memory
206 // (which is why we special case UTF above).
208 len = GetRandomBufferLength (p, encoding.GetMaxByteCount(1));
212 throw new NotSupportedException ("Unable to determine native string buffer length");
216 private static int GetInt16BufferLength (IntPtr p)
219 while (Marshal.ReadInt16 (p, len*2) != 0)
221 return checked(len*2);
224 private static int GetInt32BufferLength (IntPtr p)
227 while (Marshal.ReadInt32 (p, len*4) != 0)
229 return checked(len*4);
232 private static int GetRandomBufferLength (IntPtr p, int nullLength)
234 switch (nullLength) {
235 case 1: return checked ((int) Native.Stdlib.strlen (p));
236 case 2: return GetInt16BufferLength (p);
237 case 4: return GetInt32BufferLength (p);
241 int num_null_seen = 0;
244 byte b = Marshal.ReadByte (p, len++);
249 } while (num_null_seen != nullLength);
255 * Marshal a C `char **'. ANSI C `main' requirements are assumed:
257 * stringArray is an array of pointers to C strings
258 * stringArray has a terminating NULL string.
261 * stringArray[0] = "string 1";
262 * stringArray[1] = "string 2";
263 * stringArray[2] = NULL
265 * The terminating NULL is required so that we know when to stop looking
268 public static string[] PtrToStringArray (IntPtr stringArray)
270 return PtrToStringArray (stringArray, UnixEncoding.Instance);
273 public static string[] PtrToStringArray (IntPtr stringArray, Encoding encoding)
275 if (stringArray == IntPtr.Zero)
276 return new string[]{};
278 int argc = CountStrings (stringArray);
279 return PtrToStringArray (argc, stringArray, encoding);
282 private static int CountStrings (IntPtr stringArray)
285 while (Marshal.ReadIntPtr (stringArray, count*IntPtr.Size) != IntPtr.Zero)
291 * Like PtrToStringArray(IntPtr), but it allows the user to specify how
292 * many strings to look for in the array. As such, the requirement for a
293 * terminating NULL element is not required.
295 * Usage is similar to ANSI C `main': count is argc, stringArray is argv.
296 * stringArray[count] is NOT accessed (though ANSI C requires that
297 * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
299 public static string[] PtrToStringArray (int count, IntPtr stringArray)
301 return PtrToStringArray (count, stringArray, UnixEncoding.Instance);
304 public static string[] PtrToStringArray (int count, IntPtr stringArray, Encoding encoding)
307 throw new ArgumentOutOfRangeException ("count", "< 0");
308 if (stringArray == IntPtr.Zero)
309 return new string[count];
311 string[] members = new string[count];
312 for (int i = 0; i < count; ++i) {
313 IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
314 members[i] = PtrToString (s, encoding);
320 [Obsolete ("Use StringToHeap(string)")]
321 public static IntPtr StringToAlloc (string s)
323 return StringToHeap (s, UnixEncoding.Instance);
326 public static IntPtr StringToHeap (string s)
328 return StringToHeap (s, UnixEncoding.Instance);
331 public static IntPtr StringToHeap (string s, Encoding encoding)
333 return StringToHeap (s, 0, s.Length, encoding);
336 public static IntPtr StringToHeap (string s, int index, int count)
338 return StringToHeap (s, index, count, UnixEncoding.Instance);
341 public static IntPtr StringToHeap (string s, int index, int count, Encoding encoding)
343 int min_byte_count = encoding.GetMaxByteCount(1);
344 char[] copy = s.ToCharArray (index, count);
345 byte[] marshal = new byte [encoding.GetByteCount (copy) + min_byte_count];
347 int bytes_copied = encoding.GetBytes (copy, 0, copy.Length, marshal, 0);
349 if (bytes_copied != (marshal.Length-min_byte_count))
350 throw new NotSupportedException ("encoding.GetBytes() doesn't equal encoding.GetByteCount()!");
352 IntPtr mem = Alloc (marshal.Length);
353 if (mem == IntPtr.Zero)
354 throw new OutOfMemoryException ();
358 Marshal.Copy (marshal, 0, mem, marshal.Length);
369 public static bool ShouldRetrySyscall (int r)
371 if (r == -1 && Stdlib.GetLastError () == Error.EINTR)
376 [Obsolete ("Use ShouldRetrySyscall (int, out Mono.Unix.Native.Errno")]
377 public static bool ShouldRetrySyscall (int r, out Error errno)
380 if (r == -1 && (errno = Stdlib.GetLastError ()) == Error.EINTR)
385 [CLSCompliant (false)]
386 public static bool ShouldRetrySyscall (int r, out Native.Errno errno)
388 errno = (Native.Errno) 0;
389 if (r == -1 && (errno = Native.Stdlib.GetLastError ()) == Native.Errno.EINTR)
394 // we can't permit any printf(3)-style formatting information, since that
395 // would kill the stack. However, replacing %% is silly, and some %* are
396 // permitted (such as %m in syslog to print strerror(errno)).
397 internal static string EscapeFormatString (string message,
402 StringBuilder sb = new StringBuilder (message.Length);
403 for (int i = 0; i < message.Length; ++i) {
404 char c = message [i];
406 if (c == '%' && (i+1) < message.Length) {
407 char n = message [i+1];
408 if (n == '%' || IsCharPresent (permitted, n))
411 sb.Append ('%').Append (n);
414 // invalid format string: % at EOS.
418 return sb.ToString ();
421 private static bool IsCharPresent (char[] array, char c)
425 for (int i = 0; i < array.Length; ++i)
431 [Obsolete ("Use CreateExceptionForError (Mono.Unix.Native.Errno)")]
432 internal static Exception CreateExceptionForError (Error errno)
434 string message = GetErrorDescription (errno);
435 UnixIOException p = new UnixIOException (errno);
437 case Error.EFAULT: return new NullReferenceException (message, p);
438 case Error.EINVAL: return new ArgumentException (message, p);
443 return new IOException (message, p);
444 case Error.ENAMETOOLONG: return new PathTooLongException (message, p);
445 case Error.ENOENT: return new FileNotFoundException (message, p);
446 case Error.ENOEXEC: return new InvalidProgramException (message, p);
447 case Error.EOVERFLOW: return new OverflowException (message, p);
448 case Error.ERANGE: return new ArgumentOutOfRangeException (message);
449 default: /* ignore */ break;
454 internal static Exception CreateExceptionForError (Native.Errno errno)
456 string message = GetErrorDescription (errno);
457 UnixIOException p = new UnixIOException (errno);
459 case Native.Errno.EFAULT: return new NullReferenceException (message, p);
460 case Native.Errno.EINVAL: return new ArgumentException (message, p);
461 case Native.Errno.EIO:
462 case Native.Errno.ENOSPC:
463 case Native.Errno.EROFS:
464 case Native.Errno.ESPIPE:
465 return new IOException (message, p);
466 case Native.Errno.ENAMETOOLONG: return new PathTooLongException (message, p);
467 case Native.Errno.ENOENT: return new FileNotFoundException (message, p);
468 case Native.Errno.ENOEXEC: return new InvalidProgramException (message, p);
469 case Native.Errno.EOVERFLOW: return new OverflowException (message, p);
470 case Native.Errno.ERANGE: return new ArgumentOutOfRangeException (message);
471 default: /* ignore */ break;
476 internal static Exception CreateExceptionForLastError ()
478 return CreateExceptionForError (Stdlib.GetLastError());
481 [Obsolete ("Use ThrowExceptionForError (Mono.Unix.Native.Errno)")]
482 public static void ThrowExceptionForError (Error errno)
484 throw CreateExceptionForError (errno);
487 [CLSCompliant (false)]
488 public static void ThrowExceptionForError (Native.Errno errno)
490 throw CreateExceptionForError (errno);
493 public static void ThrowExceptionForLastError ()
495 throw CreateExceptionForLastError ();
498 [Obsolete ("Use ThrowExceptionForErrorIf (int, Mono.Unix.Native.Errno)")]
499 public static void ThrowExceptionForErrorIf (int retval, Error errno)
502 ThrowExceptionForError (errno);
505 [CLSCompliant (false)]
506 public static void ThrowExceptionForErrorIf (int retval, Native.Errno errno)
509 ThrowExceptionForError (errno);
512 public static void ThrowExceptionForLastErrorIf (int retval)
515 ThrowExceptionForLastError ();