e2950ec768
Former-commit-id: fc39669a0b707dd3c063977486506b6793da2890
772 lines
21 KiB
C#
772 lines
21 KiB
C#
//
|
|
// System.String.cs
|
|
//
|
|
// Authors:
|
|
// Patrik Torstensson
|
|
// Jeffrey Stedfast (fejj@ximian.com)
|
|
// Dan Lewis (dihlewis@yahoo.co.uk)
|
|
// Sebastien Pouliot <sebastien@ximian.com>
|
|
// Marek Safar (marek.safar@seznam.cz)
|
|
// Andreas Nahr (Classdevelopment@A-SoftTech.com)
|
|
//
|
|
// (C) 2001 Ximian, Inc. http://www.ximian.com
|
|
// Copyright (C) 2004-2005 Novell (http://www.novell.com)
|
|
// Copyright (c) 2012 Xamarin, Inc (http://www.xamarin.com)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
//
|
|
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
|
|
namespace System
|
|
{
|
|
partial class String
|
|
{
|
|
[MethodImplAttribute (MethodImplOptions.InternalCall)]
|
|
public extern String (ReadOnlySpan<char> value);
|
|
|
|
public int Length {
|
|
get {
|
|
return m_stringLength;
|
|
}
|
|
}
|
|
|
|
public unsafe static implicit operator ReadOnlySpan<char> (String value)
|
|
{
|
|
if (value == null)
|
|
return default;
|
|
|
|
fixed (void* start = &value.m_firstChar)
|
|
return new ReadOnlySpan<char> (start, value.Length);
|
|
}
|
|
|
|
|
|
internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
|
|
{
|
|
if (strA == null) {
|
|
return strB == null ? 0 : -1;
|
|
}
|
|
if (strB == null) {
|
|
return 1;
|
|
}
|
|
int lengthA = Math.Min (lenA, strA.m_stringLength - indexA);
|
|
int lengthB = Math.Min (lenB, strB.m_stringLength - indexB);
|
|
|
|
if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
|
|
return 0;
|
|
|
|
fixed (char* aptr = strA, bptr = strB) {
|
|
char* ap = aptr + indexA;
|
|
char* end = ap + Math.Min (lengthA, lengthB);
|
|
char* bp = bptr + indexB;
|
|
while (ap < end) {
|
|
if (*ap != *bp)
|
|
return *ap - *bp;
|
|
ap++;
|
|
bp++;
|
|
}
|
|
return lengthA - lengthB;
|
|
}
|
|
}
|
|
|
|
public int IndexOf (char value, int startIndex, int count)
|
|
{
|
|
if (startIndex < 0 || startIndex > this.m_stringLength)
|
|
throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
|
|
if (count < 0)
|
|
throw new ArgumentOutOfRangeException ("count", "< 0");
|
|
if (startIndex > this.m_stringLength - count)
|
|
throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.m_stringLength");
|
|
|
|
if ((startIndex == 0 && this.m_stringLength == 0) || (startIndex == this.m_stringLength) || (count == 0))
|
|
return -1;
|
|
|
|
return IndexOfUnchecked (value, startIndex, count);
|
|
}
|
|
|
|
internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
|
|
{
|
|
// It helps JIT compiler to optimize comparison
|
|
int value_32 = (int)value;
|
|
|
|
fixed (char* start = &m_firstChar) {
|
|
char* ptr = start + startIndex;
|
|
char* end_ptr = ptr + (count >> 3 << 3);
|
|
|
|
while (ptr != end_ptr) {
|
|
if (*ptr == value_32)
|
|
return (int)(ptr - start);
|
|
if (ptr[1] == value_32)
|
|
return (int)(ptr - start + 1);
|
|
if (ptr[2] == value_32)
|
|
return (int)(ptr - start + 2);
|
|
if (ptr[3] == value_32)
|
|
return (int)(ptr - start + 3);
|
|
if (ptr[4] == value_32)
|
|
return (int)(ptr - start + 4);
|
|
if (ptr[5] == value_32)
|
|
return (int)(ptr - start + 5);
|
|
if (ptr[6] == value_32)
|
|
return (int)(ptr - start + 6);
|
|
if (ptr[7] == value_32)
|
|
return (int)(ptr - start + 7);
|
|
|
|
ptr += 8;
|
|
}
|
|
|
|
end_ptr += count & 0x07;
|
|
while (ptr != end_ptr) {
|
|
if (*ptr == value_32)
|
|
return (int)(ptr - start);
|
|
|
|
ptr++;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
internal unsafe int IndexOfUnchecked (string value, int startIndex, int count)
|
|
{
|
|
int valueLen = value.Length;
|
|
if (count < valueLen)
|
|
return -1;
|
|
|
|
if (valueLen <= 1) {
|
|
if (valueLen == 1)
|
|
return IndexOfUnchecked (value[0], startIndex, count);
|
|
return startIndex;
|
|
}
|
|
|
|
fixed (char* thisptr = &m_firstChar, valueptr = value) {
|
|
char* ap = thisptr + startIndex;
|
|
char* thisEnd = ap + count - valueLen + 1;
|
|
while (ap != thisEnd) {
|
|
if (*ap == *valueptr) {
|
|
for (int i = 1; i < valueLen; i++) {
|
|
if (ap[i] != valueptr[i])
|
|
goto NextVal;
|
|
}
|
|
return (int)(ap - thisptr);
|
|
}
|
|
NextVal:
|
|
ap++;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public int IndexOfAny (char [] anyOf, int startIndex, int count)
|
|
{
|
|
if (anyOf == null)
|
|
throw new ArgumentNullException ();
|
|
if (startIndex < 0 || startIndex > this.m_stringLength)
|
|
throw new ArgumentOutOfRangeException ();
|
|
if (count < 0 || startIndex > this.m_stringLength - count)
|
|
throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than m_stringLength of the string.");
|
|
|
|
return IndexOfAnyUnchecked (anyOf, startIndex, count);
|
|
}
|
|
|
|
unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
|
|
{
|
|
if (anyOf.Length == 0)
|
|
return -1;
|
|
|
|
if (anyOf.Length == 1)
|
|
return IndexOfUnchecked (anyOf[0], startIndex, count);
|
|
|
|
fixed (char* any = anyOf) {
|
|
int highest = *any;
|
|
int lowest = *any;
|
|
|
|
char* end_any_ptr = any + anyOf.Length;
|
|
char* any_ptr = any;
|
|
while (++any_ptr != end_any_ptr) {
|
|
if (*any_ptr > highest) {
|
|
highest = *any_ptr;
|
|
continue;
|
|
}
|
|
|
|
if (*any_ptr < lowest)
|
|
lowest = *any_ptr;
|
|
}
|
|
|
|
fixed (char* start = &m_firstChar) {
|
|
char* ptr = start + startIndex;
|
|
char* end_ptr = ptr + count;
|
|
|
|
while (ptr != end_ptr) {
|
|
if (*ptr > highest || *ptr < lowest) {
|
|
ptr++;
|
|
continue;
|
|
}
|
|
|
|
if (*ptr == *any)
|
|
return (int)(ptr - start);
|
|
|
|
any_ptr = any;
|
|
while (++any_ptr != end_any_ptr) {
|
|
if (*ptr == *any_ptr)
|
|
return (int)(ptr - start);
|
|
}
|
|
|
|
ptr++;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public int LastIndexOf (char value, int startIndex, int count)
|
|
{
|
|
if (this.m_stringLength == 0)
|
|
return -1;
|
|
|
|
// >= for char (> for string)
|
|
if ((startIndex < 0) || (startIndex >= this.Length))
|
|
throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
|
|
if ((count < 0) || (count > this.Length))
|
|
throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
|
|
if (startIndex - count + 1 < 0)
|
|
throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
|
|
|
|
return LastIndexOfUnchecked (value, startIndex, count);
|
|
}
|
|
|
|
internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
|
|
{
|
|
// It helps JIT compiler to optimize comparison
|
|
int value_32 = (int)value;
|
|
|
|
fixed (char* start = &m_firstChar) {
|
|
char* ptr = start + startIndex;
|
|
char* end_ptr = ptr - (count >> 3 << 3);
|
|
|
|
while (ptr != end_ptr) {
|
|
if (*ptr == value_32)
|
|
return (int)(ptr - start);
|
|
if (ptr[-1] == value_32)
|
|
return (int)(ptr - start) - 1;
|
|
if (ptr[-2] == value_32)
|
|
return (int)(ptr - start) - 2;
|
|
if (ptr[-3] == value_32)
|
|
return (int)(ptr - start) - 3;
|
|
if (ptr[-4] == value_32)
|
|
return (int)(ptr - start) - 4;
|
|
if (ptr[-5] == value_32)
|
|
return (int)(ptr - start) - 5;
|
|
if (ptr[-6] == value_32)
|
|
return (int)(ptr - start) - 6;
|
|
if (ptr[-7] == value_32)
|
|
return (int)(ptr - start) - 7;
|
|
|
|
ptr -= 8;
|
|
}
|
|
|
|
end_ptr -= count & 0x07;
|
|
while (ptr != end_ptr) {
|
|
if (*ptr == value_32)
|
|
return (int)(ptr - start);
|
|
|
|
ptr--;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
|
|
{
|
|
if (anyOf == null)
|
|
throw new ArgumentNullException ();
|
|
if (this.m_stringLength == 0)
|
|
return -1;
|
|
|
|
if ((startIndex < 0) || (startIndex >= this.Length))
|
|
throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
|
|
if ((count < 0) || (count > this.Length))
|
|
throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
|
|
if (startIndex - count + 1 < 0)
|
|
throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
|
|
|
|
if (this.m_stringLength == 0)
|
|
return -1;
|
|
|
|
return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
|
|
}
|
|
|
|
private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
|
|
{
|
|
if (anyOf.Length == 1)
|
|
return LastIndexOfUnchecked (anyOf[0], startIndex, count);
|
|
|
|
fixed (char* start = &m_firstChar, testStart = anyOf) {
|
|
char* ptr = start + startIndex;
|
|
char* ptrEnd = ptr - count;
|
|
char* test;
|
|
char* testEnd = testStart + anyOf.Length;
|
|
|
|
while (ptr != ptrEnd) {
|
|
test = testStart;
|
|
while (test != testEnd) {
|
|
if (*test == *ptr)
|
|
return (int)(ptr - start);
|
|
test++;
|
|
}
|
|
ptr--;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
internal static int nativeCompareOrdinalEx (String strA, int indexA, String strB, int indexB, int count)
|
|
{
|
|
//
|
|
// .net does following checks in unmanaged land only which is quite
|
|
// wrong as it's not always necessary and argument names don't match
|
|
// but we are compatible
|
|
//
|
|
if (count < 0)
|
|
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
|
|
|
|
if (indexA < 0 || indexA > strA.Length)
|
|
throw new ArgumentOutOfRangeException("indexA", Environment.GetResourceString("ArgumentOutOfRange_Index"));
|
|
|
|
if (indexB < 0 || indexB > strB.Length)
|
|
throw new ArgumentOutOfRangeException("indexB", Environment.GetResourceString("ArgumentOutOfRange_Index"));
|
|
|
|
return CompareOrdinalUnchecked (strA, indexA, count, strB, indexB, count);
|
|
}
|
|
|
|
unsafe String ReplaceInternal (char oldChar, char newChar)
|
|
{
|
|
if (this.m_stringLength == 0 || oldChar == newChar)
|
|
return this;
|
|
|
|
int start_pos = IndexOfUnchecked (oldChar, 0, this.m_stringLength);
|
|
if (start_pos == -1)
|
|
return this;
|
|
|
|
if (start_pos < 4)
|
|
start_pos = 0;
|
|
|
|
string tmp = FastAllocateString (m_stringLength);
|
|
fixed (char* dest = tmp, src = &m_firstChar) {
|
|
if (start_pos != 0)
|
|
CharCopy (dest, src, start_pos);
|
|
|
|
char* end_ptr = dest + m_stringLength;
|
|
char* dest_ptr = dest + start_pos;
|
|
char* src_ptr = src + start_pos;
|
|
|
|
while (dest_ptr != end_ptr) {
|
|
if (*src_ptr == oldChar)
|
|
*dest_ptr = newChar;
|
|
else
|
|
*dest_ptr = *src_ptr;
|
|
|
|
++src_ptr;
|
|
++dest_ptr;
|
|
}
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
internal String ReplaceInternal (String oldValue, String newValue)
|
|
{
|
|
// LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
|
|
// LAMESPEC: Result is undefined if result Length is longer than maximum string Length
|
|
|
|
if (oldValue == null)
|
|
throw new ArgumentNullException ("oldValue");
|
|
|
|
if (oldValue.Length == 0)
|
|
throw new ArgumentException ("oldValue is the empty string.");
|
|
|
|
if (this.Length == 0)
|
|
return this;
|
|
|
|
if (newValue == null)
|
|
newValue = Empty;
|
|
|
|
return ReplaceUnchecked (oldValue, newValue);
|
|
}
|
|
|
|
private unsafe String ReplaceUnchecked (String oldValue, String newValue)
|
|
{
|
|
if (oldValue.m_stringLength > m_stringLength)
|
|
return this;
|
|
|
|
if (oldValue.m_stringLength == 1 && newValue.m_stringLength == 1) {
|
|
return Replace (oldValue[0], newValue[0]);
|
|
// ENHANCE: It would be possible to special case oldValue.m_stringLength == newValue.m_stringLength
|
|
// because the m_stringLength of the result would be this.m_stringLength and m_stringLength calculation unneccesary
|
|
}
|
|
|
|
const int maxValue = 200; // Allocate 800 byte maximum
|
|
int* dat = stackalloc int[maxValue];
|
|
fixed (char* source = &m_firstChar, replace = newValue) {
|
|
int i = 0, count = 0;
|
|
while (i < m_stringLength) {
|
|
int found = IndexOfUnchecked (oldValue, i, m_stringLength - i);
|
|
if (found < 0)
|
|
break;
|
|
else {
|
|
if (count < maxValue)
|
|
dat[count++] = found;
|
|
else
|
|
return ReplaceFallback (oldValue, newValue, maxValue);
|
|
}
|
|
i = found + oldValue.m_stringLength;
|
|
}
|
|
if (count == 0)
|
|
return this;
|
|
|
|
int nlen = 0;
|
|
checked {
|
|
try {
|
|
nlen = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * count);
|
|
} catch (OverflowException) {
|
|
throw new OutOfMemoryException ();
|
|
}
|
|
}
|
|
String tmp = FastAllocateString (nlen);
|
|
|
|
int curPos = 0, lastReadPos = 0;
|
|
fixed (char* dest = tmp) {
|
|
for (int j = 0; j < count; j++) {
|
|
int precopy = dat[j] - lastReadPos;
|
|
CharCopy (dest + curPos, source + lastReadPos, precopy);
|
|
curPos += precopy;
|
|
lastReadPos = dat[j] + oldValue.m_stringLength;
|
|
CharCopy (dest + curPos, replace, newValue.m_stringLength);
|
|
curPos += newValue.m_stringLength;
|
|
}
|
|
CharCopy (dest + curPos, source + lastReadPos, m_stringLength - lastReadPos);
|
|
}
|
|
return tmp;
|
|
}
|
|
}
|
|
|
|
private String ReplaceFallback (String oldValue, String newValue, int testedCount)
|
|
{
|
|
int lengthEstimate = this.m_stringLength + ((newValue.m_stringLength - oldValue.m_stringLength) * testedCount);
|
|
StringBuilder sb = new StringBuilder (lengthEstimate);
|
|
for (int i = 0; i < m_stringLength;) {
|
|
int found = IndexOfUnchecked (oldValue, i, m_stringLength - i);
|
|
if (found < 0) {
|
|
sb.Append (InternalSubString (i, m_stringLength - i));
|
|
break;
|
|
}
|
|
sb.Append (InternalSubString (i, found - i));
|
|
sb.Append (newValue);
|
|
i = found + oldValue.m_stringLength;
|
|
}
|
|
return sb.ToString ();
|
|
|
|
}
|
|
|
|
unsafe String PadHelper (int totalWidth, char paddingChar, bool isRightPadded)
|
|
{
|
|
if (totalWidth < 0)
|
|
throw new ArgumentOutOfRangeException ("totalWidth", "Non-negative number required");
|
|
if (totalWidth <= m_stringLength)
|
|
return this;
|
|
|
|
string result = FastAllocateString (totalWidth);
|
|
|
|
fixed (char *dest = result, src = &m_firstChar) {
|
|
if (isRightPadded) {
|
|
CharCopy (dest, src, m_stringLength);
|
|
char *end = dest + totalWidth;
|
|
char *p = dest + m_stringLength;
|
|
while (p < end) {
|
|
*p++ = paddingChar;
|
|
}
|
|
} else {
|
|
char *p = dest;
|
|
char *end = p + totalWidth - m_stringLength;
|
|
while (p < end) {
|
|
*p++ = paddingChar;
|
|
}
|
|
CharCopy (p, src, m_stringLength);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal bool StartsWithOrdinalUnchecked (String value)
|
|
{
|
|
return m_stringLength >= value.m_stringLength && CompareOrdinalUnchecked (this, 0, value.m_stringLength, value, 0, value.m_stringLength) == 0;
|
|
}
|
|
|
|
internal unsafe bool IsAscii ()
|
|
{
|
|
fixed (char* src = &m_firstChar) {
|
|
char* end_ptr = src + m_stringLength;
|
|
char* str_ptr = src;
|
|
|
|
while (str_ptr != end_ptr) {
|
|
if (*str_ptr >= 0x80)
|
|
return false;
|
|
|
|
++str_ptr;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal bool IsFastSort ()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
[MethodImplAttribute (MethodImplOptions.InternalCall)]
|
|
private extern static string InternalIsInterned (string str);
|
|
|
|
[MethodImplAttribute (MethodImplOptions.InternalCall)]
|
|
private extern static string InternalIntern (string str);
|
|
|
|
internal static unsafe void CharCopy (char *dest, char *src, int count) {
|
|
// Same rules as for memcpy, but with the premise that
|
|
// chars can only be aligned to even addresses if their
|
|
// enclosing types are correctly aligned
|
|
if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
|
|
if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
|
|
((short*)dest) [0] = ((short*)src) [0];
|
|
dest++;
|
|
src++;
|
|
count--;
|
|
}
|
|
if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
|
|
Buffer.memcpy2 ((byte*)dest, (byte*)src, count * 2);
|
|
return;
|
|
}
|
|
}
|
|
Buffer.memcpy4 ((byte*)dest, (byte*)src, count * 2);
|
|
}
|
|
|
|
#region Runtime method-to-ir dependencies
|
|
|
|
/* helpers used by the runtime as well as above or eslewhere in corlib */
|
|
static unsafe void memset (byte *dest, int val, int len)
|
|
{
|
|
if (len < 8) {
|
|
while (len != 0) {
|
|
*dest = (byte)val;
|
|
++dest;
|
|
--len;
|
|
}
|
|
return;
|
|
}
|
|
if (val != 0) {
|
|
val = val | (val << 8);
|
|
val = val | (val << 16);
|
|
}
|
|
// align to 4
|
|
int rest = (int)dest & 3;
|
|
if (rest != 0) {
|
|
rest = 4 - rest;
|
|
len -= rest;
|
|
do {
|
|
*dest = (byte)val;
|
|
++dest;
|
|
--rest;
|
|
} while (rest != 0);
|
|
}
|
|
while (len >= 16) {
|
|
((int*)dest) [0] = val;
|
|
((int*)dest) [1] = val;
|
|
((int*)dest) [2] = val;
|
|
((int*)dest) [3] = val;
|
|
dest += 16;
|
|
len -= 16;
|
|
}
|
|
while (len >= 4) {
|
|
((int*)dest) [0] = val;
|
|
dest += 4;
|
|
len -= 4;
|
|
}
|
|
// tail bytes
|
|
while (len > 0) {
|
|
*dest = (byte)val;
|
|
dest++;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
static unsafe void memcpy (byte *dest, byte *src, int size)
|
|
{
|
|
Buffer.Memcpy (dest, src, size);
|
|
}
|
|
|
|
/* Used by the runtime */
|
|
internal static unsafe void bzero (byte *dest, int len) {
|
|
memset (dest, 0, len);
|
|
}
|
|
|
|
internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
|
|
((byte*)dest) [0] = 0;
|
|
}
|
|
|
|
internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
|
|
((short*)dest) [0] = 0;
|
|
}
|
|
|
|
internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
|
|
((int*)dest) [0] = 0;
|
|
}
|
|
|
|
internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
|
|
((long*)dest) [0] = 0;
|
|
}
|
|
|
|
internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
|
|
((byte*)dest) [0] = ((byte*)src) [0];
|
|
}
|
|
|
|
internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
|
|
((short*)dest) [0] = ((short*)src) [0];
|
|
}
|
|
|
|
internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
|
|
((int*)dest) [0] = ((int*)src) [0];
|
|
}
|
|
|
|
internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
|
|
((long*)dest) [0] = ((long*)src) [0];
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Certain constructors are redirected to CreateString methods with
|
|
// matching argument list. The this pointer should not be used.
|
|
|
|
private unsafe String CreateString (sbyte* value)
|
|
{
|
|
if (value == null)
|
|
return Empty;
|
|
|
|
byte* bytes = (byte*) value;
|
|
int length = 0;
|
|
|
|
try {
|
|
while (bytes++ [0] != 0)
|
|
length++;
|
|
} catch (NullReferenceException) {
|
|
throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
|
|
}
|
|
|
|
return CreateString (value, 0, length, null);
|
|
}
|
|
|
|
unsafe String CreateString (sbyte* value, int startIndex, int length)
|
|
{
|
|
return CreateString (value, startIndex, length, null);
|
|
}
|
|
|
|
unsafe string CreateString (char *value)
|
|
{
|
|
return CtorCharPtr (value);
|
|
}
|
|
|
|
unsafe string CreateString (char *value, int startIndex, int length)
|
|
{
|
|
return CtorCharPtrStartLength (value, startIndex, length);
|
|
}
|
|
|
|
string CreateString (char [] val, int startIndex, int length)
|
|
{
|
|
return CtorCharArrayStartLength (val, startIndex, length);
|
|
}
|
|
|
|
string CreateString (char [] val)
|
|
{
|
|
return CtorCharArray (val);
|
|
}
|
|
|
|
unsafe string CreateString (char c, int count)
|
|
{
|
|
if (count < 0)
|
|
throw new ArgumentOutOfRangeException ("count");
|
|
if (count == 0)
|
|
return Empty;
|
|
string result = FastAllocateString (count);
|
|
fixed (char *dest = result) {
|
|
char *p = dest;
|
|
char *end = p + count;
|
|
while (p < end) {
|
|
*p = c;
|
|
p++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
|
|
{
|
|
if (length < 0)
|
|
throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
|
|
if (startIndex < 0)
|
|
throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
|
|
if (value + startIndex < value)
|
|
throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
|
|
|
|
if (enc == null) {
|
|
if (value == null)
|
|
throw new ArgumentNullException ("value");
|
|
if (length == 0)
|
|
return Empty;
|
|
|
|
enc = Encoding.Default;
|
|
}
|
|
|
|
byte [] bytes = new byte [length];
|
|
|
|
if (length != 0)
|
|
fixed (byte* bytePtr = bytes)
|
|
try {
|
|
if (value == null)
|
|
throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
|
|
memcpy (bytePtr, (byte*) (value + startIndex), length);
|
|
} catch (NullReferenceException) {
|
|
throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
|
|
}
|
|
|
|
// GetString () is called even when length == 0
|
|
return enc.GetString (bytes);
|
|
}
|
|
|
|
unsafe String CreateString (ReadOnlySpan<char> value)
|
|
{
|
|
if (value.Length == 0)
|
|
return Empty;
|
|
|
|
String result = FastAllocateString (value.Length);
|
|
fixed (char *dest = result, ptr = &value.DangerousGetPinnableReference ())
|
|
wstrcpy (dest, ptr, value.Length);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
} |