[Posix] Optimize string marshal code by using unsafe code (#4964)
authorMarius Ungureanu <teromario@yahoo.com>
Fri, 2 Jun 2017 07:03:47 +0000 (00:03 -0700)
committerMarek Safar <marek.safar@gmail.com>
Fri, 2 Jun 2017 07:03:47 +0000 (09:03 +0200)
mcs/class/Mono.Posix/Mono.Unix/UnixEncoding.cs
mcs/class/Mono.Posix/Mono.Unix/UnixMarshal.cs

index 1d8e40ca45cf5ac5e4fcec6e97eaa6f754790c51..d869b1b90c68bb7d8a4dc47142c597d70c0f5835 100644 (file)
@@ -300,17 +300,35 @@ public class UnixEncoding : Encoding
                        throw new ArgumentOutOfRangeException ("byteIndex", _("ArgRange_Array"));
                }
 
+               unsafe {
+                       fixed (char* p = s) {
+                               fixed (byte* b = bytes) {
+                                       return GetBytes (p + charIndex, charCount, b + byteIndex, bytes.Length - byteIndex);
+                               }
+                       }
+               }
+       }
+
+       public unsafe override int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
+       {
+               if (bytes == null || chars == null)
+                       throw new ArgumentNullException (bytes == null ? "bytes" : "chars");
+
+               if (charCount < 0 || byteCount < 0)
+                       throw new ArgumentOutOfRangeException (charCount < 0 ? "charCount" : "byteCount");
+                       
                // Convert the characters into bytes.
                char ch;
-               int length = bytes.Length;
+               int length = byteCount;
                uint pair;
-               int posn = byteIndex;
+               int posn = 0;
+               int charIndex = 0;
                while (charCount > 0) {
                        // Fetch the next UTF-16 character pair value.
-                       ch = s[charIndex++];
+                       ch = chars [charIndex++];
                        if (ch >= '\uD800' && ch <= '\uDBFF' && charCount > 1) {
                                // This may be the start of a surrogate pair.
-                               pair = (uint)(s[charIndex]);
+                               pair = (uint)(chars[charIndex]);
                                if (pair >= (uint)0xDC00 && pair <= (uint)0xDFFF) {
                                        pair = (pair - (uint)0xDC00) +
                                                   ((((uint)ch) - (uint)0xD800) << 10) +
@@ -326,7 +344,7 @@ public class UnixEncoding : Encoding
                                }
                                charCount -= 2;
                                if (charCount >= 0) {
-                                       bytes[posn++] = (byte) s [charIndex++];
+                                       bytes[posn++] = (byte)chars [charIndex++];
                                }
                                continue;
                        } else {
@@ -365,7 +383,7 @@ public class UnixEncoding : Encoding
                }
 
                // Return the final count to the caller.
-               return posn - byteIndex;
+               return posn;
        }
 
        // Internal version of "GetCharCount" which can handle a rolling
@@ -394,7 +412,7 @@ public class UnixEncoding : Encoding
                uint leftSoFar = (leftOverCount & (uint)0x0F);
                uint leftSize = ((leftOverCount >> 4) & (uint)0x0F);
                while (count > 0) {
-                       ch = (uint)(bytes[index++]);
+                       ch = (uint)(bytes [index++]);
                        ++next_raw;
                        --count;
                        if (leftSize == 0) {
index 334b6f2a49b42c089ea2b1492109021cbc75628a..0b01eb19ad40fc1592cd78c2ed3aa74f6a1c6a7e 100644 (file)
@@ -325,27 +325,42 @@ namespace Mono.Unix {
                        if (encoding == null)
                                throw new ArgumentNullException ("encoding");
 
-                       int min_byte_count = encoding.GetMaxByteCount(1);
-                       char[] copy = s.ToCharArray (index, count);
-                       byte[] marshal = new byte [encoding.GetByteCount (copy) + min_byte_count];
+                       if (index < 0 || count < 0)
+                               throw new ArgumentOutOfRangeException ((index < 0 ? "index" : "count"),
+                                        "Non - negative number required.");
 
-                       int bytes_copied = encoding.GetBytes (copy, 0, copy.Length, marshal, 0);
+                       if (s.Length - index < count)
+                               throw new ArgumentOutOfRangeException ("s", "Index and count must refer to a location within the string.");
 
-                       if (bytes_copied != (marshal.Length-min_byte_count))
-                               throw new NotSupportedException ("encoding.GetBytes() doesn't equal encoding.GetByteCount()!");
+                       int null_terminator_count = encoding.GetMaxByteCount (1);
+                       int length_without_null = encoding.GetByteCount (s);
+                       int marshalLength = checked (length_without_null + null_terminator_count);
 
-                       IntPtr mem = AllocHeap (marshal.Length);
+                       IntPtr mem = AllocHeap (marshalLength);
                        if (mem == IntPtr.Zero)
                                throw new UnixIOException (Native.Errno.ENOMEM);
 
-                       bool copied = false;
-                       try {
-                               Marshal.Copy (marshal, 0, mem, marshal.Length);
-                               copied = true;
-                       }
-                       finally {
-                               if (!copied)
-                                       FreeHeap (mem);
+                       unsafe {
+                               fixed (char* p = s) {
+                                       byte* marshal = (byte*)mem;
+                                       int bytes_copied;
+
+                                       try {
+                                               bytes_copied = encoding.GetBytes (p + index, count, marshal, marshalLength);
+                                       } catch {
+                                               FreeHeap (mem);
+                                               throw;
+                                       }
+
+                                       if (bytes_copied != length_without_null) {
+                                               FreeHeap (mem);
+                                               throw new NotSupportedException ("encoding.GetBytes() doesn't equal encoding.GetByteCount()!");
+                                       }
+
+                                       marshal += length_without_null;
+                                       for (int i = 0; i < null_terminator_count; ++i)
+                                               marshal[i] = 0;
+                               }
                        }
 
                        return mem;