/// <summary> /// Normalize the given path. /// </summary> /// <remarks> /// Normalizes via Win32 GetFullPathName(). /// </remarks> /// <param name="path">Path to normalize</param> /// <exception cref="PathTooLongException">Thrown if we have a string that is too large to fit into a UNICODE_STRING.</exception> /// <exception cref="IOException">Thrown if the path is empty.</exception> /// <returns>Normalized path</returns> internal static string Normalize(string path) { Span <char> initialBuffer = stackalloc char[PathInternal.MaxShortPath]; ValueStringBuilder builder = new ValueStringBuilder(initialBuffer); // Get the full path GetFullPathName(path, ref builder); // If we have the exact same string we were passed in, don't allocate another string. // TryExpandShortName does this input identity check. string result = builder.AsSpan().Contains('~') ? TryExpandShortFileName(ref builder, originalPath: path) : builder.AsSpan().EqualsOrdinal(path.AsSpan()) ? path : builder.ToString(); // Clear the buffer builder.Dispose(); return(result); }
public unsafe void Append_PtrInt_MatchesStringBuilder() { var sb = new StringBuilder(); var vsb = new ValueStringBuilder(); for (int i = 1; i <= 100; i++) { string s = i.ToString(); fixed(char *p = s) { sb.Append(p, s.Length); vsb.Append(p, s.Length); } } Assert.Equal(sb.Length, vsb.Length); Assert.Equal(sb.ToString(), vsb.ToString()); }
public override string ToString() { var sbName = new ValueStringBuilder(MethodBase.MethodNameBufferSize); sbName.Append(PropertyType.FormatTypeName()); sbName.Append(' '); sbName.Append(Name); RuntimeType[] arguments = Signature.Arguments; if (arguments.Length > 0) { sbName.Append(" ["); MethodBase.AppendParameters(ref sbName, arguments, Signature.CallingConvention); sbName.Append(']'); } return(sbName.ToString()); }
public override void Update(double totalTime, double frameTime) { base.Update(totalTime, frameTime); if (Time.Ticks > _time_to_update) { _time_to_update = Time.Ticks + 100; if (!NetClient.Socket.IsConnected) { _ping = NetClient.LoginSocket.Statistics.Ping; _deltaBytesReceived = NetClient.LoginSocket.Statistics.DeltaBytesReceived; _deltaBytesSent = NetClient.LoginSocket.Statistics.DeltaBytesSent; } else if (!NetClient.Socket.IsDisposed) { _ping = NetClient.Socket.Statistics.Ping; _deltaBytesReceived = NetClient.Socket.Statistics.DeltaBytesReceived; _deltaBytesSent = NetClient.Socket.Statistics.DeltaBytesSent; } Span <char> span = stackalloc char[128]; ValueStringBuilder sb = new ValueStringBuilder(span); if (IsMinimized) { sb.Append($"Ping: {_ping} ms"); } else { sb.Append($"Ping: {_ping} ms\n{"In:"} {NetStatistics.GetSizeAdaptive(_deltaBytesReceived),-6} {"Out:"} {NetStatistics.GetSizeAdaptive(_deltaBytesSent),-6}"); } _cacheText = sb.ToString(); sb.Dispose(); Vector2 size = Fonts.Bold.MeasureString(_cacheText); _trans.Width = Width = (int)(size.X + 20); _trans.Height = Height = (int)(size.Y + 20); WantUpdateSize = true; } }
public void TryCopyTo_FailsWhenDestinationIsTooSmall_SucceedsWhenItsLargeEnough() { var vsb = new ValueStringBuilder(); const string Text = "expected text"; vsb.Append(Text); Assert.Equal(Text.Length, vsb.Length); Span <char> dst = new char[Text.Length - 1]; Assert.False(vsb.TryCopyTo(dst, out int charsWritten)); Assert.Equal(0, charsWritten); dst = new char[Text.Length]; Assert.True(vsb.TryCopyTo(dst, out charsWritten)); Assert.Equal(Text.Length, charsWritten); Assert.Equal(0, vsb.Length); }
public void AsSpan_ReturnsCorrectValue_DoesntClearBuilder() { var sb = new StringBuilder(); var vsb = new ValueStringBuilder(); for (int i = 1; i <= 100; i++) { string s = i.ToString(); sb.Append(s); vsb.Append(s); } var resultString = new string(vsb.AsSpan()); Assert.Equal(sb.ToString(), resultString); Assert.NotEqual(0, sb.Length); Assert.Equal(sb.Length, vsb.Length); }
private static void GetUserName(ref ValueStringBuilder builder) { uint size = 0; while (Interop.Secur32.GetUserNameExW(Interop.Secur32.NameSamCompatible, ref builder.GetPinnableReference(), ref size) == Interop.BOOLEAN.FALSE) { if (Marshal.GetLastWin32Error() == Interop.Errors.ERROR_MORE_DATA) { builder.EnsureCapacity(checked ((int)size)); } else { builder.Length = 0; return; } } builder.Length = (int)size; }
public static unsafe string Join([Nullable(new byte[] { 1, 2 })] params string[] paths) { if (paths == null) { throw new ArgumentNullException(nameof(paths)); } if (paths.Length == 0) { return(string.Empty); } int num = 0; foreach (string path in paths) { num += path != null ? path.Length : 0; } int capacity = num + (paths.Length - 1); // ISSUE: untyped stack allocation ValueStringBuilder valueStringBuilder = new ValueStringBuilder(new Span <char>((void *)__untypedstackalloc(new IntPtr(520)), 260)); valueStringBuilder.EnsureCapacity(capacity); for (int index = 0; index < paths.Length; ++index) { string path = paths[index]; if (path != null && path.Length != 0) { if (valueStringBuilder.Length == 0) { valueStringBuilder.Append(path); } else { if (!PathInternal.IsDirectorySeparator(valueStringBuilder[valueStringBuilder.Length - 1]) && !PathInternal.IsDirectorySeparator(path[0])) { valueStringBuilder.Append('\\'); } valueStringBuilder.Append(path); } } } return(valueStringBuilder.ToString()); }
public unsafe string GetRawInline() { string variable = Variable; if (variable == null) { throw new ArgumentNullException(nameof(variable)); } Span <char> stack = stackalloc char[128]; ValueStringBuilder buffer = new ValueStringBuilder(stack); uint returnValue; fixed(char *v = variable) { while (true) { fixed(char *b = buffer) { if ((returnValue = Raw.GetEnvironmentVariableW(v, b, (uint)buffer.Capacity)) <= buffer.Capacity) { break; } else { buffer.EnsureCapacity((int)returnValue); } } } ; } if (returnValue == 0) { return(null); } buffer.Length = (int)returnValue; return(buffer.ToString()); }
private string Format(string[] elements) { // Need to calculate TimeTaken now, if applicable var date = elements[W3CLoggingMiddleware._dateIndex]; var time = elements[W3CLoggingMiddleware._timeIndex]; if (!string.IsNullOrEmpty(date) && !string.IsNullOrEmpty(time) && _loggingFields.HasFlag(W3CLoggingFields.TimeTaken)) { DateTime start = DateTime.ParseExact(date + time, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture); var elapsed = DateTime.UtcNow.Subtract(start); elements[W3CLoggingMiddleware._timeTakenIndex] = elapsed.TotalMilliseconds.ToString(CultureInfo.InvariantCulture); } // 200 is around the length of an average cookie-less entry var sb = new ValueStringBuilder(200); var firstElement = true; for (var i = 0; i < elements.Length; i++) { if (_loggingFields.HasFlag((W3CLoggingFields)(1 << i))) { if (!firstElement) { sb.Append(' '); } else { firstElement = false; } // If the element was not logged, or was the empty string, we log it as a dash if (string.IsNullOrEmpty(elements[i])) { sb.Append('-'); } else { sb.Append(elements[i]); } } } return(sb.ToString()); }
public void Dispose_ClearsBuilder_ThenReusable() { const string Text1 = "test"; var vsb = new ValueStringBuilder(); vsb.Append(Text1); Assert.Equal(Text1.Length, vsb.Length); vsb.Dispose(); Assert.Equal(0, vsb.Length); Assert.Equal(string.Empty, vsb.ToString()); Assert.True(vsb.TryCopyTo(Span <char> .Empty, out _)); const string Text2 = "another test"; vsb.Append(Text2); Assert.Equal(Text2.Length, vsb.Length); Assert.Equal(Text2, vsb.ToString()); }
// copied from CoreCLR's RuntimeConstructorInfo public override string ToString() { if (toString == null) { var sbName = new ValueStringBuilder(MethodNameBufferSize); // "Void" really doesn't make sense here. But we'll keep it for compat reasons. sbName.Append("Void "); sbName.Append(Name); sbName.Append('('); AppendParameters(ref sbName, GetParameterTypes(), CallingConvention); sbName.Append(')'); toString = sbName.ToString(); } return(toString); }
public void AppendSpan_DataAppendedCorrectly() { var sb = new StringBuilder(); var vsb = new ValueStringBuilder(); for (int i = 1; i <= 1000; i++) { string s = i.ToString(); sb.Append(s); Span <char> span = vsb.AppendSpan(s.Length); Assert.Equal(sb.Length, vsb.Length); s.AsSpan().CopyTo(span); } Assert.Equal(sb.Length, vsb.Length); Assert.Equal(sb.ToString(), vsb.ToString()); }
public static void HtmlEncode(string?value, TextWriter output) { if (output == null) { throw new ArgumentNullException(nameof(output)); } if (string.IsNullOrEmpty(value)) { output.Write(value); return; } ReadOnlySpan <char> valueSpan = value.AsSpan(); // Don't create ValueStringBuilder if we don't have anything to encode int index = IndexOfHtmlEncodingChars(valueSpan); if (index == -1) { output.Write(value); return; } // For small inputs we allocate on the stack. In most cases a buffer three // times larger the original string should be sufficient as usually not all // characters need to be encoded. // For larger string we rent the input string's length plus a fixed // conservative amount of chars from the ArrayPool. Span <char> buffer = value.Length < 80 ? stackalloc char[256] : null; ValueStringBuilder sb = buffer != null ? new ValueStringBuilder(buffer) : new ValueStringBuilder(value.Length + 200); sb.Append(valueSpan.Slice(0, index)); HtmlEncode(valueSpan.Slice(index), ref sb); output.Write(sb.AsSpan()); sb.Dispose(); }
public override string ToString() { using var sb = new ValueStringBuilder(); sb.Append('{'); var first = true; foreach (var value in Values) { if (!first) { sb.Append(','); } sb.Append(value); first = false; } sb.Append('}'); return(sb.ToString()); }
private static string ExpandEnvironmentVariablesCore(string name) { Span <char> initialBuffer = stackalloc char[128]; var builder = new ValueStringBuilder(initialBuffer); uint length; while ((length = Interop.Kernel32.ExpandEnvironmentStrings(name, ref builder.GetPinnableReference(), (uint)builder.Capacity)) > builder.Capacity) { builder.EnsureCapacity((int)length); } if (length == 0) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } // length includes the null terminator builder.Length = (int)length - 1; return(builder.ToString()); }
internal static unsafe char[] UnescapeString(char *pStr, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser?syntax, bool isQuery) { ValueStringBuilder vsb = new ValueStringBuilder(dest.Length); vsb.Append(dest.AsSpan(0, destPosition)); UnescapeString(pStr, start, end, ref vsb, rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery); if (vsb.Length > dest.Length) { dest = vsb.AsSpan().ToArray(); } else { vsb.AsSpan(destPosition).TryCopyTo(dest.AsSpan(destPosition)); } destPosition = vsb.Length; vsb.Dispose(); return(dest); }
// copied from CoreCLR's RuntimeMethodInfo public override string ToString() { if (toString == null) { var sbName = new ValueStringBuilder(MethodNameBufferSize); sbName.Append(ReturnType.FormatTypeName()); sbName.Append(' '); sbName.Append(Name); if (IsGenericMethod) sbName.Append(RuntimeMethodHandle.ConstructInstantiation(this, TypeNameFormatFlags.FormatBasic)); sbName.Append('('); AppendParameters(ref sbName, GetParameterTypes(), CallingConvention); sbName.Append(')'); toString = sbName.ToString(); } return toString; }
/// <summary> /// Returns the expansion of the passed replacement pattern. For /// example, if the replacement pattern is ?$1$2?, Result returns the concatenation /// of Group(1).ToString() and Group(2).ToString(). /// </summary> public virtual string Result(string replacement) { if (replacement == null) { throw new ArgumentNullException(nameof(replacement)); } if (_regex == null) { throw new NotSupportedException(SR.NoResultOnFailed); } // Gets the weakly cached replacement helper or creates one if there isn't one already. RegexReplacement repl = RegexReplacement.GetOrCreate(_regex._replref, replacement, _regex.caps, _regex.capsize, _regex.capnames, _regex.roptions); Span <char> charInitSpan = stackalloc char[ReplaceBufferSize]; var vsb = new ValueStringBuilder(charInitSpan); repl.ReplacementImpl(ref vsb, this); return(vsb.ToString()); }
public static void HtmlDecode(string?value, TextWriter output) { if (output == null) { throw new ArgumentNullException(nameof(output)); } if (string.IsNullOrEmpty(value)) { output.Write(value); return; } ReadOnlySpan <char> valueSpan = value.AsSpan(); int index = IndexOfHtmlDecodingChars(valueSpan); if (index == -1) { output.Write(value); return; } // In the worst case the decoded string has the same length. // For small inputs we use stack allocation. Span <char> buffer = value.Length <= 256 ? stackalloc char[256] : null; ValueStringBuilder sb = buffer != null ? new ValueStringBuilder(buffer) : new ValueStringBuilder(value.Length); sb.Append(valueSpan.Slice(0, index)); HtmlDecode(valueSpan.Slice(index), ref sb); output.Write(sb.AsSpan()); sb.Dispose(); }
public void GetEnumeratorTest() { using ValueStringBuilder sb = ValueStringBuilder.CreateFrom("Hello".AsSpan()); var enumerator = sb.GetEnumerator(); Assert.True(enumerator.MoveNext()); Assert.AreEqual('H', enumerator.Current); Assert.True(enumerator.MoveNext()); Assert.AreEqual('e', enumerator.Current); Assert.True(enumerator.MoveNext()); Assert.AreEqual('l', enumerator.Current); Assert.True(enumerator.MoveNext()); Assert.AreEqual('l', enumerator.Current); Assert.True(enumerator.MoveNext()); Assert.AreEqual('o', enumerator.Current); Assert.False(enumerator.MoveNext()); }
internal string ReadASCII(int size) { EnsureSize(size); Span <char> span = stackalloc char[size]; ValueStringBuilder sb = new ValueStringBuilder(span); for (int i = 0; i < size; i++) { char c = (char)ReadByte(); if (c != 0) { sb.Append(c); } } string ss = sb.ToString(); sb.Dispose(); return(ss); }
/// <summary> /// Given a Match, emits into the ValueStringBuilder the evaluated /// Right-to-Left substitution pattern. /// </summary> public void ReplacementImplRTL(ref ValueStringBuilder vsb, Match match) { for (int i = _rules.Count - 1; i >= 0; i--) { int r = _rules[i]; if (r >= 0) // string lookup { vsb.AppendReversed(_strings[r]); } else if (r < -Specials) // group lookup { vsb.AppendReversed(match.GroupToStringImpl(-Specials - 1 - r)); } else { switch (-Specials - 1 - r) { // special insertion patterns case LeftPortion: vsb.AppendReversed(match.GetLeftSubstring()); break; case RightPortion: vsb.AppendReversed(match.GetRightSubstring()); break; case LastGroup: vsb.AppendReversed(match.LastGroupToStringImpl()); break; case WholeString: vsb.AppendReversed(match.Text); break; } } } }
private static void GetFullPathName(ReadOnlySpan<char> path, ref ValueStringBuilder builder) { // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here. Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path)); uint result = 0; while ((result = Interop.Kernel32.GetFullPathNameW(ref MemoryMarshal.GetReference(path), (uint)builder.Capacity, ref builder.GetPinnableReference(), IntPtr.Zero)) > builder.Capacity) { // Reported size is greater than the buffer size. Increase the capacity. builder.EnsureCapacity(checked((int)result)); } if (result == 0) { // Failure, get the error and throw int errorCode = Marshal.GetLastWin32Error(); if (errorCode == 0) errorCode = Interop.Errors.ERROR_BAD_PATHNAME; throw Win32Marshal.GetExceptionForWin32Error(errorCode, path.ToString()); } builder.Length = (int)result; }
public void TryCopyTo_ClearsBuilder_ThenReusable() { const string Text1 = "test"; var vsb = new ValueStringBuilder(); vsb.Append(Text1); Assert.Equal(Text1.Length, vsb.Length); Span <char> dst = new char[Text1.Length]; Assert.True(vsb.TryCopyTo(dst, out int charsWritten)); Assert.Equal(Text1.Length, charsWritten); Assert.Equal(Text1, new string(dst)); Assert.Equal(0, vsb.Length); Assert.Equal(string.Empty, vsb.ToString()); Assert.True(vsb.TryCopyTo(Span <char> .Empty, out _)); const string Text2 = "another test"; vsb.Append(Text2); Assert.Equal(Text2.Length, vsb.Length); Assert.Equal(Text2, vsb.ToString()); }
private static void BuildCommandLine(ProcessStartInfo startInfo, ref ValueStringBuilder commandLine) { // Construct a StringBuilder with the appropriate command line // to pass to CreateProcess. If the filename isn't already // in quotes, we quote it here. This prevents some security // problems (it specifies exactly which part of the string // is the file to execute). ReadOnlySpan <char> fileName = startInfo.FileName.AsSpan().Trim(); bool fileNameIsQuoted = fileName.Length > 0 && fileName[0] == '\"' && fileName[fileName.Length - 1] == '\"'; if (!fileNameIsQuoted) { commandLine.Append('"'); } commandLine.Append(fileName); if (!fileNameIsQuoted) { commandLine.Append('"'); } startInfo.AppendArgumentsTo(ref commandLine); }
/// <summary>Transforms an ASCII character into its hexadecimal representation, adding the characters to a StringBuilder.</summary> private static void AddHexEscaped(byte c, ref ValueStringBuilder destination) { destination.Append('%'); destination.Append(HexConverter.ToCharUpper(c >> 4)); destination.Append(HexConverter.ToCharUpper(c)); }
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (value is Font font) { if (destinationType == typeof(string)) { if (culture == null) { culture = CultureInfo.CurrentCulture; } ValueStringBuilder sb = new ValueStringBuilder(); sb.Append(font.Name); sb.Append(culture.TextInfo.ListSeparator[0] + " "); sb.Append(font.Size.ToString(culture.NumberFormat)); switch (font.Unit) { // MS throws ArgumentException, if unit is set // to GraphicsUnit.Display // Don't know what to append for GraphicsUnit.Display case GraphicsUnit.Display: sb.Append("display"); break; case GraphicsUnit.Document: sb.Append("doc"); break; case GraphicsUnit.Point: sb.Append("pt"); break; case GraphicsUnit.Inch: sb.Append("in"); break; case GraphicsUnit.Millimeter: sb.Append("mm"); break; case GraphicsUnit.Pixel: sb.Append("px"); break; case GraphicsUnit.World: sb.Append("world"); break; } if (font.Style != FontStyle.Regular) { sb.Append(culture.TextInfo.ListSeparator[0] + " style="); sb.Append(font.Style.ToString()); } return(sb.ToString()); } if (destinationType == typeof(InstanceDescriptor)) { ConstructorInfo met = typeof(Font).GetTypeInfo().GetConstructor(new Type[] { typeof(string), typeof(float), typeof(FontStyle), typeof(GraphicsUnit) }); object[] args = new object[4]; args[0] = font.Name; args[1] = font.Size; args[2] = font.Style; args[3] = font.Unit; return(new InstanceDescriptor(met, args)); } } return(base.ConvertTo(context, culture, value, destinationType)); }
public override void Update(double totalTime, double frameTime) { base.Update(totalTime, frameTime); if (Time.Ticks > _timeToUpdate) { _timeToUpdate = Time.Ticks + 100; GameScene scene = Client.Game.GetScene <GameScene>(); Span <char> span = stackalloc char[256]; ValueStringBuilder sb = new ValueStringBuilder(span); if (IsMinimized && scene != null) { sb.Append (string.Format( DEBUG_STRING_0, CUOEnviroment.CurrentRefreshRate, 0, 0, !World.InGame ? 1f : scene.Camera.Zoom, scene.RenderedObjectsCount ) ); sb.Append($"- CUO version: {CUOEnviroment.Version}, Client version: {Settings.GlobalSettings.ClientVersion}\n"); //_sb.AppendFormat(DEBUG_STRING_1, Engine.DebugInfo.MobilesRendered, Engine.DebugInfo.ItemsRendered, Engine.DebugInfo.StaticsRendered, Engine.DebugInfo.MultiRendered, Engine.DebugInfo.LandsRendered, Engine.DebugInfo.EffectsRendered); sb.Append(string.Format(DEBUG_STRING_2, World.InGame ? $"{World.Player.X}, {World.Player.Y}, {World.Player.Z}" : "0xFFFF, 0xFFFF, 0", Mouse.Position, SelectedObject.Object is GameObject gobj ? $"{gobj.X}, {gobj.Y}, {gobj.Z}" : "0xFFFF, 0xFFFF, 0")); sb.Append(string.Format(DEBUG_STRING_3, ReadObject(SelectedObject.Object))); if (CUOEnviroment.Profiler) { double timeDraw = Profiler.GetContext("RenderFrame").TimeInContext; double timeUpdate = Profiler.GetContext("Update").TimeInContext; double timeFixedUpdate = Profiler.GetContext("FixedUpdate").TimeInContext; double timeOutOfContext = Profiler.GetContext("OutOfContext").TimeInContext; //double timeTotalCheck = timeOutOfContext + timeDraw + timeUpdate; double timeTotal = Profiler.TrackedTime; double avgDrawMs = Profiler.GetContext("RenderFrame").AverageTime; sb.Append("- Profiling\n"); sb.Append ( string.Format ( " Draw:{0:0.0}% Update:{1:0.0}% FixedUpd:{2:0.0} AvgDraw:{3:0.0}ms {4}\n", 100d * (timeDraw / timeTotal), 100d * (timeUpdate / timeTotal), 100d * (timeFixedUpdate / timeTotal), avgDrawMs, CUOEnviroment.CurrentRefreshRate ) ); } } else if (scene != null && scene.Camera.Zoom != 1f) { sb.Append(string.Format(DEBUG_STRING_SMALL, CUOEnviroment.CurrentRefreshRate, !World.InGame ? 1f : scene.Camera.Zoom)); } else { sb.Append(string.Format(DEBUG_STRING_SMALL_NO_ZOOM, CUOEnviroment.CurrentRefreshRate)); } _cacheText = sb.ToString(); sb.Dispose(); Vector2 size = Fonts.Bold.MeasureString(_cacheText); _alphaBlendControl.Width = Width = (int)(size.X + 20); _alphaBlendControl.Height = Height = (int)(size.Y + 20); WantUpdateSize = true; } }
public static string Combine(params string[] paths) { if (paths == null) { throw new ArgumentNullException(nameof(paths)); } int maxSize = 0; int firstComponent = 0; // We have two passes, the first calculates how large a buffer to allocate and does some precondition // checks on the paths passed in. The second actually does the combination. for (int i = 0; i < paths.Length; i++) { if (paths[i] == null) { throw new ArgumentNullException(nameof(paths)); } if (paths[i].Length == 0) { continue; } if (IsPathRooted(paths[i])) { firstComponent = i; maxSize = paths[i].Length; } else { maxSize += paths[i].Length; } char ch = paths[i][paths[i].Length - 1]; if (!PathInternal.IsDirectorySeparator(ch)) { maxSize++; } } Span <char> initialBuffer = stackalloc char[260]; // MaxShortPath on Windows var builder = new ValueStringBuilder(initialBuffer); builder.EnsureCapacity(maxSize); for (int i = firstComponent; i < paths.Length; i++) { if (paths[i].Length == 0) { continue; } if (builder.Length == 0) { builder.Append(paths[i]); } else { char ch = builder[builder.Length - 1]; if (!PathInternal.IsDirectorySeparator(ch)) { builder.Append(PathInternal.DirectorySeparatorChar); } builder.Append(paths[i]); } } return(builder.ToString()); }