+ return PtrToString (p, UnixEncoding.Instance);
+ }
+
+ public static unsafe string PtrToString (IntPtr p, Encoding encoding)
+ {
+ if (p == IntPtr.Zero)
+ return null;
+
+ int len = GetStringByteLength (p, encoding);
+
+ // Due to variable-length encoding schemes, GetStringByteLength() may
+ // have returned multiple "null" characters. (For example, when
+ // encoding a string into UTF-8 there will be 4 terminating nulls.)
+ // We don't want these null's to be in the returned string, so strip
+ // them off.
+ string s = new string ((sbyte*) p, 0, len, encoding);
+ len = s.Length;
+ while (len > 0 && s [len-1] == 0)
+ --len;
+ if (len == s.Length)
+ return s;
+ return s.Substring (0, len);
+ }
+
+ private static int GetStringByteLength (IntPtr p, Encoding encoding)
+ {
+ Type encodingType = encoding.GetType ();
+
+ int len = -1;
+
+ // Encodings that will always end with a single null byte
+ if (typeof(UTF8Encoding).IsAssignableFrom (encodingType) ||
+ typeof(UTF7Encoding).IsAssignableFrom (encodingType) ||
+ typeof(UnixEncoding).IsAssignableFrom (encodingType) ||
+ typeof(ASCIIEncoding).IsAssignableFrom (encodingType)) {
+ len = checked ((int) Native.Stdlib.strlen (p));
+ }
+ // Encodings that will always end with a 0x0000 16-bit word
+ else if (typeof(UnicodeEncoding).IsAssignableFrom (encodingType)) {
+ len = GetInt16BufferLength (p);
+ }
+ // Some non-public encoding, such as Latin1 or a DBCS charset.
+ // Look for a sequence of encoding.GetMaxByteCount() bytes that are all
+ // 0, which should be the terminating null.
+ // This is "iffy", since it may fail for variable-width encodings; for
+ // example, UTF8Encoding.GetMaxByteCount(1) = 4, so this would read 3
+ // bytes past the end of the string, possibly into garbage memory
+ // (which is why we special case UTF above).
+ else {
+ len = GetRandomBufferLength (p, encoding.GetMaxByteCount(1));
+ }
+
+ if (len == -1)
+ throw new NotSupportedException ("Unable to determine native string buffer length");
+ return len;
+ }
+
+ private static int GetInt16BufferLength (IntPtr p)
+ {
+ int len = 0;
+ while (Marshal.ReadInt16 (p, len*2) != 0)
+ checked {++len;}
+ return checked(len*2);
+ }
+
+ private static int GetInt32BufferLength (IntPtr p)
+ {
+ int len = 0;
+ while (Marshal.ReadInt32 (p, len*4) != 0)
+ checked {++len;}
+ return checked(len*4);
+ }
+
+ private static int GetRandomBufferLength (IntPtr p, int nullLength)
+ {
+ switch (nullLength) {
+ case 1: return checked ((int) Native.Stdlib.strlen (p));
+ case 2: return GetInt16BufferLength (p);
+ case 4: return GetInt32BufferLength (p);
+ }
+
+ int len = 0;
+ int num_null_seen = 0;
+
+ do {
+ byte b = Marshal.ReadByte (p, len++);
+ if (b == 0)
+ ++num_null_seen;
+ else
+ num_null_seen = 0;
+ } while (num_null_seen != nullLength);
+
+ return len;