/// <summary> /// Escapes a <see cref="StringSegment"/> as a quoted-string, which is defined by /// <see href="https://tools.ietf.org/html/rfc7230#section-3.2.6">the RFC specification</see>. /// </summary> /// <remarks> /// This will add a backslash before each backslash and quote and add quotes /// around the input. Assumes that the input does not have quotes around it, /// as this method will add them. Throws if the input contains any invalid escape characters, /// as defined by rfc7230. /// </remarks> /// <param name="input">The input to be escaped.</param> /// <returns>An escaped version of the quoted-string.</returns> public static StringSegment EscapeAsQuotedString(StringSegment input) { // By calling this, we know that the string requires quotes around it to be a valid token. var backSlashCount = CountAndCheckCharactersNeedingBackslashesWhenEncoding(input); var stringBuilder = new InplaceStringBuilder(input.Length + backSlashCount + 2); // 2 for quotes stringBuilder.Append('\"'); for (var i = 0; i < input.Length; i++) { if (input[i] == '\\' || input[i] == '\"') { stringBuilder.Append('\\'); } else if ((input[i] <= 0x1F || input[i] == 0x7F) && input[i] != 0x09) { // Control characters are not allowed in a quoted-string, which include all characters // below 0x1F (except for 0x09 (TAB)) and 0x7F. throw new FormatException($"Invalid control character '{input[i]}' in input."); } stringBuilder.Append(input[i]); } stringBuilder.Append('\"'); return(stringBuilder.ToString()); }
public static string ToHexString(this Span <byte> bytes) { if (HexLookupLowercase == null) { lock (HexLookupLock) { if (HexLookupLowercase == null) { HexLookupLowercase = new string[256]; for (byte b = 0; b <= 255; b++) { HexLookupLowercase[b] = b.ToString("x2"); if (b == 255) { break; } } } } } var sb = new InplaceStringBuilder(bytes.Length * 2); foreach (var b in bytes) { sb.Append(HexLookupLowercase[b]); } return(sb.ToString()); }
public static string ToRfc1123String(this DateTimeOffset dateTime, bool quoted) { var universal = dateTime.UtcDateTime; var length = quoted ? QuotedRfc1123DateLength : Rfc1123DateLength; var target = new InplaceStringBuilder(length); if (quoted) { target.Append(Quote); } target.Append(DayNames[(int)universal.DayOfWeek]); target.Append(Comma); target.Append(Space); AppendNumber(ref target, universal.Day); target.Append(Space); target.Append(MonthNames[universal.Month - 1]); target.Append(Space); AppendYear(ref target, universal.Year); target.Append(Space); AppendTimeOfDay(ref target, universal.TimeOfDay); target.Append(Space); target.Append(Gmt); if (quoted) { target.Append(Quote); } return(target.ToString()); }
// To enable unit testing internal static string DecodeToBase64String(string text) { if (string.IsNullOrEmpty(text)) { return(text); } var padLength = 3 - ((text.Length + 3) % 4); var inplaceStringBuilder = new InplaceStringBuilder(capacity: text.Length + padLength); for (var i = 0; i < text.Length; i++) { if (text[i] == '-') { inplaceStringBuilder.Append('+'); } else if (text[i] == '_') { inplaceStringBuilder.Append('/'); } else { inplaceStringBuilder.Append(text[i]); } } for (var i = 0; i < padLength; i++) { inplaceStringBuilder.Append('='); } return(inplaceStringBuilder.ToString()); }
/// <summary> /// Given a quoted-string as defined by <see href="https://tools.ietf.org/html/rfc7230#section-3.2.6">the RFC specification</see>, /// removes quotes and unescapes backslashes and quotes. This assumes that the input is a valid quoted-string. /// </summary> /// <param name="input">The quoted-string to be unescaped.</param> /// <returns>An unescaped version of the quoted-string.</returns> public static StringSegment UnescapeAsQuotedString(StringSegment input) { input = RemoveQuotes(input); // First pass to calculate the size of the InplaceStringBuilder var backSlashCount = CountBackslashesForDecodingQuotedString(input); if (backSlashCount == 0) { return(input); } var stringBuilder = new InplaceStringBuilder(input.Length - backSlashCount); for (var i = 0; i < input.Length; i++) { if (i < input.Length - 1 && input[i] == '\\') { // If there is an backslash character as the last character in the string, // we will assume that it should be included literally in the unescaped string // Ex: "hello\\" => "hello\\" // Also, if a sender adds a quoted pair like '\\''n', // we will assume it is over escaping and just add a n to the string. // Ex: "he\\llo" => "hello" stringBuilder.Append(input[i + 1]); i++; continue; } stringBuilder.Append(input[i]); } return(stringBuilder.ToString()); }
private static void AppendYear(ref InplaceStringBuilder target, int year) { target.Append(GetAsciiChar(year / 1000)); target.Append(GetAsciiChar(year % 1000 / 100)); target.Append(GetAsciiChar(year % 100 / 10)); target.Append(GetAsciiChar(year % 10)); }
// To enable unit testing internal static string EncodeInternal(string base64EncodedString) { var length = base64EncodedString.Length; while (length > 0 && base64EncodedString[length - 1] == '=') { length--; } if (length == 0) { return(string.Empty); } var inplaceStringBuilder = new InplaceStringBuilder(length); for (var i = 0; i < length; i++) { if (base64EncodedString[i] == '+') { inplaceStringBuilder.Append('-'); } else if (base64EncodedString[i] == '/') { inplaceStringBuilder.Append('_'); } else { inplaceStringBuilder.Append(base64EncodedString[i]); } } return(inplaceStringBuilder.ToString()); }
private static void AppendTimeOfDay(ref InplaceStringBuilder target, TimeSpan timeOfDay) { AppendNumber(ref target, timeOfDay.Hours); target.Append(Colon); AppendNumber(ref target, timeOfDay.Minutes); target.Append(Colon); AppendNumber(ref target, timeOfDay.Seconds); }
public void Append_ThrowsIfNotEnoughSpace() { var formatter = new InplaceStringBuilder(1); var exception = Assert.Throws <InvalidOperationException>(() => formatter.Append("123")); Assert.Equal("Not enough capacity to write '3' characters, only '1' left.", exception.Message); }
public void Build_ThrowsIfNotEnoughWritten() { var formatter = new InplaceStringBuilder(5); formatter.Append("123"); var exception = Assert.Throws <InvalidOperationException>(() => formatter.ToString()); Assert.Equal("Entire reserved capacity was not used. Capacity: '5', written '3'.", exception.Message); }
public static string HexEncode(this byte[] bytes) { var sb = new InplaceStringBuilder(bytes.Length * 2); foreach (var _byte in bytes) { sb.Append($"{_byte:x2}"); } return(sb.ToString()); }
public void Capacity_ThrowsIfAppendWasCalled() { var formatter = new InplaceStringBuilder(3); formatter.Append("123"); var exception = Assert.Throws <InvalidOperationException>(() => formatter.Capacity = 5); Assert.Equal("Cannot change capacity after write started.", exception.Message); }
private static void AppendSegment(ref InplaceStringBuilder builder, StringSegment name, StringSegment value) { builder.Append(SeparatorToken); builder.Append(name); if (value != null) { builder.Append(EqualsToken); builder.Append(value); } }
public void ToString_ReturnsStringWithAllAppendedValues() { var s1 = "123"; var c1 = '4'; var s2 = "56789"; var seg = new StringSegment("890123", 2, 2); var formatter = new InplaceStringBuilder(); formatter.Capacity += s1.Length + 1 + s2.Length + seg.Length; formatter.Append(s1); formatter.Append(c1); formatter.Append(s2, 0, 2); formatter.Append(s2, 2, 2); formatter.Append(s2, 4, 1); formatter.Append(seg); Assert.Equal("12345678901", formatter.ToString()); }
protected string NormalizeKey(string templateKey) { if (!(_razorProject is FileSystemRazorProject)) { return(templateKey); } var addLeadingSlash = templateKey[0] != '\\' && templateKey[0] != '/'; var transformSlashes = templateKey.IndexOf('\\') != -1; if (!addLeadingSlash && !transformSlashes) { return(templateKey); } var length = templateKey.Length; if (addLeadingSlash) { length++; } var builder = new InplaceStringBuilder(length); if (addLeadingSlash) { builder.Append('/'); } for (var i = 0; i < templateKey.Length; i++) { var ch = templateKey[i]; if (ch == '\\') { ch = '/'; } builder.Append(ch); } return(builder.ToString()); }
public static string NormalizePath(string path) { var addLeadingSlash = path[0] != '\\' && path[0] != '/'; var transformSlashes = path.IndexOf('\\') != -1; if (!addLeadingSlash && !transformSlashes) { return(path); } var length = path.Length; if (addLeadingSlash) { length++; } var builder = new InplaceStringBuilder(length); if (addLeadingSlash) { builder.Append('/'); } for (var i = 0; i < path.Length; i++) { var ch = path[i]; if (ch == '\\') { ch = '/'; } builder.Append(ch); } return(builder.ToString()); }
private void RewriteStyleAttributes(ResponseAnalysisContext context, string html) { var headIndex = html.IndexOf("</head>", StringComparison.OrdinalIgnoreCase); if (headIndex == -1) { return; } SortedList <string, StringSegment> inlineStyles = null; foreach (var index in FastHtmlParser.FindAllAttributeIndexes(html, "style")) { // get content var inner = FastHtmlParser.GetAttributeValueAtName(html, "style", index); if (inner.Length == 0) { continue; } if (inlineStyles == null) { inlineStyles = new SortedList <string, StringSegment>(); } // compute hash var hash = ComputeHash(inner.Trim()); if (!inlineStyles.ContainsKey(hash)) { inlineStyles.Add(hash, inner); } // add change context.AddChange(TextChange.Remove(html, index, inner.Offset + inner.Length + 1 - index)); var tagIndex = FastHtmlParser.FindOpenTagAtAttribute(html, index); var classAttribute = FastHtmlParser.GetAttributeValueAtTag(html, "class", tagIndex); if (classAttribute.Length > 0) { context.AddChange(TextChange.Insert(html, classAttribute.Offset + classAttribute.Length, "_" + hash)); } else { context.AddChange(FastHtmlParser.CreateInsertAttributeChange(html, tagIndex, "class", "_" + hash)); } } // assemble inline styles if (inlineStyles != null && inlineStyles.Count > 0) { var hashBuilder = new InplaceStringBuilder(inlineStyles.Count * HashBytes * 2); foreach (var entry in inlineStyles) { hashBuilder.Append(entry.Key); } var hash = ComputeHash(hashBuilder.ToString()); if (!InlineContentService.ContainsStyleByHash(hash)) { var contentBuilder = StringBuilderPool.Get(); foreach (var entry in inlineStyles) { contentBuilder.Append('.'); contentBuilder.Append('_'); contentBuilder.Append(entry.Key); contentBuilder.Append('{'); contentBuilder.Append(entry.Value.Value); contentBuilder.Append('}'); } InlineContentService.TryAddStyleByHash(hash, contentBuilder.ToString()); StringBuilderPool.Return(contentBuilder); } var tag = $"<link rel=\"stylesheet\" href=\"/.waf/styles/{hash}\" type=\"text/css\" />"; context.AddChange(TextChange.Insert(html, headIndex, tag)); } }
private static void AppendNumber(ref InplaceStringBuilder target, int number) { target.Append(GetAsciiChar(number / 10)); target.Append(GetAsciiChar(number % 10)); }
static string GetJoinedStringValueFromArray(string[] values) { // Calculate final length var length = 0; for (var i = 0; i < values.Length; i++) { var value = values[i]; // Skip null and empty values if (value != null && value.Length > 0) { if (length > 0) { // Add seperator length++; } length += value.Length; } } #if NETCOREAPP // Create the new string return(string.Create(length, values, (span, strings) => { var offset = 0; // Skip null and empty values for (var i = 0; i < strings.Length; i++) { var value = strings[i]; if (value != null && value.Length > 0) { if (offset > 0) { // Add seperator span[offset] = ','; offset++; } value.AsSpan().CopyTo(span.Slice(offset)); offset += value.Length; } } })); #else #pragma warning disable CS0618 var sb = new InplaceStringBuilder(length); #pragma warning restore CS0618 var hasAdded = false; // Skip null and empty values for (var i = 0; i < values.Length; i++) { var value = values[i]; if (value != null && value.Length > 0) { if (hasAdded) { // Add seperator sb.Append(','); } sb.Append(value); hasAdded = true; } } return(sb.ToString()); #endif }
// name="value"; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; samesite={Strict|Lax}; httponly public override string ToString() { var length = _name.Length + EqualsToken.Length + _value.Length; string expires = null; string maxAge = null; string sameSite = null; if (Expires.HasValue) { expires = HeaderUtilities.FormatDate(Expires.Value); length += SeparatorToken.Length + ExpiresToken.Length + EqualsToken.Length + expires.Length; } if (MaxAge.HasValue) { maxAge = HeaderUtilities.FormatNonNegativeInt64((long)MaxAge.Value.TotalSeconds); length += SeparatorToken.Length + MaxAgeToken.Length + EqualsToken.Length + maxAge.Length; } if (Domain != null) { length += SeparatorToken.Length + DomainToken.Length + EqualsToken.Length + Domain.Length; } if (Path != null) { length += SeparatorToken.Length + PathToken.Length + EqualsToken.Length + Path.Length; } if (Secure) { length += SeparatorToken.Length + SecureToken.Length; } if (SameSite != SameSiteMode.None) { sameSite = SameSite == SameSiteMode.Lax ? SameSiteLaxToken : SameSiteStrictToken; length += SeparatorToken.Length + SameSiteToken.Length + EqualsToken.Length + sameSite.Length; } if (HttpOnly) { length += SeparatorToken.Length + HttpOnlyToken.Length; } var sb = new InplaceStringBuilder(length); sb.Append(_name); sb.Append(EqualsToken); sb.Append(_value); if (expires != null) { AppendSegment(ref sb, ExpiresToken, expires); } if (maxAge != null) { AppendSegment(ref sb, MaxAgeToken, maxAge); } if (Domain != null) { AppendSegment(ref sb, DomainToken, Domain); } if (Path != null) { AppendSegment(ref sb, PathToken, Path); } if (Secure) { AppendSegment(ref sb, SecureToken, null); } if (SameSite != SameSiteMode.None) { AppendSegment(ref sb, SameSiteToken, sameSite); } if (HttpOnly) { AppendSegment(ref sb, HttpOnlyToken, null); } return(sb.ToString()); }