1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 // ---------------------------------------------------------------------------
8 // ---------------------------------------------------------------------------
17 #pragma inline_depth (25)
20 //-----------------------------------------------------------------------------
22 //-----------------------------------------------------------------------------
24 // Have one internal, well-known, literal for the empty string.
25 const BYTE SString::s_EmptyBuffer[2] = { 0 };
27 // @todo: these need to be initialized by calling GetACP()
29 UINT SString::s_ACP = 0;
31 #ifndef DACCESS_COMPILE
32 static BYTE s_EmptySpace[sizeof(SString)] = { 0 };
33 #endif // DACCESS_COMPILE
35 SPTR_IMPL(SString,SString,s_Empty);
37 void SString::Startup()
39 STATIC_CONTRACT_NOTHROW;
40 STATIC_CONTRACT_GC_NOTRIGGER;
46 #ifndef DACCESS_COMPILE
47 s_Empty = PTR_SString(new (s_EmptySpace) SString());
48 s_Empty->SetNormalized();
49 #endif // DACCESS_COMPILE
56 CHECK SString::CheckStartup()
60 CHECK(s_Empty != NULL);
64 //-----------------------------------------------------------------------------
65 // Case insensitive helpers.
66 //-----------------------------------------------------------------------------
68 static WCHAR MapChar(WCHAR wc, DWORD dwFlags)
76 int iRet = ::LCMapStringEx(LOCALE_NAME_INVARIANT, dwFlags, &wc, 1, &wTmp, 1, NULL, NULL, 0);
78 // This can fail in non-exceptional cases becauseof unknown unicode characters.
83 // For PAL, no locale specific processing is done
85 if (dwFlags == LCMAP_UPPERCASE)
91 PAL_ToUpperInvariant(wc);
96 _ASSERTE(dwFlags == LCMAP_LOWERCASE);
101 PAL_ToLowerInvariant(wc);
104 #endif // !FEATURE_PAL
109 #define IS_UPPER_A_TO_Z(x) (((x) >= W('A')) && ((x) <= W('Z')))
110 #define IS_LOWER_A_TO_Z(x) (((x) >= W('a')) && ((x) <= W('z')))
111 #define CAN_SIMPLE_UPCASE(x) (((x)&~0x7f) == 0)
112 #define CAN_SIMPLE_DOWNCASE(x) (((x)&~0x7f) == 0)
113 #define SIMPLE_UPCASE(x) (IS_LOWER_A_TO_Z(x) ? ((x) - W('a') + W('A')) : (x))
114 #define SIMPLE_DOWNCASE(x) (IS_UPPER_A_TO_Z(x) ? ((x) - W('A') + W('a')) : (x))
117 int SString::CaseCompareHelper(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count, BOOL stopOnNull, BOOL stopOnCount)
119 LIMITED_METHOD_CONTRACT;
121 _ASSERTE(stopOnNull || stopOnCount);
123 const WCHAR *buffer1End = buffer1 + count;
126 while (!stopOnCount || (buffer1 < buffer1End))
128 WCHAR ch1 = *buffer1++;
129 WCHAR ch2 = *buffer2++;
131 if ((ch1 == 0) || (ch2 == 0))
133 if (diff != 0 || stopOnNull)
142 diff = ((CAN_SIMPLE_UPCASE(ch1) ? SIMPLE_UPCASE(ch1) : MapChar(ch1, LCMAP_UPPERCASE))
143 - (CAN_SIMPLE_UPCASE(ch2) ? SIMPLE_UPCASE(ch2) : MapChar(ch2, LCMAP_UPPERCASE)));
155 #define IS_LOWER_A_TO_Z_ANSI(x) (((x) >= 'a') && ((x) <= 'z'))
156 #define CAN_SIMPLE_UPCASE_ANSI(x) (((x) >= 0x20) && ((x) <= 0x7f))
157 #define SIMPLE_UPCASE_ANSI(x) (IS_LOWER_A_TO_Z(x) ? ((x) - 'a' + 'A') : (x))
159 int GetCaseInsensitiveValueA(const CHAR *buffer, int length) {
160 LIMITED_METHOD_CONTRACT;
161 _ASSERTE(buffer != NULL);
162 _ASSERTE(length == 1 || ((length == 2) && IsDBCSLeadByte(*buffer)));
166 int conversionReturn = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, buffer, length, &wideCh, 1);
167 if (conversionReturn == 0)
169 // An invalid sequence should only compare equal to itself, so use a negative mapping.
172 sortValue = -((int)((unsigned char)(*buffer)));
176 sortValue = -(((((int)((unsigned char)(*buffer))) << 8) | ((int)((unsigned char)(*(buffer + 1))))));
181 _ASSERTE(conversionReturn == 1);
182 sortValue = MapChar(wideCh, LCMAP_UPPERCASE);
188 int SString::CaseCompareHelperA(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count, BOOL stopOnNull, BOOL stopOnCount)
190 LIMITED_METHOD_CONTRACT;
192 _ASSERTE(stopOnNull || stopOnCount);
194 const CHAR *buffer1End = buffer1 + count;
197 while (!stopOnCount || (buffer1 < buffer1End))
201 if ((ch1 == 0) || (ch2 == 0))
204 if (diff != 0 || stopOnNull)
211 else if (CAN_SIMPLE_UPCASE_ANSI(ch1) && CAN_SIMPLE_UPCASE_ANSI(ch2))
216 diff = (SIMPLE_UPCASE_ANSI(ch1) - SIMPLE_UPCASE_ANSI(ch2));
228 if (IsDBCSLeadByte(ch1)
229 && IsDBCSLeadByte(ch2)
230 && (!stopOnCount || ((buffer1 + 1) < buffer1End)))
234 int sortValue1 = GetCaseInsensitiveValueA(buffer1, length);
235 int sortValue2 = GetCaseInsensitiveValueA(buffer2, length);
236 diff = sortValue1 - sortValue2;
249 int CaseHashHelper(const WCHAR *buffer, COUNT_T count)
251 LIMITED_METHOD_CONTRACT;
253 const WCHAR *bufferEnd = buffer + count;
256 while (buffer < bufferEnd)
258 WCHAR ch = *buffer++;
259 ch = CAN_SIMPLE_UPCASE(ch) ? SIMPLE_UPCASE(ch) : MapChar(ch, LCMAP_UPPERCASE);
261 hash = (((hash << 5) + hash) ^ ch);
267 static int CaseHashHelperA(const CHAR *buffer, COUNT_T count)
269 LIMITED_METHOD_CONTRACT;
271 const CHAR *bufferEnd = buffer + count;
274 while (buffer < bufferEnd)
277 ch = SIMPLE_UPCASE_ANSI(ch);
279 hash = (((hash << 5) + hash) ^ ch);
285 //-----------------------------------------------------------------------------
286 // Set this string to a copy of the unicode string
287 //-----------------------------------------------------------------------------
288 void SString::Set(const WCHAR *string)
293 PRECONDITION(CheckPointer(string, NULL_OK));
296 SUPPORTS_DAC_HOST_ONLY;
300 if (string == NULL || *string == 0)
304 Resize((COUNT_T) wcslen(string), REPRESENTATION_UNICODE);
305 wcscpy_s(GetRawUnicode(), GetBufferSizeInCharIncludeNullChar(), string);
311 //-----------------------------------------------------------------------------
312 // Set this string to a copy of the first count characters of the given
314 //-----------------------------------------------------------------------------
315 void SString::Set(const WCHAR *string, COUNT_T count)
320 PRECONDITION(CheckPointer(string, NULL_OK));
321 PRECONDITION(CheckCount(count));
331 Resize(count, REPRESENTATION_UNICODE);
332 wcsncpy_s(GetRawUnicode(), GetBufferSizeInCharIncludeNullChar(), string, count);
333 GetRawUnicode()[count] = 0;
339 //-----------------------------------------------------------------------------
340 // Set this string to a point to the first count characters of the given
341 // preallocated unicode string (shallow copy).
342 //-----------------------------------------------------------------------------
343 void SString::SetPreallocated(const WCHAR *string, COUNT_T count)
348 PRECONDITION(CheckPointer(string, NULL_OK));
349 PRECONDITION(CheckCount(count));
350 SS_POSTCONDITION(IsEmpty());
353 SUPPORTS_DAC_HOST_ONLY;
358 SetImmutable((BYTE*) string, count*2);
360 SetRepresentation(REPRESENTATION_UNICODE);
365 //-----------------------------------------------------------------------------
366 // Set this string to a copy of the given ansi string
367 //-----------------------------------------------------------------------------
368 void SString::SetASCII(const ASCII *string)
373 PRECONDITION(CheckPointer(string, NULL_OK));
374 PRECONDITION(CheckASCIIString(string));
380 if (string == NULL || *string == 0)
384 Resize((COUNT_T) strlen(string), REPRESENTATION_ASCII);
385 strcpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string);
391 //-----------------------------------------------------------------------------
392 // Set this string to a copy of the first count characters of the given
394 //-----------------------------------------------------------------------------
395 void SString::SetASCII(const ASCII *string, COUNT_T count)
400 PRECONDITION(CheckPointer(string, NULL_OK));
401 PRECONDITION(CheckASCIIString(string, count));
402 PRECONDITION(CheckCount(count));
412 Resize(count, REPRESENTATION_ASCII);
413 strncpy_s(GetRawASCII(), GetBufferSizeInCharIncludeNullChar(), string, count);
414 GetRawASCII()[count] = 0;
420 //-----------------------------------------------------------------------------
421 // Set this string to a copy of the given UTF8 string
422 //-----------------------------------------------------------------------------
423 void SString::SetUTF8(const UTF8 *string)
427 // !!! Check for illegal UTF8 encoding?
429 PRECONDITION(CheckPointer(string, NULL_OK));
432 SUPPORTS_DAC_HOST_ONLY;
436 if (string == NULL || *string == 0)
440 Resize((COUNT_T) strlen(string), REPRESENTATION_UTF8);
441 strcpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string);
447 //-----------------------------------------------------------------------------
448 // Set this string to a copy of the first count characters of the given
450 //-----------------------------------------------------------------------------
451 void SString::SetUTF8(const UTF8 *string, COUNT_T count)
455 // !!! Check for illegal UTF8 encoding?
457 PRECONDITION(CheckPointer(string, NULL_OK));
458 PRECONDITION(CheckCount(count));
468 Resize(count, REPRESENTATION_UTF8);
469 strncpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string, count);
470 GetRawUTF8()[count] = 0;
476 //-----------------------------------------------------------------------------
477 // Set this string to a copy of the given ANSI string
478 //-----------------------------------------------------------------------------
479 void SString::SetANSI(const ANSI *string)
484 PRECONDITION(CheckPointer(string, NULL_OK));
490 if (string == NULL || *string == 0)
494 Resize((COUNT_T) strlen(string), REPRESENTATION_ANSI);
495 strcpy_s(GetRawANSI(), GetBufferSizeInCharIncludeNullChar(), string);
501 //-----------------------------------------------------------------------------
502 // Set this string to a copy of the first count characters of the given
504 //-----------------------------------------------------------------------------
505 void SString::SetANSI(const ANSI *string, COUNT_T count)
510 PRECONDITION(CheckPointer(string, NULL_OK));
511 PRECONDITION(CheckCount(count));
521 Resize(count, REPRESENTATION_ANSI);
522 strncpy_s(GetRawANSI(), GetBufferSizeInCharIncludeNullChar(), string, count);
523 GetRawANSI()[count] = 0;
529 //-----------------------------------------------------------------------------
530 // Set this string to the given unicode character
531 //-----------------------------------------------------------------------------
532 void SString::Set(WCHAR character)
539 SUPPORTS_DAC_HOST_ONLY;
547 Resize(1, REPRESENTATION_UNICODE);
548 GetRawUnicode()[0] = character;
549 GetRawUnicode()[1] = 0;
555 //-----------------------------------------------------------------------------
556 // Set this string to the given UTF8 character
557 //-----------------------------------------------------------------------------
558 void SString::SetUTF8(CHAR character)
572 Resize(1, REPRESENTATION_UTF8);
573 GetRawUTF8()[0] = character;
581 //-----------------------------------------------------------------------------
582 // Set this string to the given ansi literal.
583 // This will share the memory and not make a copy.
584 //-----------------------------------------------------------------------------
585 void SString::SetLiteral(const ASCII *literal)
590 PRECONDITION(CheckPointer(literal));
591 PRECONDITION(CheckASCIIString(literal));
597 SString s(Literal, literal);
603 //-----------------------------------------------------------------------------
604 // Set this string to the given unicode literal.
605 // This will share the memory and not make a copy.
606 //-----------------------------------------------------------------------------
607 void SString::SetLiteral(const WCHAR *literal)
612 PRECONDITION(CheckPointer(literal));
618 SString s(Literal, literal);
624 //-----------------------------------------------------------------------------
625 // Hash the string contents
626 //-----------------------------------------------------------------------------
627 ULONG SString::Hash() const
632 THROWS_UNLESS_NORMALIZED;
639 SS_RETURN HashString(GetRawUnicode());
642 //-----------------------------------------------------------------------------
643 // Hash the string contents
644 //-----------------------------------------------------------------------------
645 ULONG SString::HashCaseInsensitive() const
650 THROWS_UNLESS_NORMALIZED;
655 ConvertToIteratable();
659 switch (GetRepresentation())
661 case REPRESENTATION_UNICODE:
662 case REPRESENTATION_EMPTY:
663 result = CaseHashHelper(GetRawUnicode(), GetRawCount());
666 case REPRESENTATION_ASCII:
667 result = CaseHashHelperA(GetRawASCII(), GetRawCount());
677 //-----------------------------------------------------------------------------
678 // Truncate this string to count characters.
679 //-----------------------------------------------------------------------------
680 void SString::Truncate(const Iterator &i)
685 PRECONDITION(CheckIteratorRange(i));
686 SS_POSTCONDITION(GetRawCount() == i - Begin());
689 SUPPORTS_DAC_HOST_ONLY;
693 CONSISTENCY_CHECK(IsFixedSize());
695 COUNT_T size = i - Begin();
697 Resize(size, GetRepresentation(), PRESERVE);
699 i.Resync(this, (BYTE *) (GetRawUnicode() + size));
704 //-----------------------------------------------------------------------------
705 // Convert the ASCII representation for this String to Unicode. We can do this
706 // quickly and in-place (if this == &dest), which is why it is optimized.
707 //-----------------------------------------------------------------------------
708 void SString::ConvertASCIIToUnicode(SString &dest) const
712 PRECONDITION(IsRepresentation(REPRESENTATION_ASCII));
713 POSTCONDITION(dest.IsRepresentation(REPRESENTATION_UNICODE));
716 SUPPORTS_DAC_HOST_ONLY;
720 // Handle the empty case.
727 CONSISTENCY_CHECK(CheckPointer(GetRawASCII()));
728 CONSISTENCY_CHECK(GetRawCount() > 0);
730 // If dest is the same as this, then we need to preserve on resize.
731 dest.Resize(GetRawCount(), REPRESENTATION_UNICODE,
732 this == &dest ? PRESERVE : DONT_PRESERVE);
734 // Make sure the buffer is big enough.
735 CONSISTENCY_CHECK(dest.GetAllocation() > (GetRawCount() * sizeof(WCHAR)));
737 // This is a poor man's widen. Since we know that the representation is ASCII,
738 // we can just pad the string with a bunch of zero-value bytes. Of course,
739 // we move from the end of the string to the start so that we can convert in
740 // place (in the case that &dest == this).
741 WCHAR *outBuf = dest.GetRawUnicode() + dest.GetRawCount();
742 ASCII *inBuf = GetRawASCII() + GetRawCount();
744 while (GetRawASCII() <= inBuf)
746 CONSISTENCY_CHECK(dest.GetRawUnicode() <= outBuf);
747 // The casting zero-extends the value, thus giving us the zero-valued byte.
748 *outBuf = (WCHAR) *inBuf;
756 //-----------------------------------------------------------------------------
757 // Convert the internal representation for this String to Unicode.
758 //-----------------------------------------------------------------------------
759 void SString::ConvertToUnicode() const
763 POSTCONDITION(IsRepresentation(REPRESENTATION_UNICODE));
764 if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
766 SUPPORTS_DAC_HOST_ONLY;
770 if (!IsRepresentation(REPRESENTATION_UNICODE))
772 if (IsRepresentation(REPRESENTATION_ASCII))
774 ConvertASCIIToUnicode(*(const_cast<SString *>(this)));
780 PREFIX_ASSUME(!s.IsImmutable());
781 (const_cast<SString*>(this))->Set(s);
788 //-----------------------------------------------------------------------------
789 // Convert the internal representation for this String to Unicode, while
790 // preserving the iterator if the conversion is done.
791 //-----------------------------------------------------------------------------
792 void SString::ConvertToUnicode(const CIterator &i) const
796 PRECONDITION(i.Check());
797 POSTCONDITION(IsRepresentation(REPRESENTATION_UNICODE));
798 if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
800 SUPPORTS_DAC_HOST_ONLY;
804 if (!IsRepresentation(REPRESENTATION_UNICODE))
806 CONSISTENCY_CHECK(IsFixedSize());
809 // Get the current index of the iterator
812 CONSISTENCY_CHECK(GetCharacterSizeShift() == 0);
813 index = (COUNT_T) (i.m_ptr - m_buffer);
816 if (IsRepresentation(REPRESENTATION_ASCII))
818 ConvertASCIIToUnicode(*(const_cast<SString *>(this)));
824 (const_cast<SString*>(this))->Set(s);
827 // Move the iterator to the new location.
830 i.Resync(this, (BYTE *) (GetRawUnicode() + index));
837 //-----------------------------------------------------------------------------
838 // Set s to be a copy of this string's contents, but in the unicode format.
839 //-----------------------------------------------------------------------------
840 void SString::ConvertToUnicode(SString &s) const
844 PRECONDITION(s.Check());
845 POSTCONDITION(s.IsRepresentation(REPRESENTATION_UNICODE));
848 SUPPORTS_DAC_HOST_ONLY;
854 switch (GetRepresentation())
856 case REPRESENTATION_EMPTY:
860 case REPRESENTATION_UNICODE:
864 case REPRESENTATION_UTF8:
868 case REPRESENTATION_ASCII:
869 ConvertASCIIToUnicode(s);
872 case REPRESENTATION_ANSI:
880 COUNT_T length = WszMultiByteToWideChar(page, 0, GetRawANSI(), GetRawCount()+1, 0, 0);
884 s.Resize(length-1, REPRESENTATION_UNICODE);
886 length = WszMultiByteToWideChar(page, 0, GetRawANSI(), GetRawCount()+1, s.GetRawUnicode(), length);
893 //-----------------------------------------------------------------------------
894 // Set s to be a copy of this string's contents, but in the ANSI format.
895 //-----------------------------------------------------------------------------
896 void SString::ConvertToANSI(SString &s) const
900 PRECONDITION(s.Check());
901 POSTCONDITION(s.IsRepresentation(REPRESENTATION_ANSI));
907 switch (GetRepresentation())
909 case REPRESENTATION_EMPTY:
913 case REPRESENTATION_ASCII:
914 case REPRESENTATION_ANSI:
918 case REPRESENTATION_UTF8:
919 // No direct conversion to ANSI
923 case REPRESENTATION_UNICODE:
930 // @todo: use WC_NO_BEST_FIT_CHARS
931 COUNT_T length = WszWideCharToMultiByte(CP_ACP, 0, GetRawUnicode(), GetRawCount()+1,
932 NULL, 0, NULL, NULL);
934 s.Resize(length-1, REPRESENTATION_ANSI);
936 // @todo: use WC_NO_BEST_FIT_CHARS
937 length = WszWideCharToMultiByte(CP_ACP, 0, GetRawUnicode(), GetRawCount()+1,
938 s.GetRawANSI(), length, NULL, NULL);
945 //-----------------------------------------------------------------------------
946 // Set s to be a copy of this string's contents, but in the utf8 format.
947 //-----------------------------------------------------------------------------
948 COUNT_T SString::ConvertToUTF8(SString &s) const
952 PRECONDITION(s.Check());
953 POSTCONDITION(s.IsRepresentation(REPRESENTATION_UTF8));
959 switch (GetRepresentation())
961 case REPRESENTATION_EMPTY:
965 case REPRESENTATION_ASCII:
966 case REPRESENTATION_UTF8:
968 RETURN s.GetRawCount()+1;
970 case REPRESENTATION_ANSI:
971 // No direct conversion from ANSI to UTF8
975 case REPRESENTATION_UNICODE:
982 // <TODO> @todo: use WC_NO_BEST_FIT_CHARS </TODO>
986 HRESULT hr = FString::Unicode_Utf8_Length(GetRawUnicode(), & allAscii, & length);
990 s.Resize(length, REPRESENTATION_UTF8);
992 //FString::Unicode_Utf8 expects an array all the time
993 //we optimize the empty string by replacing it with null for SString above in Resize
996 hr = FString::Unicode_Utf8(GetRawUnicode(), allAscii, (LPSTR) s.GetRawUTF8(), length);
1005 //-----------------------------------------------------------------------------
1006 // Replace a single character with another character.
1007 //-----------------------------------------------------------------------------
1008 void SString::Replace(const Iterator &i, WCHAR c)
1013 PRECONDITION(CheckIteratorRange(i, 1));
1014 POSTCONDITION(Match(i, c));
1020 if (IsRepresentation(REPRESENTATION_ASCII) && ((c&~0x7f) == 0))
1022 *(BYTE*)i.m_ptr = (BYTE) c;
1026 ConvertToUnicode(i);
1028 *(USHORT*)i.m_ptr = c;
1034 //-----------------------------------------------------------------------------
1035 // Replace the substring specified by position, length with the given string s.
1036 //-----------------------------------------------------------------------------
1037 void SString::Replace(const Iterator &i, COUNT_T length, const SString &s)
1042 PRECONDITION(CheckIteratorRange(i, length));
1043 PRECONDITION(s.Check());
1044 POSTCONDITION(Match(i, s));
1047 SUPPORTS_DAC_HOST_ONLY;
1051 Representation representation = GetRepresentation();
1052 if (representation == REPRESENTATION_EMPTY)
1054 // This special case contains some optimizations (like literal sharing).
1056 ConvertToIteratable();
1057 i.Resync(this, m_buffer);
1062 const SString &source = GetCompatibleString(s, temp, i);
1064 COUNT_T deleteSize = length<<GetCharacterSizeShift();
1065 COUNT_T insertSize = source.GetRawCount()<<source.GetCharacterSizeShift();
1067 SBuffer::Replace(i, deleteSize, insertSize);
1068 SBuffer::Copy(i, source.m_buffer, insertSize);
1074 //-----------------------------------------------------------------------------
1075 // Find s in this string starting at i. Return TRUE & update iterator if found.
1076 //-----------------------------------------------------------------------------
1077 BOOL SString::Find(CIterator &i, const SString &s) const
1082 PRECONDITION(CheckIteratorRange(i));
1083 PRECONDITION(s.Check());
1084 POSTCONDITION(RETVAL == Match(i, s));
1085 THROWS_UNLESS_BOTH_NORMALIZED(s);
1090 // Get a compatible string from s
1092 const SString &source = GetCompatibleString(s, temp, i);
1094 switch (GetRepresentation())
1096 case REPRESENTATION_UNICODE:
1098 COUNT_T count = source.GetRawCount();
1099 const WCHAR *start = i.GetUnicode();
1100 const WCHAR *end = GetUnicode() + GetRawCount() - count;
1101 while (start <= end)
1103 if (wcsncmp(start, source.GetRawUnicode(), count) == 0)
1105 i.Resync(this, (BYTE*) start);
1113 case REPRESENTATION_ANSI:
1114 case REPRESENTATION_ASCII:
1116 COUNT_T count = source.GetRawCount();
1117 const CHAR *start = i.GetASCII();
1118 const CHAR *end = GetRawASCII() + GetRawCount() - count;
1119 while (start <= end)
1121 if (strncmp(start, source.GetRawASCII(), count) == 0)
1123 i.Resync(this, (BYTE*) start);
1131 case REPRESENTATION_EMPTY:
1133 if (source.GetRawCount() == 0)
1138 case REPRESENTATION_UTF8:
1146 //-----------------------------------------------------------------------------
1147 // Find s in this string starting at i. Return TRUE & update iterator if found.
1148 //-----------------------------------------------------------------------------
1149 BOOL SString::Find(CIterator &i, WCHAR c) const
1154 PRECONDITION(CheckIteratorRange(i));
1155 POSTCONDITION(RETVAL == Match(i, c));
1156 THROWS_UNLESS_NORMALIZED;
1161 // Get a compatible string
1163 ConvertToUnicode(i);
1165 switch (GetRepresentation())
1167 case REPRESENTATION_UNICODE:
1169 const WCHAR *start = i.GetUnicode();
1170 const WCHAR *end = GetUnicode() + GetRawCount() - 1;
1171 while (start <= end)
1175 i.Resync(this, (BYTE*) start);
1183 case REPRESENTATION_ANSI:
1184 case REPRESENTATION_ASCII:
1186 const CHAR *start = i.GetASCII();
1187 const CHAR *end = GetRawASCII() + GetRawCount() - 1;
1188 while (start <= end)
1192 i.Resync(this, (BYTE*) start);
1200 case REPRESENTATION_EMPTY:
1203 case REPRESENTATION_UTF8:
1211 //-----------------------------------------------------------------------------
1212 // Find s in this string, working backwards staring at i.
1213 // Return TRUE and update iterator if found.
1214 //-----------------------------------------------------------------------------
1215 BOOL SString::FindBack(CIterator &i, const SString &s) const
1220 PRECONDITION(CheckIteratorRange(i));
1221 PRECONDITION(s.Check());
1222 POSTCONDITION(RETVAL == Match(i, s));
1223 THROWS_UNLESS_BOTH_NORMALIZED(s);
1228 // Get a compatible string from s
1230 const SString &source = GetCompatibleString(s, temp, i);
1232 switch (GetRepresentation())
1234 case REPRESENTATION_UNICODE:
1236 COUNT_T count = source.GetRawCount();
1237 const WCHAR *start = GetRawUnicode() + GetRawCount() - count;
1238 if (start > i.GetUnicode())
1239 start = i.GetUnicode();
1240 const WCHAR *end = GetRawUnicode();
1242 while (start >= end)
1244 if (wcsncmp(start, source.GetRawUnicode(), count) == 0)
1246 i.Resync(this, (BYTE*) start);
1254 case REPRESENTATION_ANSI:
1255 case REPRESENTATION_ASCII:
1257 COUNT_T count = source.GetRawCount();
1258 const CHAR *start = GetRawASCII() + GetRawCount() - count;
1259 if (start > i.GetASCII())
1260 start = i.GetASCII();
1261 const CHAR *end = GetRawASCII();
1263 while (start >= end)
1265 if (strncmp(start, source.GetRawASCII(), count) == 0)
1267 i.Resync(this, (BYTE*) start);
1275 case REPRESENTATION_EMPTY:
1277 if (source.GetRawCount() == 0)
1282 case REPRESENTATION_UTF8:
1290 //-----------------------------------------------------------------------------
1291 // Find s in this string, working backwards staring at i.
1292 // Return TRUE and update iterator if found.
1293 //-----------------------------------------------------------------------------
1294 BOOL SString::FindBack(CIterator &i, WCHAR c) const
1299 PRECONDITION(CheckIteratorRange(i));
1300 POSTCONDITION(RETVAL == Match(i, c));
1301 THROWS_UNLESS_NORMALIZED;
1306 // Get a compatible string from s
1308 ConvertToUnicode(i);
1310 switch (GetRepresentation())
1312 case REPRESENTATION_UNICODE:
1314 const WCHAR *start = GetRawUnicode() + GetRawCount() - 1;
1315 if (start > i.GetUnicode())
1316 start = i.GetUnicode();
1317 const WCHAR *end = GetRawUnicode();
1319 while (start >= end)
1323 i.Resync(this, (BYTE*) start);
1331 case REPRESENTATION_ANSI:
1332 case REPRESENTATION_ASCII:
1334 const CHAR *start = GetRawASCII() + GetRawCount() - 1;
1335 if (start > i.GetASCII())
1336 start = i.GetASCII();
1337 const CHAR *end = GetRawASCII();
1339 while (start >= end)
1343 i.Resync(this, (BYTE*) start);
1351 case REPRESENTATION_EMPTY:
1354 case REPRESENTATION_UTF8:
1362 //-----------------------------------------------------------------------------
1363 // Returns TRUE if this string begins with the contents of s
1364 //-----------------------------------------------------------------------------
1365 BOOL SString::BeginsWith(const SString &s) const
1367 WRAPPER_NO_CONTRACT;
1369 return Match(Begin(), s);
1372 //-----------------------------------------------------------------------------
1373 // Returns TRUE if this string begins with the contents of s
1374 //-----------------------------------------------------------------------------
1375 BOOL SString::BeginsWithCaseInsensitive(const SString &s) const
1377 WRAPPER_NO_CONTRACT;
1379 return MatchCaseInsensitive(Begin(), s);
1382 //-----------------------------------------------------------------------------
1383 // Returns TRUE if this string ends with the contents of s
1384 //-----------------------------------------------------------------------------
1385 BOOL SString::EndsWith(const SString &s) const
1387 WRAPPER_NO_CONTRACT;
1389 // Need this check due to iterator arithmetic below.
1390 if (GetCount() < s.GetCount())
1395 return Match(End() - s.GetCount(), s);
1398 //-----------------------------------------------------------------------------
1399 // Returns TRUE if this string ends with the contents of s
1400 //-----------------------------------------------------------------------------
1401 BOOL SString::EndsWithCaseInsensitive(const SString &s) const
1403 WRAPPER_NO_CONTRACT;
1405 // Need this check due to iterator arithmetic below.
1406 if (GetCount() < s.GetCount())
1411 return MatchCaseInsensitive(End() - s.GetCount(), s);
1414 //-----------------------------------------------------------------------------
1415 // Compare this string's contents to s's contents.
1416 // The comparison does not take into account localization issues like case folding.
1417 // Return 0 if equal, <0 if this < s, >0 is this > s. (same as strcmp).
1418 //-----------------------------------------------------------------------------
1419 int SString::Compare(const SString &s) const
1424 PRECONDITION(s.Check());
1425 THROWS_UNLESS_BOTH_NORMALIZED(s);
1431 const SString &source = GetCompatibleString(s, temp);
1437 if (GetRawCount() < source.GetRawCount())
1439 smaller = GetRawCount();
1442 else if (GetRawCount() > source.GetRawCount())
1444 smaller = source.GetRawCount();
1449 smaller = GetRawCount();
1453 switch (GetRepresentation())
1455 case REPRESENTATION_UNICODE:
1456 result = wcsncmp(GetRawUnicode(), source.GetRawUnicode(), smaller);
1459 case REPRESENTATION_ASCII:
1460 case REPRESENTATION_ANSI:
1461 result = strncmp(GetRawASCII(), source.GetRawASCII(), smaller);
1464 case REPRESENTATION_EMPTY:
1469 case REPRESENTATION_UTF8:
1479 //-----------------------------------------------------------------------------
1480 // Compare this string's contents to s's contents.
1481 // Return 0 if equal, <0 if this < s, >0 is this > s. (same as strcmp).
1482 //-----------------------------------------------------------------------------
1484 int SString::CompareCaseInsensitive(const SString &s) const
1489 PRECONDITION(s.Check());
1490 THROWS_UNLESS_BOTH_NORMALIZED(s);
1496 const SString &source = GetCompatibleString(s, temp);
1502 if (GetRawCount() < source.GetRawCount())
1504 smaller = GetRawCount();
1507 else if (GetRawCount() > source.GetRawCount())
1509 smaller = source.GetRawCount();
1514 smaller = GetRawCount();
1518 switch (GetRepresentation())
1520 case REPRESENTATION_UNICODE:
1521 result = CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), smaller, FALSE, TRUE);
1524 case REPRESENTATION_ASCII:
1525 case REPRESENTATION_ANSI:
1526 result = CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), smaller, FALSE, TRUE);
1529 case REPRESENTATION_EMPTY:
1534 case REPRESENTATION_UTF8:
1544 //-----------------------------------------------------------------------------
1545 // Compare this string's contents to s's contents.
1546 // The comparison does not take into account localization issues like case folding.
1547 // Return 1 if equal, 0 if not.
1548 //-----------------------------------------------------------------------------
1549 BOOL SString::Equals(const SString &s) const
1554 PRECONDITION(s.Check());
1555 THROWS_UNLESS_BOTH_NORMALIZED(s);
1556 FAULTS_UNLESS_BOTH_NORMALIZED(s, ThrowOutOfMemory());
1562 const SString &source = GetCompatibleString(s, temp);
1564 COUNT_T count = GetRawCount();
1566 if (count != source.GetRawCount())
1569 switch (GetRepresentation())
1571 case REPRESENTATION_UNICODE:
1572 RETURN (wcsncmp(GetRawUnicode(), source.GetRawUnicode(), count) == 0);
1574 case REPRESENTATION_ASCII:
1575 case REPRESENTATION_ANSI:
1576 RETURN (strncmp(GetRawASCII(), source.GetRawASCII(), count) == 0);
1578 case REPRESENTATION_EMPTY:
1582 case REPRESENTATION_UTF8:
1589 //-----------------------------------------------------------------------------
1590 // Compare this string's contents case insensitively to s's contents.
1591 // Return 1 if equal, 0 if not.
1592 //-----------------------------------------------------------------------------
1593 BOOL SString::EqualsCaseInsensitive(const SString &s) const
1598 PRECONDITION(s.Check());
1599 THROWS_UNLESS_BOTH_NORMALIZED(s);
1600 FAULTS_UNLESS_BOTH_NORMALIZED(s, ThrowOutOfMemory());
1606 const SString &source = GetCompatibleString(s, temp);
1608 COUNT_T count = GetRawCount();
1610 if (count != source.GetRawCount())
1613 switch (GetRepresentation())
1615 case REPRESENTATION_UNICODE:
1616 RETURN (CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), count, FALSE, TRUE) == 0);
1618 case REPRESENTATION_ASCII:
1619 case REPRESENTATION_ANSI:
1620 RETURN (CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), count, FALSE, TRUE) == 0);
1622 case REPRESENTATION_EMPTY:
1626 case REPRESENTATION_UTF8:
1633 //-----------------------------------------------------------------------------
1634 // Compare s's contents to the substring starting at position
1635 // The comparison does not take into account localization issues like case folding.
1636 // Return TRUE if equal, FALSE if not
1637 //-----------------------------------------------------------------------------
1638 BOOL SString::Match(const CIterator &i, const SString &s) const
1643 PRECONDITION(CheckIteratorRange(i));
1644 PRECONDITION(s.Check());
1645 THROWS_UNLESS_BOTH_NORMALIZED(s);
1651 const SString &source = GetCompatibleString(s, temp, i);
1653 COUNT_T remaining = End() - i;
1654 COUNT_T count = source.GetRawCount();
1656 if (remaining < count)
1659 switch (GetRepresentation())
1661 case REPRESENTATION_UNICODE:
1662 RETURN (wcsncmp(i.GetUnicode(), source.GetRawUnicode(), count) == 0);
1664 case REPRESENTATION_ASCII:
1665 case REPRESENTATION_ANSI:
1666 RETURN (strncmp(i.GetASCII(), source.GetRawASCII(), count) == 0);
1668 case REPRESENTATION_EMPTY:
1672 case REPRESENTATION_UTF8:
1679 //-----------------------------------------------------------------------------
1680 // Compare s's contents case insensitively to the substring starting at position
1681 // Return TRUE if equal, FALSE if not
1682 //-----------------------------------------------------------------------------
1683 BOOL SString::MatchCaseInsensitive(const CIterator &i, const SString &s) const
1688 PRECONDITION(CheckIteratorRange(i));
1689 PRECONDITION(s.Check());
1690 THROWS_UNLESS_BOTH_NORMALIZED(s);
1696 const SString &source = GetCompatibleString(s, temp, i);
1698 COUNT_T remaining = End() - i;
1699 COUNT_T count = source.GetRawCount();
1701 if (remaining < count)
1704 switch (GetRepresentation())
1706 case REPRESENTATION_UNICODE:
1707 case REPRESENTATION_ANSI:
1708 RETURN (CaseCompareHelper(i.GetUnicode(), source.GetRawUnicode(), count, FALSE, TRUE) == 0);
1710 case REPRESENTATION_ASCII:
1711 RETURN (CaseCompareHelperA(i.GetASCII(), source.GetRawASCII(), count, FALSE, TRUE) == 0);
1713 case REPRESENTATION_EMPTY:
1717 case REPRESENTATION_UTF8:
1724 //-----------------------------------------------------------------------------
1725 // Compare c case insensitively to the character at position
1726 // Return TRUE if equal, FALSE if not
1727 //-----------------------------------------------------------------------------
1728 BOOL SString::MatchCaseInsensitive(const CIterator &i, WCHAR c) const
1734 PRECONDITION(CheckIteratorRange(i));
1739 // End() will not throw here
1740 CONTRACT_VIOLATION(ThrowsViolation);
1746 SS_RETURN (test == c
1747 || ((CAN_SIMPLE_UPCASE(test) ? SIMPLE_UPCASE(test) : MapChar(test, LCMAP_UPPERCASE))
1748 == (CAN_SIMPLE_UPCASE(c) ? SIMPLE_UPCASE(c) : MapChar(c, LCMAP_UPPERCASE))));
1751 //-----------------------------------------------------------------------------
1752 // Convert string to unicode lowercase using the invariant culture
1753 // Note: Please don't use it in PATH as multiple character can map to the same
1754 // lower case symbol
1755 //-----------------------------------------------------------------------------
1756 void SString::LowerCase()
1761 PRECONDITION(CheckPointer(this));
1762 SS_POSTCONDITION(CheckPointer(RETVAL));
1763 if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
1770 for (WCHAR *pwch = GetRawUnicode(); pwch < GetRawUnicode() + GetRawCount(); ++pwch)
1772 *pwch = (CAN_SIMPLE_DOWNCASE(*pwch) ? SIMPLE_DOWNCASE(*pwch) : MapChar(*pwch, LCMAP_LOWERCASE));
1776 //-----------------------------------------------------------------------------
1777 // Convert null-terminated string to lowercase using the invariant culture
1778 //-----------------------------------------------------------------------------
1780 void SString::LowerCase(__inout_z LPWSTR wszString)
1790 if (wszString == NULL)
1795 for (WCHAR * pwch = wszString; *pwch != '\0'; ++pwch)
1797 *pwch = (CAN_SIMPLE_DOWNCASE(*pwch) ? SIMPLE_DOWNCASE(*pwch) : MapChar(*pwch, LCMAP_LOWERCASE));
1801 //-----------------------------------------------------------------------------
1802 // Convert string to unicode uppercase using the invariant culture
1803 // Note: Please don't use it in PATH as multiple character can map to the same
1804 // upper case symbol
1805 //-----------------------------------------------------------------------------
1806 void SString::UpperCase()
1811 PRECONDITION(CheckPointer(this));
1812 SS_POSTCONDITION(CheckPointer(RETVAL));
1813 if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
1821 for (WCHAR *pwch = GetRawUnicode(); pwch < GetRawUnicode() + GetRawCount(); ++pwch)
1823 *pwch = (CAN_SIMPLE_UPCASE(*pwch) ? SIMPLE_UPCASE(*pwch) : MapChar(*pwch, LCMAP_UPPERCASE));
1827 //-----------------------------------------------------------------------------
1828 // Get a const pointer to the internal buffer as an ANSI string.
1829 //-----------------------------------------------------------------------------
1830 const CHAR *SString::GetANSI(AbstractScratchBuffer &scratch) const
1832 SS_CONTRACT(const CHAR *)
1834 INSTANCE_CHECK_NULL;
1843 if (IsRepresentation(REPRESENTATION_ANSI))
1844 SS_RETURN GetRawANSI();
1846 ConvertToANSI((SString&)scratch);
1847 SS_RETURN ((SString&)scratch).GetRawANSI();
1850 //-----------------------------------------------------------------------------
1851 // Get a const pointer to the internal buffer as a UTF8 string.
1852 //-----------------------------------------------------------------------------
1853 const UTF8 *SString::GetUTF8(AbstractScratchBuffer &scratch) const
1855 CONTRACT(const UTF8 *)
1857 INSTANCE_CHECK_NULL;
1866 if (IsRepresentation(REPRESENTATION_UTF8))
1867 RETURN GetRawUTF8();
1869 ConvertToUTF8((SString&)scratch);
1870 RETURN ((SString&)scratch).GetRawUTF8();
1873 const UTF8 *SString::GetUTF8(AbstractScratchBuffer &scratch, COUNT_T *pcbUtf8) const
1875 CONTRACT(const UTF8 *)
1877 INSTANCE_CHECK_NULL;
1886 if (IsRepresentation(REPRESENTATION_UTF8))
1888 *pcbUtf8 = GetRawCount() + 1;
1889 RETURN GetRawUTF8();
1892 *pcbUtf8 = ConvertToUTF8((SString&)scratch);
1893 RETURN ((SString&)scratch).GetRawUTF8();
1896 //-----------------------------------------------------------------------------
1897 // Get a const pointer to the internal buffer which must already be a UTF8 string.
1898 // This avoids the need to create a scratch buffer we know will never be used.
1899 //-----------------------------------------------------------------------------
1900 const UTF8 *SString::GetUTF8NoConvert() const
1902 CONTRACT(const UTF8 *)
1904 INSTANCE_CHECK_NULL;
1913 if (IsRepresentation(REPRESENTATION_UTF8))
1914 RETURN GetRawUTF8();
1916 ThrowHR(E_INVALIDARG);
1919 //-----------------------------------------------------------------------------
1920 // Safe version of sprintf.
1921 // Prints formatted ansi text w/ var args to this buffer.
1922 //-----------------------------------------------------------------------------
1923 void SString::Printf(const CHAR *format, ...)
1925 WRAPPER_NO_CONTRACT;
1928 va_start(args, format);
1929 VPrintf(format, args);
1935 // Check the Printf use for potential globalization bugs. %S formatting
1936 // specifier does Unicode->Ansi or Ansi->Unicode conversion using current
1937 // C-locale. This almost always means globalization bug in the CLR codebase.
1939 // Ideally, we would elimitate %S from all format strings. Unfortunately,
1940 // %S is too widespread in non-shipping code that such cleanup is not feasible.
1942 static void CheckForFormatStringGlobalizationIssues(const SString &format, const SString &result)
1952 BOOL fDangerousFormat = FALSE;
1954 // Check whether the format string contains the %S formatting specifier
1955 SString::CIterator itrFormat = format.Begin();
1958 if (*itrFormat++ == '%')
1960 // <TODO>Handle the complex format strings like %blahS</TODO>
1961 if (*itrFormat++ == 'S')
1963 fDangerousFormat = TRUE;
1969 if (fDangerousFormat)
1971 BOOL fNonAsciiUsed = FALSE;
1973 // Now check whether there are any non-ASCII characters in the output.
1975 // Check whether the result contains non-Ascii characters
1976 SString::CIterator itrResult = format.Begin();
1979 if (*itrResult++ > 127)
1981 fNonAsciiUsed = TRUE;
1986 CONSISTENCY_CHECK_MSGF(!fNonAsciiUsed,
1987 ("Non-ASCII string was produced by %%S format specifier. This is likely globalization bug."
1988 "To fix this, change the format string to %%s and do the correct encoding at the Printf callsite"));
2005 #if defined(_MSC_VER)
2007 #define va_copy(dest,src) (dest = src)
2010 void SString::VPrintf(const CHAR *format, va_list args)
2015 PRECONDITION(CheckPointer(format));
2022 // sprintf gives us no means to know how many characters are written
2023 // other than guessing and trying
2025 if (GetRawCount() > 0)
2027 // First, try to use the existing buffer
2029 int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, ap);
2034 // Succeeded in writing. Now resize -
2035 Resize(result, REPRESENTATION_ANSI, PRESERVE);
2036 SString sss(Ansi, format);
2037 INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
2042 // Make a guess how long the result will be (note this will be doubled)
2044 COUNT_T guess = (COUNT_T) strlen(format)+1;
2045 if (guess < GetRawCount())
2046 guess = GetRawCount();
2047 if (guess < MINIMUM_GUESS)
2048 guess = MINIMUM_GUESS;
2052 // Double the previous guess - eventually we will get enough space
2054 Resize(guess, REPRESENTATION_ANSI);
2056 // Clear errno to avoid false alarms
2060 int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, ap);
2065 // Succeed in writing. Shrink the buffer to fit exactly.
2066 Resize(result, REPRESENTATION_ANSI, PRESERVE);
2067 SString sss(Ansi, format);
2068 INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
2077 if (errno!=0 && errno!=EBADF && errno!=ERANGE)
2079 CONSISTENCY_CHECK_MSG(FALSE, "_vsnprintf_s failed. Potential globalization bug.");
2080 ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
2086 void SString::Printf(const WCHAR *format, ...)
2088 WRAPPER_NO_CONTRACT;
2091 va_start(args, format);
2092 VPrintf(format, args);
2096 void SString::PPrintf(const WCHAR *format, ...)
2101 PRECONDITION(CheckPointer(format));
2108 va_start(argItr, format);
2109 PVPrintf(format, argItr);
2115 void SString::VPrintf(const WCHAR *format, va_list args)
2120 PRECONDITION(CheckPointer(format));
2127 // sprintf gives us no means to know how many characters are written
2128 // other than guessing and trying
2130 if (GetRawCount() > 0)
2132 // First, try to use the existing buffer
2134 int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
2140 Resize(result, REPRESENTATION_UNICODE, PRESERVE);
2141 SString sss(format);
2142 INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
2147 // Make a guess how long the result will be (note this will be doubled)
2149 COUNT_T guess = (COUNT_T) wcslen(format)+1;
2150 if (guess < GetRawCount())
2151 guess = GetRawCount();
2152 if (guess < MINIMUM_GUESS)
2153 guess = MINIMUM_GUESS;
2157 // Double the previous guess - eventually we will get enough space
2159 Resize(guess, REPRESENTATION_UNICODE);
2161 // Clear errno to avoid false alarms
2165 int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
2170 Resize(result, REPRESENTATION_UNICODE, PRESERVE);
2171 SString sss(format);
2172 INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
2181 if (errno!=0 && errno!=EBADF && errno!=ERANGE)
2183 CONSISTENCY_CHECK_MSG(FALSE, "_vsnwprintf_s failed. Potential globalization bug.");
2184 ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
2190 void SString::PVPrintf(const WCHAR *format, va_list args)
2195 PRECONDITION(CheckPointer(format));
2202 // sprintf gives us no means to know how many characters are written
2203 // other than guessing and trying
2205 if (GetRawCount() > 0)
2207 // First, try to use the existing buffer
2209 #if defined(FEATURE_CORESYSTEM)
2210 int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
2212 int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, ap);
2218 Resize(result, REPRESENTATION_UNICODE, PRESERVE);
2219 SString sss(format);
2220 INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
2225 // Make a guess how long the result will be (note this will be doubled)
2227 COUNT_T guess = (COUNT_T) wcslen(format)+1;
2228 if (guess < GetRawCount())
2229 guess = GetRawCount();
2230 if (guess < MINIMUM_GUESS)
2231 guess = MINIMUM_GUESS;
2235 // Double the previous guess - eventually we will get enough space
2237 Resize(guess, REPRESENTATION_UNICODE, DONT_PRESERVE);
2239 // Clear errno to avoid false alarms
2243 #if defined(FEATURE_CORESYSTEM)
2244 int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
2246 int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, ap);
2252 Resize(result, REPRESENTATION_UNICODE, PRESERVE);
2253 SString sss(format);
2254 INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
2263 if (errno!=0 && errno!=EBADF && errno!=ERANGE)
2265 CONSISTENCY_CHECK_MSG(FALSE, "_vsnwprintf_s failed. Potential globalization bug.");
2266 ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
2272 void SString::AppendPrintf(const CHAR *format, ...)
2274 WRAPPER_NO_CONTRACT;
2277 va_start(args, format);
2278 AppendVPrintf(format, args);
2282 void SString::AppendVPrintf(const CHAR *format, va_list args)
2284 WRAPPER_NO_CONTRACT;
2287 s.VPrintf(format, args);
2291 void SString::AppendPrintf(const WCHAR *format, ...)
2293 WRAPPER_NO_CONTRACT;
2296 va_start(args, format);
2297 AppendVPrintf(format, args);
2301 void SString::AppendVPrintf(const WCHAR *format, va_list args)
2303 WRAPPER_NO_CONTRACT;
2306 s.VPrintf(format, args);
2310 //----------------------------------------------------------------------------
2311 // LoadResource - moved to sstring_com.cpp
2312 //----------------------------------------------------------------------------
2314 //----------------------------------------------------------------------------
2315 // Format the message and put the contents in this string
2316 //----------------------------------------------------------------------------
2318 BOOL SString::FormatMessage(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId,
2319 const SString &arg1, const SString &arg2,
2320 const SString &arg3, const SString &arg4,
2321 const SString &arg5, const SString &arg6,
2322 const SString &arg7, const SString &arg8,
2323 const SString &arg9, const SString &arg10)
2333 const WCHAR *args[] = {arg1.GetUnicode(), arg2.GetUnicode(), arg3.GetUnicode(), arg4.GetUnicode(),
2334 arg5.GetUnicode(), arg6.GetUnicode(), arg7.GetUnicode(), arg8.GetUnicode(),
2335 arg9.GetUnicode(), arg10.GetUnicode()};
2337 if (GetRawCount() > 0)
2339 // First, try to use our existing buffer to hold the result.
2340 Resize(GetRawCount(), REPRESENTATION_UNICODE);
2342 DWORD result = ::WszFormatMessage(dwFlags | FORMAT_MESSAGE_ARGUMENT_ARRAY,
2343 lpSource, dwMessageId, dwLanguageId,
2344 GetRawUnicode(), GetRawCount()+1, (va_list*)args);
2346 // Although we cannot directly detect truncation, we can tell if we
2347 // used up all the space (in which case we will assume truncation.)
2349 if (result != 0 && result < GetRawCount())
2351 if (GetRawUnicode()[result-1] == W(' '))
2353 GetRawUnicode()[result-1] = W('\0');
2356 Resize(result, REPRESENTATION_UNICODE, PRESERVE);
2361 // We don't have enough space in our buffer, do dynamic allocation.
2362 LocalAllocHolder<WCHAR> string;
2364 DWORD result = ::WszFormatMessage(dwFlags | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
2365 lpSource, dwMessageId, dwLanguageId,
2366 (LPWSTR)(LPWSTR*)&string, 0, (va_list*)args);
2372 if (string[result-1] == W(' '))
2373 string[result-1] = W('\0');
2381 //----------------------------------------------------------------------------
2383 //----------------------------------------------------------------------------
2385 // @todo -this should be removed and placed outside of SString
2386 void SString::MakeFullNamespacePath(const SString &nameSpace, const SString &name)
2396 if (nameSpace.GetRepresentation() == REPRESENTATION_UTF8
2397 && name.GetRepresentation() == REPRESENTATION_UTF8)
2399 const UTF8 *ns = nameSpace.GetRawUTF8();
2400 const UTF8 *n = name.GetRawUTF8();
2401 COUNT_T count = ns::GetFullLength(ns, n)-1;
2402 Resize(count, REPRESENTATION_UTF8);
2404 ns::MakePath(GetRawUTF8(), count+1, ns, n);
2408 const WCHAR *ns = nameSpace;
2409 const WCHAR *n = name;
2410 COUNT_T count = ns::GetFullLength(ns, n)-1;
2411 Resize(count, REPRESENTATION_UNICODE);
2413 ns::MakePath(GetRawUnicode(), count+1, ns, n);
2422 //----------------------------------------------------------------------------
2424 // Check to see if the string fits the suggested representation
2425 //----------------------------------------------------------------------------
2426 BOOL SString::IsRepresentation(Representation representation) const
2430 PRECONDITION(CheckRepresentation(representation));
2437 Representation currentRepresentation = GetRepresentation();
2439 // If representations are the same, cool.
2440 if (currentRepresentation == representation)
2443 // If we have an empty representation, we match everything
2444 if (currentRepresentation == REPRESENTATION_EMPTY)
2447 // If we're a 1 byte charset, there are some more chances to match
2448 if (currentRepresentation != REPRESENTATION_UNICODE
2449 && representation != REPRESENTATION_UNICODE)
2451 // If we're ASCII, we can be any 1 byte rep
2452 if (currentRepresentation == REPRESENTATION_ASCII)
2455 // We really want to be ASCII - scan to see if we qualify
2460 // Sorry, must convert.
2464 //----------------------------------------------------------------------------
2466 // Get the contents of the given string in a form which is compatible with our
2467 // string (and is in a fixed character set.) Updates the given iterator
2468 // if necessary to keep it in sync.
2469 //----------------------------------------------------------------------------
2470 const SString &SString::GetCompatibleString(const SString &s, SString &scratch, const CIterator &i) const
2474 PRECONDITION(s.Check());
2475 PRECONDITION(scratch.Check());
2476 PRECONDITION(scratch.CheckEmpty());
2477 THROWS_UNLESS_BOTH_NORMALIZED(s);
2483 // Since we have an iterator, we should be fixed size already
2484 CONSISTENCY_CHECK(IsFixedSize());
2486 switch (GetRepresentation())
2488 case REPRESENTATION_EMPTY:
2491 case REPRESENTATION_ASCII:
2492 if (s.IsRepresentation(REPRESENTATION_ASCII))
2495 // We can't in general convert to ASCII, so try unicode.
2496 ConvertToUnicode(i);
2499 case REPRESENTATION_UNICODE:
2500 if (s.IsRepresentation(REPRESENTATION_UNICODE))
2503 // @todo: we could convert s to unicode - is that a good policy????
2504 s.ConvertToUnicode(scratch);
2507 case REPRESENTATION_UTF8:
2508 case REPRESENTATION_ANSI:
2509 // These should all be impossible since we have an CIterator on us.
2511 UNREACHABLE_MSG("Unexpected string representation");
2517 //----------------------------------------------------------------------------
2519 // Get the contents of the given string in a form which is compatible with our
2520 // string (and is in a fixed character set.)
2521 // May convert our string to unicode.
2522 //----------------------------------------------------------------------------
2523 const SString &SString::GetCompatibleString(const SString &s, SString &scratch) const
2527 PRECONDITION(s.Check());
2528 PRECONDITION(scratch.Check());
2529 PRECONDITION(scratch.CheckEmpty());
2530 THROWS_UNLESS_BOTH_NORMALIZED(s);
2535 // First, make sure we have a fixed size.
2538 switch (GetRepresentation())
2540 case REPRESENTATION_EMPTY:
2543 case REPRESENTATION_ANSI:
2544 if (s.IsRepresentation(REPRESENTATION_ANSI))
2547 s.ConvertToANSI(scratch);
2550 case REPRESENTATION_ASCII:
2551 if (s.IsRepresentation(REPRESENTATION_ASCII))
2554 // We can't in general convert to ASCII, so try unicode.
2558 case REPRESENTATION_UNICODE:
2559 if (s.IsRepresentation(REPRESENTATION_UNICODE))
2562 // @todo: we could convert s to unicode in place - is that a good policy????
2563 s.ConvertToUnicode(scratch);
2566 case REPRESENTATION_UTF8:
2574 //----------------------------------------------------------------------------
2576 // If we have a 1 byte representation, scan the buffer to see if we can gain
2577 // some conversion flexibility by labelling it ASCII
2578 //----------------------------------------------------------------------------
2579 BOOL SString::ScanASCII() const
2583 POSTCONDITION(IsRepresentation(REPRESENTATION_ASCII) || IsASCIIScanned());
2590 if (!IsASCIIScanned())
2592 const CHAR *c = GetRawANSI();
2593 const CHAR *cEnd = c + GetRawCount();
2602 const_cast<SString *>(this)->SetRepresentation(REPRESENTATION_ASCII);
2606 const_cast<SString *>(this)->SetASCIIScanned();
2611 //----------------------------------------------------------------------------
2613 // Resize updates the geometry of the string and ensures that
2614 // the space can be written to.
2615 // count - number of characters (not including null) to hold
2616 // preserve - if we realloc, do we copy data from old to new?
2617 //----------------------------------------------------------------------------
2619 void SString::Resize(COUNT_T count, SString::Representation representation, Preserve preserve)
2623 PRECONDITION(CountToSize(count) >= count);
2624 POSTCONDITION(IsRepresentation(representation));
2625 POSTCONDITION(GetRawCount() == count);
2626 if (count == 0) NOTHROW; else THROWS;
2628 SUPPORTS_DAC_HOST_ONLY;
2632 // If we are resizing to zero, Clear is more efficient
2639 SetRepresentation(representation);
2641 COUNT_T size = CountToSize(count);
2649 SBuffer::Resize(size, preserve);
2660 //-----------------------------------------------------------------------------
2661 // This is essentially a specialized version of the above for size 0
2662 //-----------------------------------------------------------------------------
2663 void SString::Clear()
2668 POSTCONDITION(IsEmpty());
2671 SUPPORTS_DAC_HOST_ONLY;
2675 SetRepresentation(REPRESENTATION_EMPTY);
2679 // Use shared empty string rather than allocating a new buffer
2680 SBuffer::SetImmutable(s_EmptyBuffer, sizeof(s_EmptyBuffer));
2684 // Leave allocated buffer for future growth
2685 SBuffer::TweakSize(sizeof(WCHAR));
2686 GetRawUnicode()[0] = 0;
2693 #ifdef DACCESS_COMPILE
2695 //---------------------------------------------------------------------------------------
2697 // Return a pointer to the raw buffer
2700 // A pointer to the raw string buffer.
2702 void * SString::DacGetRawContent() const
2709 switch (GetRepresentation())
2711 case REPRESENTATION_EMPTY:
2714 case REPRESENTATION_UNICODE:
2715 case REPRESENTATION_UTF8:
2716 case REPRESENTATION_ASCII:
2717 case REPRESENTATION_ANSI:
2718 // Note: no need to call DacInstantiateString because we know the exact length already.
2719 return SBuffer::DacGetRawContent();
2727 //---------------------------------------------------------------------------------------
2729 // Return a pointer to the raw buffer as a pointer to a unicode string. Does not
2730 // do conversion, and thus requires that the representation already be in unicode.
2733 // A pointer to the raw string buffer as a unicode string.
2735 const WCHAR * SString::DacGetRawUnicode() const
2737 if (IsEmpty() || (GetRepresentation() == REPRESENTATION_EMPTY))
2742 if (GetRepresentation() != REPRESENTATION_UNICODE)
2744 DacError(E_UNEXPECTED);
2747 HRESULT status = S_OK;
2748 WCHAR* wszBuf = NULL;
2751 wszBuf = static_cast<WCHAR*>(SBuffer::DacGetRawContent());
2753 EX_CATCH_HRESULT(status);
2755 if (SUCCEEDED(status))
2765 //---------------------------------------------------------------------------------------
2767 // Copy the string from the target into the provided buffer, converting to unicode if necessary
2770 // cBufChars - size of pBuffer in count of unicode characters.
2771 // pBuffer - a buffer of cBufChars unicode chars.
2772 // pcNeedChars - space to store the number of unicode chars in the SString.
2775 // true if successful - and buffer is filled with the unicode representation of
2777 // false if unsuccessful.
2779 bool SString::DacGetUnicode(COUNT_T cBufChars,
2780 __out_z __inout_ecount(cBufChars) WCHAR * pBuffer,
2781 COUNT_T * pcNeedChars) const
2785 PVOID pContent = NULL;
2788 if (IsEmpty() || (GetRepresentation() == REPRESENTATION_EMPTY))
2794 if (pBuffer && cBufChars)
2801 HRESULT status = S_OK;
2804 pContent = SBuffer::DacGetRawContent();
2806 EX_CATCH_HRESULT(status);
2808 if (SUCCEEDED(status) && pContent != NULL)
2810 switch (GetRepresentation())
2813 case REPRESENTATION_UNICODE:
2817 *pcNeedChars = GetCount() + 1;
2820 if (pBuffer && cBufChars)
2822 if (cBufChars > GetCount() + 1)
2824 cBufChars = GetCount() + 1;
2826 memcpy(pBuffer, pContent, cBufChars * sizeof(*pBuffer));
2827 pBuffer[cBufChars - 1] = 0;
2832 case REPRESENTATION_UTF8:
2834 case REPRESENTATION_ASCII:
2835 case REPRESENTATION_ANSI:
2836 // iPage defaults to CP_ACP.
2839 *pcNeedChars = WszMultiByteToWideChar(iPage, 0, reinterpret_cast<PSTR>(pContent), -1, NULL, 0);
2841 if (pBuffer && cBufChars)
2843 if (!WszMultiByteToWideChar(iPage, 0, reinterpret_cast<PSTR>(pContent), -1, pBuffer, cBufChars))
2858 #endif //DACCESS_COMPILE