|
1 | 1 | using System;
|
2 |
| -using System.Buffers; |
3 | 2 |
|
4 | 3 | namespace T4Sample
|
5 | 4 | {
|
| 5 | + /// <summary> |
| 6 | + /// Extension methods for <see cref="string"/>. |
| 7 | + /// </summary> |
6 | 8 | public static class StringHelper
|
7 | 9 | {
|
| 10 | + /// <summary> |
| 11 | + /// Convert snake_case to PascalCase(UpperCamel). |
| 12 | + /// </summary> |
| 13 | + /// <param name="snakeCaseString">snake_case string.</param> |
| 14 | + /// <returns>PascalCase string.</returns> |
8 | 15 | public static string ToPascalCase(this string snakeCaseString)
|
9 |
| - => string.IsNullOrEmpty(snakeCaseString) ? snakeCaseString : ToCamelCaseInner(snakeCaseString.AsSpan(), true); |
| 16 | + => string.IsNullOrEmpty(snakeCaseString) ? |
| 17 | + snakeCaseString : ToCamelCaseInner(snakeCaseString.AsSpan(), true); |
| 18 | + |
| 19 | + /// <summary> |
| 20 | + /// Convert snake_case to camelCase(lowerCamel). |
| 21 | + /// </summary> |
| 22 | + /// <param name="snakeCaseString">snake_case string.</param> |
| 23 | + /// <returns>camelCase string.</returns> |
10 | 24 | public static string ToCamelCase(this string snakeCaseString)
|
11 |
| - => string.IsNullOrEmpty(snakeCaseString) ? snakeCaseString : ToCamelCaseInner(snakeCaseString.AsSpan(), false); |
| 25 | + => string.IsNullOrEmpty(snakeCaseString) ? |
| 26 | + snakeCaseString : ToCamelCaseInner(snakeCaseString.AsSpan(), false); |
12 | 27 |
|
| 28 | + /// <summary> |
| 29 | + /// Inner Method for <see cref="ToPascalCase(string)"/> and <see cref="ToCamelCase(string)"/>. |
| 30 | + /// </summary> |
| 31 | + /// <param name="source">snake_case chars span.</param> |
| 32 | + /// <param name="isUpper">First Letter is Upper or Lower.</param> |
| 33 | + /// <returns>(Upper|Lower) camelCase string.</returns> |
13 | 34 | private static string ToCamelCaseInner(ReadOnlySpan<char> source, bool isUpper)
|
14 | 35 | {
|
15 |
| - var buffer = ArrayPool<char>.Shared.Rent(source.Length); |
16 |
| - try |
17 |
| - { |
18 |
| - var bufferSpan = buffer.AsSpan(); |
| 36 | + // if length is short, use stackalloc to avoid `new`. |
| 37 | + // TODO: use ArrayPool to avoid `new` completely. |
| 38 | + var buffer = source.Length <= 100 ? |
| 39 | + stackalloc char[source.Length] : new char[source.Length]; |
19 | 40 |
|
20 |
| - int written = 0; |
21 |
| - foreach (var c in source) |
| 41 | + int written = 0; |
| 42 | + foreach (var c in source) |
| 43 | + { |
| 44 | + if (c == '_') |
22 | 45 | {
|
23 |
| - if (c == '_') |
24 |
| - { |
25 |
| - // (written != 0) means "Is Not First Letter". |
26 |
| - // if camelCase, isUpper switches `true` when "Is Not First Letter" and comes "_". |
27 |
| - // if PascalCase, isUpper switches `true` when comes "_". |
28 |
| - isUpper |= (written != 0); |
29 |
| - continue; |
30 |
| - } |
31 |
| - |
32 |
| - bufferSpan[written++] = isUpper ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c); |
33 |
| - isUpper = false; |
| 46 | + // (written != 0) means "Is Not First Letter". |
| 47 | + // if camelCase and first letter, (isUpper | "Is Not First Letter") is false. |
| 48 | + // if PascalCase and first letter, (isUpper | "Is Not First Letter") is true. |
| 49 | + // if not first letter, (isUpper | "Is Not First Letter") is true whether isUpper is true or not. |
| 50 | + isUpper |= (written != 0); |
| 51 | + continue; |
34 | 52 | }
|
35 |
| - return (written == 0) ? "" : new string(bufferSpan.Slice(0, written)); |
36 |
| - } |
37 |
| - finally |
38 |
| - { |
39 |
| - ArrayPool<char>.Shared.Return(buffer); |
| 53 | + |
| 54 | + buffer[written++] = isUpper ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c); |
| 55 | + isUpper = false; |
40 | 56 | }
|
| 57 | + return (written == 0) ? "" : new string(buffer.Slice(0, written)); |
41 | 58 | }
|
42 | 59 | }
|
43 | 60 | }
|
0 commit comments