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//*****************************************************************************
5// NamespaceUtil.cpp
6//
7
8//
9// Helpers for converting namespace separators.
10//
11//*****************************************************************************
12#include "stdafx.h"
13#include "corhdr.h"
14#include "corhlpr.h"
15#include "sstring.h"
16#include "utilcode.h"
17
18#ifndef _ASSERTE
19#define _ASSERTE(foo)
20#endif
21
22#include "nsutilpriv.h"
23
24
25//*****************************************************************************
26// Determine how many chars large a fully qualified name would be given the
27// two parts of the name. The return value includes room for every character
28// in both names, as well as room for the separator and a final terminator.
29//*****************************************************************************
30int ns::GetFullLength( // Number of chars in full name.
31 const WCHAR *szNameSpace, // Namspace for value.
32 const WCHAR *szName) // Name of value.
33{
34 STATIC_CONTRACT_NOTHROW;
35 STATIC_CONTRACT_GC_NOTRIGGER;
36 STATIC_CONTRACT_FORBID_FAULT;
37
38 int iLen = 1; // Null terminator.
39 if (szNameSpace)
40 iLen += (int)wcslen(szNameSpace);
41 if (szName)
42 iLen += (int)wcslen(szName);
43 if (szNameSpace && *szNameSpace && szName && *szName)
44 ++iLen;
45 return iLen;
46} //int ns::GetFullLength()
47
48int ns::GetFullLength( // Number of chars in full name.
49 LPCUTF8 szNameSpace, // Namspace for value.
50 LPCUTF8 szName) // Name of value.
51{
52 STATIC_CONTRACT_NOTHROW;
53 STATIC_CONTRACT_GC_NOTRIGGER;
54 STATIC_CONTRACT_FORBID_FAULT;
55
56
57 int iLen = 1;
58 if (szNameSpace)
59 iLen += (int)strlen(szNameSpace);
60 if (szName)
61 iLen += (int)strlen(szName);
62 if (szNameSpace && *szNameSpace && szName && *szName)
63 ++iLen;
64 return iLen;
65} //int ns::GetFullLength()
66
67
68//*****************************************************************************
69// Scan the string from the rear looking for the first valid separator. If
70// found, return a pointer to it. Else return null. This code is smart enough
71// to skip over special sequences, such as:
72// a.b..ctor
73// ^
74// |
75// The ".ctor" is considered one token.
76//*****************************************************************************
77WCHAR *ns::FindSep( // Pointer to separator or null.
78 const WCHAR *szPath) // The path to look in.
79{
80 STATIC_CONTRACT_NOTHROW;
81 STATIC_CONTRACT_GC_NOTRIGGER;
82 STATIC_CONTRACT_FORBID_FAULT;
83
84 _ASSERTE(szPath);
85 WCHAR *ptr = (WCHAR*)wcsrchr(szPath, NAMESPACE_SEPARATOR_WCHAR);
86 if((ptr == NULL) || (ptr == szPath)) return NULL;
87 if(*(ptr - 1) == NAMESPACE_SEPARATOR_WCHAR) // here ptr is at least szPath+1
88 --ptr;
89 return ptr;
90} //WCHAR *ns::FindSep()
91
92//<TODO>@todo: this isn't dbcs safe if this were ansi, but this is utf8. Still an issue?</TODO>
93LPUTF8 ns::FindSep( // Pointer to separator or null.
94 LPCUTF8 szPath) // The path to look in.
95{
96 STATIC_CONTRACT_NOTHROW;
97 STATIC_CONTRACT_GC_NOTRIGGER;
98 STATIC_CONTRACT_FORBID_FAULT;
99 STATIC_CONTRACT_SUPPORTS_DAC;
100
101 _ASSERTE(szPath);
102 LPUTF8 ptr = const_cast<LPUTF8>(strrchr(szPath, NAMESPACE_SEPARATOR_CHAR));
103 if((ptr == NULL) || (ptr == szPath)) return NULL;
104 if(*(ptr - 1) == NAMESPACE_SEPARATOR_CHAR) // here ptr is at least szPath+1
105 --ptr;
106 return ptr;
107} //LPUTF8 ns::FindSep()
108
109
110
111//*****************************************************************************
112// Take a path and find the last separator (nsFindSep), and then replace the
113// separator with a '\0' and return a pointer to the name. So for example:
114// a.b.c
115// becomes two strings "a.b" and "c" and the return value points to "c".
116//*****************************************************************************
117WCHAR *ns::SplitInline( // Pointer to name portion.
118 __inout __inout_z WCHAR *szPath) // The path to split.
119{
120 STATIC_CONTRACT_NOTHROW;
121 STATIC_CONTRACT_GC_NOTRIGGER;
122 STATIC_CONTRACT_FORBID_FAULT;
123
124 WCHAR *ptr = ns::FindSep(szPath);
125 if (ptr)
126 {
127 *ptr = 0;
128 ++ptr;
129 }
130 return ptr;
131} // WCHAR *ns::SplitInline()
132
133LPUTF8 ns::SplitInline( // Pointer to name portion.
134 __inout __inout_z LPUTF8 szPath) // The path to split.
135{
136 STATIC_CONTRACT_NOTHROW;
137 STATIC_CONTRACT_GC_NOTRIGGER;
138 STATIC_CONTRACT_FORBID_FAULT;
139
140 LPUTF8 ptr = ns::FindSep(szPath);
141 if (ptr)
142 {
143 *ptr = 0;
144 ++ptr;
145 }
146 return ptr;
147} // LPUTF8 ns::SplitInline()
148
149void ns::SplitInline(
150 __inout __inout_z LPWSTR szPath, // Path to split.
151 LPCWSTR &szNameSpace, // Return pointer to namespace.
152 LPCWSTR &szName) // Return pointer to name.
153{
154 STATIC_CONTRACT_NOTHROW;
155 STATIC_CONTRACT_GC_NOTRIGGER;
156 STATIC_CONTRACT_FORBID_FAULT;
157
158 WCHAR *ptr = SplitInline(szPath);
159 if (ptr)
160 {
161 szNameSpace = szPath;
162 szName = ptr;
163 }
164 else
165 {
166 szNameSpace = 0;
167 szName = szPath;
168 }
169} // void ns::SplitInline()
170
171void ns::SplitInline(
172 __inout __inout_z LPUTF8 szPath, // Path to split.
173 LPCUTF8 &szNameSpace, // Return pointer to namespace.
174 LPCUTF8 &szName) // Return pointer to name.
175{
176 STATIC_CONTRACT_NOTHROW;
177 STATIC_CONTRACT_GC_NOTRIGGER;
178 STATIC_CONTRACT_FORBID_FAULT;
179
180 LPUTF8 ptr = SplitInline(szPath);
181 if (ptr)
182 {
183 szNameSpace = szPath;
184 szName = ptr;
185 }
186 else
187 {
188 szNameSpace = 0;
189 szName = szPath;
190 }
191} // void ns::SplitInline()
192
193
194//*****************************************************************************
195// Split the last parsable element from the end of the string as the name,
196// the first part as the namespace.
197//*****************************************************************************
198int ns::SplitPath( // true ok, false trunction.
199 const WCHAR *szPath, // Path to split.
200 __out_ecount(cchNameSpace) WCHAR *szNameSpace, // Output for namespace value.
201 int cchNameSpace, // Max chars for output.
202 __out_ecount(cchName) WCHAR *szName, // Output for name.
203 int cchName) // Max chars for output.
204{
205 STATIC_CONTRACT_NOTHROW;
206 STATIC_CONTRACT_GC_NOTRIGGER;
207 STATIC_CONTRACT_FORBID_FAULT;
208
209 const WCHAR *ptr = ns::FindSep(szPath);
210 size_t iLen = (ptr) ? ptr - szPath : 0;
211 size_t iCopyMax;
212 int brtn = true;
213 if (szNameSpace && cchNameSpace)
214 {
215 _ASSERTE(cchNameSpace > 1);
216 iCopyMax = cchNameSpace - 1;
217 iCopyMax = min(iCopyMax, iLen);
218 wcsncpy_s(szNameSpace, cchNameSpace, szPath, iCopyMax);
219 szNameSpace[iCopyMax] = 0;
220
221 if (iLen >= (size_t)cchNameSpace)
222 brtn = false;
223 }
224
225 if (szName && cchName)
226 {
227 _ASSERTE(cchName > 1);
228 iCopyMax = cchName - 1;
229 if (ptr)
230 ++ptr;
231 else
232 ptr = szPath;
233 iLen = (int)wcslen(ptr);
234 iCopyMax = min(iCopyMax, iLen);
235 wcsncpy_s(szName, cchName, ptr, iCopyMax);
236 szName[iCopyMax] = 0;
237
238 if (iLen >= (size_t)cchName)
239 brtn = false;
240 }
241 return brtn;
242} // int ns::SplitPath()
243
244
245int ns::SplitPath( // true ok, false trunction.
246 LPCUTF8 szPath, // Path to split.
247 __out_ecount_opt (cchNameSpace) LPUTF8 szNameSpace, // Output for namespace value.
248 int cchNameSpace, // Max chars for output.
249 __out_ecount_opt (cchName) LPUTF8 szName, // Output for name.
250 int cchName) // Max chars for output.
251{
252 STATIC_CONTRACT_NOTHROW;
253 STATIC_CONTRACT_GC_NOTRIGGER;
254 STATIC_CONTRACT_FORBID_FAULT;
255
256 LPCUTF8 ptr = ns::FindSep(szPath);
257 size_t iLen = (ptr) ? ptr - szPath : 0;
258 size_t iCopyMax;
259 int brtn = true;
260 if (szNameSpace && cchNameSpace)
261 {
262 _ASSERTE(cchNameSpace > 1);
263 iCopyMax = cchNameSpace-1;
264 iCopyMax = min(iCopyMax, iLen);
265 strncpy_s(szNameSpace, cchNameSpace, szPath, iCopyMax);
266 szNameSpace[iCopyMax] = 0;
267
268 if (iLen >= (size_t)cchNameSpace)
269 brtn = false;
270 }
271
272 if (szName && cchName)
273 {
274 _ASSERTE(cchName > 1);
275 iCopyMax = cchName-1;
276 if (ptr)
277 ++ptr;
278 else
279 ptr = szPath;
280 iLen = (int)strlen(ptr);
281 iCopyMax = min(iCopyMax, iLen);
282 strncpy_s(szName, cchName, ptr, iCopyMax);
283 szName[iCopyMax] = 0;
284
285 if (iLen >= (size_t)cchName)
286 brtn = false;
287 }
288 return brtn;
289} // int ns::SplitPath()
290
291
292//*****************************************************************************
293// Take two values and put them together in a fully qualified path using the
294// correct separator.
295//*****************************************************************************
296int ns::MakePath( // true ok, false truncation.
297 __out_ecount(cchChars) WCHAR *szOut, // output path for name.
298 int cchChars, // max chars for output path.
299 const WCHAR *szNameSpace, // Namespace.
300 const WCHAR *szName) // Name.
301{
302 STATIC_CONTRACT_NOTHROW;
303 STATIC_CONTRACT_GC_NOTRIGGER;
304 STATIC_CONTRACT_FORBID_FAULT;
305
306 if (cchChars < 1)
307 return false;
308
309 if (szOut)
310 *szOut = 0;
311 else
312 return false;
313
314 if (szNameSpace && *szNameSpace != W('\0'))
315 {
316 if (wcsncpy_s(szOut, cchChars, szNameSpace, _TRUNCATE) == STRUNCATE)
317 return false;
318
319 // Add namespace separator if a non-empty name was supplied
320 if (szName && *szName != W('\0'))
321 {
322 if (wcsncat_s(szOut, cchChars, NAMESPACE_SEPARATOR_WSTR, _TRUNCATE) == STRUNCATE)
323 {
324 return false;
325 }
326 }
327 }
328
329 if (szName && *szName)
330 {
331 if (wcsncat_s(szOut, cchChars, szName, _TRUNCATE) == STRUNCATE)
332 return false;
333 }
334
335 return true;
336} // int ns::MakePath()
337
338int ns::MakePath( // true ok, false truncation.
339 __out_ecount(cchChars) LPUTF8 szOut, // output path for name.
340 int cchChars, // max chars for output path.
341 LPCUTF8 szNameSpace, // Namespace.
342 LPCUTF8 szName) // Name.
343{
344 STATIC_CONTRACT_NOTHROW;
345 STATIC_CONTRACT_GC_NOTRIGGER;
346 STATIC_CONTRACT_FORBID_FAULT;
347
348 if (cchChars < 1)
349 return false;
350
351 if (szOut)
352 *szOut = 0;
353 else
354 return false;
355
356 if (szNameSpace && *szNameSpace != W('\0'))
357 {
358 if (strncpy_s(szOut, cchChars, szNameSpace, _TRUNCATE) == STRUNCATE)
359 return false;
360
361 // Add namespace separator if a non-empty name was supplied
362 if (szName && *szName != W('\0'))
363 {
364 if (strncat_s(szOut, cchChars, NAMESPACE_SEPARATOR_STR, _TRUNCATE) == STRUNCATE)
365 {
366 return false;
367 }
368 }
369 }
370
371 if (szName && *szName)
372 {
373 if (strncat_s(szOut, cchChars, szName, _TRUNCATE) == STRUNCATE)
374 return false;
375 }
376
377 return true;
378
379} // int ns::MakePath()
380
381int ns::MakePath( // true ok, false truncation.
382 __out_ecount(cchChars) WCHAR *szOut, // output path for name.
383 int cchChars, // max chars for output path.
384 LPCUTF8 szNamespace, // Namespace.
385 LPCUTF8 szName) // Name.
386{
387 STATIC_CONTRACT_NOTHROW;
388 STATIC_CONTRACT_GC_NOTRIGGER;
389 STATIC_CONTRACT_FORBID_FAULT;
390
391 if (cchChars < 1)
392 return false;
393
394 if (szOut)
395 *szOut = 0;
396 else
397 return false;
398
399 if (szNamespace != NULL && *szNamespace != '\0')
400 {
401 if (cchChars < 2)
402 return false;
403
404 int count;
405
406 // We use cBuffer - 2 to account for the '.' and at least a 1 character name below.
407 count = WszMultiByteToWideChar(CP_UTF8, 0, szNamespace, -1, szOut, cchChars-2);
408 if (count == 0)
409 return false; // Supply a bigger buffer!
410
411 // buffer access is bounded: WszMultiByteToWideChar returns 0 if access doesn't fit in range
412#ifdef _PREFAST_
413 #pragma warning( suppress: 26015 )
414#endif
415 szOut[count-1] = NAMESPACE_SEPARATOR_WCHAR;
416 szOut += count;
417 cchChars -= count;
418 }
419
420 if (((cchChars == 0) && (szName != NULL) && (*szName != '\0')) ||
421 (WszMultiByteToWideChar(CP_UTF8, 0, szName, -1, szOut, cchChars) == 0))
422 return false; // supply a bigger buffer!
423 return true;
424} // int ns::MakePath()
425
426int ns::MakePath( // true ok, false out of memory
427 CQuickBytes &qb, // Where to put results.
428 LPCUTF8 szNameSpace, // Namespace for name.
429 LPCUTF8 szName) // Final part of name.
430{
431 STATIC_CONTRACT_NOTHROW;
432 STATIC_CONTRACT_GC_NOTRIGGER;
433 STATIC_CONTRACT_FAULT;
434
435 int iLen = 2;
436 if (szNameSpace)
437 iLen += (int)strlen(szNameSpace);
438 if (szName)
439 iLen += (int)strlen(szName);
440 LPUTF8 szOut = (LPUTF8) qb.AllocNoThrow(iLen);
441 if (!szOut)
442 return false;
443 return ns::MakePath(szOut, iLen, szNameSpace, szName);
444} // int ns::MakePath()
445
446int ns::MakePath( // true ok, false out of memory
447 CQuickArray<WCHAR> &qa, // Where to put results.
448 LPCUTF8 szNameSpace, // Namespace for name.
449 LPCUTF8 szName) // Final part of name.
450{
451 STATIC_CONTRACT_NOTHROW;
452 STATIC_CONTRACT_GC_NOTRIGGER;
453 STATIC_CONTRACT_FAULT;
454
455 int iLen = 2;
456 if (szNameSpace)
457 iLen += (int)strlen(szNameSpace);
458 if (szName)
459 iLen += (int)strlen(szName);
460 WCHAR *szOut = (WCHAR *) qa.AllocNoThrow(iLen);
461 if (!szOut)
462 return false;
463 return ns::MakePath(szOut, iLen, szNameSpace, szName);
464} // int ns::MakePath()
465
466int ns::MakePath( // true ok, false out of memory
467 CQuickBytes &qb, // Where to put results.
468 const WCHAR *szNameSpace, // Namespace for name.
469 const WCHAR *szName) // Final part of name.
470{
471 STATIC_CONTRACT_NOTHROW;
472 STATIC_CONTRACT_GC_NOTRIGGER;
473 STATIC_CONTRACT_FAULT;
474
475 int iLen = 2;
476 if (szNameSpace)
477 iLen += (int)wcslen(szNameSpace);
478 if (szName)
479 iLen += (int)wcslen(szName);
480 WCHAR *szOut = (WCHAR *) qb.AllocNoThrow(iLen * sizeof(WCHAR));
481 if (!szOut)
482 return false;
483 return ns::MakePath(szOut, iLen, szNameSpace, szName);
484} // int ns::MakePath()
485
486void ns::MakePath( // throws on out of memory
487 SString &ssBuf, // Where to put results.
488 const SString &ssNameSpace, // Namespace for name.
489 const SString &ssName) // Final part of name.
490{
491 STATIC_CONTRACT_THROWS;
492 STATIC_CONTRACT_GC_NOTRIGGER;
493 STATIC_CONTRACT_FAULT;
494
495 ssBuf.Clear();
496
497 if (!ssNameSpace.IsEmpty())
498 {
499 if (ssName.IsEmpty())
500 {
501 ssBuf.Set(ssNameSpace);
502 }
503 else
504 {
505 SString s(SString::Literal, NAMESPACE_SEPARATOR_WSTR);
506 ssBuf.Set(ssNameSpace, s);
507 }
508 }
509
510 if (!ssName.IsEmpty())
511 {
512 ssBuf.Append(ssName);
513 }
514}
515
516bool ns::MakeAssemblyQualifiedName( // true ok, false truncation
517 __out_ecount(dwBuffer) WCHAR* pBuffer, // Buffer to recieve the results
518 int dwBuffer, // Number of characters total in buffer
519 const WCHAR *szTypeName, // Namespace for name.
520 int dwTypeName, // Number of characters (not including null)
521 const WCHAR *szAssemblyName, // Final part of name.
522 int dwAssemblyName) // Number of characters (not including null)
523{
524 STATIC_CONTRACT_NOTHROW;
525 STATIC_CONTRACT_GC_NOTRIGGER;
526 STATIC_CONTRACT_FORBID_FAULT;
527
528 if (dwBuffer < 2)
529 return false;
530
531 int iCopyMax = 0;
532 _ASSERTE(pBuffer);
533 *pBuffer = NULL;
534
535 if (szTypeName && *szTypeName != W('\0'))
536 {
537 _ASSERTE(dwTypeName > 0);
538 iCopyMax = min(dwBuffer-1, dwTypeName);
539 wcsncpy_s(pBuffer, dwBuffer, szTypeName, iCopyMax);
540 dwBuffer -= iCopyMax;
541 }
542
543 if (szAssemblyName && *szAssemblyName != W('\0'))
544 {
545
546 if(dwBuffer < ASSEMBLY_SEPARATOR_LEN)
547 return false;
548
549 for(DWORD i = 0; i < ASSEMBLY_SEPARATOR_LEN; i++)
550 pBuffer[iCopyMax+i] = ASSEMBLY_SEPARATOR_WSTR[i];
551
552 dwBuffer -= ASSEMBLY_SEPARATOR_LEN;
553 if(dwBuffer == 0)
554 return false;
555
556 int iCur = iCopyMax + ASSEMBLY_SEPARATOR_LEN;
557 _ASSERTE(dwAssemblyName > 0);
558 iCopyMax = min(dwBuffer-1, dwAssemblyName);
559 wcsncpy_s(pBuffer + iCur, dwBuffer, szAssemblyName, iCopyMax);
560 pBuffer[iCur + iCopyMax] = W('\0');
561
562 if (iCopyMax < dwAssemblyName)
563 return false;
564 }
565 else {
566 if(dwBuffer == 0) {
567 PREFIX_ASSUME(iCopyMax > 0);
568 pBuffer[iCopyMax-1] = W('\0');
569 return false;
570 }
571 else
572 pBuffer[iCopyMax] = W('\0');
573 }
574
575 return true;
576} // int ns::MakePath()
577
578bool ns::MakeAssemblyQualifiedName( // true ok, false out of memory
579 CQuickBytes &qb, // Where to put results.
580 const WCHAR *szTypeName, // Namespace for name.
581 const WCHAR *szAssemblyName) // Final part of name.
582{
583 STATIC_CONTRACT_NOTHROW;
584 STATIC_CONTRACT_GC_NOTRIGGER;
585 STATIC_CONTRACT_FAULT;
586
587 int iTypeName = 0;
588 int iAssemblyName = 0;
589 if (szTypeName)
590 iTypeName = (int)wcslen(szTypeName);
591 if (szAssemblyName)
592 iAssemblyName = (int)wcslen(szAssemblyName);
593
594 int iLen = ASSEMBLY_SEPARATOR_LEN + iTypeName + iAssemblyName + 1; // Space for null terminator
595 WCHAR *szOut = (WCHAR *) qb.AllocNoThrow(iLen * sizeof(WCHAR));
596 if (!szOut)
597 return false;
598
599 bool ret;
600 ret = ns::MakeAssemblyQualifiedName(szOut, iLen, szTypeName, iTypeName, szAssemblyName, iAssemblyName);
601 _ASSERTE(ret);
602 return true;
603}
604
605int ns::MakeNestedTypeName( // true ok, false out of memory
606 CQuickBytes &qb, // Where to put results.
607 LPCUTF8 szEnclosingName, // Full name for enclosing type
608 LPCUTF8 szNestedName) // Full name for nested type
609{
610 STATIC_CONTRACT_NOTHROW;
611 STATIC_CONTRACT_GC_NOTRIGGER;
612 STATIC_CONTRACT_FAULT;
613
614 _ASSERTE(szEnclosingName && szNestedName);
615 int iLen = 2;
616 iLen += (int)strlen(szEnclosingName);
617 iLen += (int)strlen(szNestedName);
618 LPUTF8 szOut = (LPUTF8) qb.AllocNoThrow(iLen);
619 if (!szOut)
620 return false;
621 return ns::MakeNestedTypeName(szOut, iLen, szEnclosingName, szNestedName);
622} // int ns::MakeNestedTypeName()
623
624int ns::MakeNestedTypeName( // true ok, false truncation.
625 __out_ecount (cchChars) LPUTF8 szOut, // output path for name.
626 int cchChars, // max chars for output path.
627 LPCUTF8 szEnclosingName, // Full name for enclosing type
628 LPCUTF8 szNestedName) // Full name for nested type
629{
630 STATIC_CONTRACT_NOTHROW;
631 STATIC_CONTRACT_GC_NOTRIGGER;
632 STATIC_CONTRACT_FORBID_FAULT;
633
634 if (cchChars < 1)
635 return false;
636
637 int iCopyMax = 0, iLen;
638 int brtn = true;
639 *szOut = 0;
640
641 iLen = (int)strlen(szEnclosingName);
642 iCopyMax = min(cchChars-1, iLen);
643 strncpy_s(szOut, cchChars, szEnclosingName, iCopyMax);
644
645 if (iLen >= cchChars)
646 brtn = false;
647
648 szOut[iCopyMax] = NESTED_SEPARATOR_CHAR;
649 int iCur = iCopyMax+1; // iCopyMax characters + nested_separator_char
650 cchChars -= iCur;
651 if(cchChars == 0)
652 return false;
653
654 iLen = (int)strlen(szNestedName);
655 iCopyMax = min(cchChars-1, iLen);
656 strncpy_s(&szOut[iCur], cchChars, szNestedName, iCopyMax);
657 szOut[iCur + iCopyMax] = 0;
658
659 if (iLen >= cchChars)
660 brtn = false;
661
662 return brtn;
663} // int ns::MakeNestedTypeName()
664
665void ns::MakeNestedTypeName( // throws on out of memory
666 SString &ssBuf, // output path for name.
667 const SString &ssEnclosingName, // Full name for enclosing type
668 const SString &ssNestedName) // Full name for nested type
669{
670 STATIC_CONTRACT_THROWS;
671 STATIC_CONTRACT_GC_NOTRIGGER;
672
673 ssBuf.Clear();
674
675 ssBuf.Append(ssEnclosingName);
676 ssBuf.Append(NESTED_SEPARATOR_WCHAR);
677 ssBuf.Append(ssNestedName);
678}
679
680