< Summary

Class:GDX.SegmentedString
Assembly:GDX
File(s):D:/BuildAgent/work/GDX-Documentation/Projects/GDX_Development/Packages/com.dotbunny.gdx/GDX/SegmentedString.cs
Covered lines:128
Uncovered lines:0
Coverable lines:128
Total lines:276
Line coverage:100% (128 of 128)
Covered branches:0
Total branches:0
Covered methods:11
Total methods:11
Method coverage:100% (11 of 11)

Coverage History

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AsCharArray()0%110100%
AsCharArray(...)0%110100%
AsString()0%110100%
AsString(...)0%110100%
GetCount()0%110100%
GetHashCode()0%110100%
GetHashCode(...)0%110100%
GetOffset(...)0%110100%
GetSegmentLength(...)0%110100%
SplitOnNonAlphaNumericToLower(...)0%12120100%
SplitOnNonAlphaNumericToLowerHashed(...)0%15150100%

File(s)

D:/BuildAgent/work/GDX-Documentation/Projects/GDX_Development/Packages/com.dotbunny.gdx/GDX/SegmentedString.cs

#LineLine coverage
 1// Copyright (c) 2020-2022 dotBunny Inc.
 2// dotBunny licenses this file to you under the BSL-1.0 license.
 3// See the LICENSE file in the project root for more information.
 4
 5using System;
 6using Unity.Mathematics;
 7
 8namespace GDX
 9{
 10    /// <summary>
 11    ///     A segmented collection of <see cref="char"/>.
 12    /// </summary>
 13    public struct SegmentedString
 14    {
 15        /// <summary>
 16        ///     The initial array of characters.
 17        /// </summary>
 18        char[] m_Characters;
 19
 20        /// <summary>
 21        ///     Datastore of word segment information.
 22        /// </summary>
 23        /// <remarks>
 24        ///     <list type="table">
 25        ///         <listheader>
 26        ///             <term>Axis</term>
 27        ///             <description>Typical Usage</description>
 28        ///         </listheader>
 29        ///         <item>
 30        ///             <term>x</term>
 31        ///             <description>The start offset in <see cref="m_Characters"/> of a word.</description>
 32        ///         </item>
 33        ///         <item>
 34        ///             <term>y</term>
 35        ///             <description>The length of the word.</description>
 36        ///         </item>
 37        ///         <item>
 38        ///             <term>z</term>
 39        ///             <description>The calculated <see cref="StringExtensions.GetStableHashCode"/> for the word.</desc
 40        ///         </item>
 41        ///     </list>
 42        /// </remarks>
 43        int3[] m_Segments;
 44
 45        /// <summary>
 46        ///     The number of words.
 47        /// </summary>
 48        int m_Count;
 49
 50        /// <summary>
 51        ///     The calculated <see cref="StringExtensions.GetStableHashCode"/> for the entirety of <see cref="m_Charact
 52        /// </summary>
 53        int m_HashCode;
 54
 55        /// <summary>
 56        ///     Get the <see cref="m_Characters"/> array.
 57        /// </summary>
 58        /// <returns>A <see cref="char"/> array.</returns>
 59        public char[] AsCharArray()
 160        {
 161            return m_Characters;
 162        }
 63
 64        public char[] AsCharArray(int segmentIndex)
 165        {
 166            char[] returnArray = new char[m_Segments[segmentIndex].y];
 167            Array.Copy(m_Characters, m_Segments[segmentIndex].x,
 68                returnArray, 0, m_Segments[segmentIndex].y);
 169            return returnArray;
 170        }
 71
 72        public string AsString()
 173        {
 174            return new string(m_Characters);
 175        }
 76
 77        public string AsString(int segmentIndex)
 578        {
 579            return new string(m_Characters, m_Segments[segmentIndex].x, m_Segments[segmentIndex].y);
 580        }
 81
 82
 83        public int GetCount()
 184        {
 185            return m_Count;
 186        }
 87
 88
 89        public override int GetHashCode()
 190        {
 191            return m_HashCode;
 192        }
 93
 94        public int GetHashCode(int segmentIndex)
 395        {
 396            return m_Segments[segmentIndex].z;
 397        }
 98
 99        public int GetOffset(int segmentIndex)
 1100        {
 1101            return m_Segments[segmentIndex].x;
 1102        }
 103
 104        public int GetSegmentLength(int segmentIndex)
 1105        {
 1106            return m_Segments[segmentIndex].y;
 1107        }
 108
 109        public static SegmentedString SplitOnNonAlphaNumericToLower(string targetString)
 1110        {
 1111            SegmentedString returnValue = new SegmentedString
 112            {
 113                // Copy to a new character array that we will maintain
 114                m_Characters = targetString.ToCharArray()
 115            };
 116
 1117            int charactersLength = returnValue.m_Characters.Length;
 1118            returnValue.m_Segments = new int3[charactersLength];
 119
 1120            bool isInsideSegment = false;
 121
 80122            for (int i = 0; i < charactersLength; i++)
 39123            {
 124                // Convert our character to its ascii value
 39125                int c = returnValue.m_Characters[i];
 126
 127                // Check character value and shift it if necessary (32)
 39128                if (c >= StringExtensions.AsciiUpperCaseStart && c <= StringExtensions.AsciiUpperCaseEnd)
 5129                {
 5130                    c ^= StringExtensions.AsciiCaseShift;
 131
 132                    // Update value
 5133                    returnValue.m_Characters[i] = (char)c;
 5134                }
 135
 136                // Check our first character
 39137                bool isValid =
 138                    (c >= StringExtensions.AsciiLowerCaseStart && c <= StringExtensions.AsciiLowerCaseEnd) ||
 139                    (c >= StringExtensions.AsciiNumberStart && c <= StringExtensions.AsciiNumberEnd);
 140
 141                // If we are valid, but not in a segment
 39142                if (isValid && !isInsideSegment)
 8143                {
 144                    // Mark start spot
 8145                    returnValue.m_Segments[returnValue.m_Count].x = i;
 146
 8147                    isInsideSegment = true;
 8148                }
 149
 39150                if (!isValid && isInsideSegment)
 7151                {
 152                    // Close out this iteration of a segment
 7153                    isInsideSegment = false;
 7154                    returnValue.m_Segments[returnValue.m_Count].y = i - returnValue.m_Segments[returnValue.m_Count].x;
 7155                    returnValue.m_Count++;
 7156                }
 39157            }
 158
 159            // Finish segment if we didnt before
 1160            if (isInsideSegment)
 1161            {
 1162                returnValue.m_Segments[returnValue.m_Count].y =
 163                    charactersLength - returnValue.m_Segments[returnValue.m_Count].x;
 1164                returnValue.m_Count++;
 1165            }
 166
 1167            return returnValue;
 1168        }
 169
 170        public static SegmentedString SplitOnNonAlphaNumericToLowerHashed(string targetString)
 8171        {
 8172            SegmentedString returnValue = new SegmentedString
 173            {
 174                // Copy to a new character array that we will maintain
 175                m_Characters = targetString.ToCharArray()
 176            };
 177
 8178            int charactersLength = returnValue.m_Characters.Length;
 8179            returnValue.m_Segments = new int3[charactersLength];
 180
 8181            int segmentHashA = 5381;
 8182            int segmentHashB = segmentHashA;
 8183            int hashA = 5381;
 8184            int hashB = hashA;
 8185            bool useAlternateHash = false;
 8186            bool useAlternateSegmentHash = false;
 8187            bool isInsideSegment = false;
 188
 640189            for (int i = 0; i < charactersLength; i++)
 312190            {
 191                // Convert our character to its ascii value
 312192                int c = returnValue.m_Characters[i];
 193
 194                // Check character value and shift it if necessary (32)
 312195                if (c >= StringExtensions.AsciiUpperCaseStart && c <= StringExtensions.AsciiUpperCaseEnd)
 40196                {
 40197                    c ^= StringExtensions.AsciiCaseShift;
 198
 199                    // Update value
 40200                    returnValue.m_Characters[i] = (char)c;
 40201                }
 202
 203
 204                // Hash character for overall hashing
 205                // Flopping hash
 312206                if (!useAlternateHash)
 160207                {
 160208                    hashA = ((hashA << 5) + hashA) ^ c;
 160209                    useAlternateHash = true;
 160210                }
 211                else
 152212                {
 152213                    hashB = ((hashB << 5) + hashB) ^ c;
 152214                    useAlternateHash = false;
 152215                }
 216
 217                // Check our first character
 312218                bool isValid =
 219                    (c >= StringExtensions.AsciiLowerCaseStart && c <= StringExtensions.AsciiLowerCaseEnd) ||
 220                    (c >= StringExtensions.AsciiNumberStart && c <= StringExtensions.AsciiNumberEnd);
 221
 222                // If we are valid, but not in a segment
 312223                if (isValid && !isInsideSegment)
 64224                {
 225                    // Reset hashes
 64226                    segmentHashA = 5381;
 64227                    segmentHashB = segmentHashA;
 64228                    useAlternateSegmentHash = false;
 229
 230                    // Mark start spot
 64231                    returnValue.m_Segments[returnValue.m_Count].x = i;
 232
 64233                    isInsideSegment = true;
 64234                }
 235
 312236                if (isValid)
 240237                {
 238                    // Flopping hash
 240239                    if (!useAlternateSegmentHash)
 136240                    {
 136241                        segmentHashA = ((segmentHashA << 5) + segmentHashA) ^ c;
 136242                        useAlternateSegmentHash = true;
 136243                    }
 244                    else
 104245                    {
 104246                        segmentHashB = ((segmentHashB << 5) + segmentHashB) ^ c;
 104247                        useAlternateSegmentHash = false;
 104248                    }
 240249                }
 250
 312251                if (!isValid && isInsideSegment)
 56252                {
 253                    // Close out this iteration of a segment
 56254                    isInsideSegment = false;
 56255                    returnValue.m_Segments[returnValue.m_Count].y = i - returnValue.m_Segments[returnValue.m_Count].x;
 56256                    returnValue.m_Segments[returnValue.m_Count].z = segmentHashA + segmentHashB * 1566083941;
 56257                    returnValue.m_Count++;
 56258                }
 312259            }
 260
 261            // Finish segment if we didnt before
 8262            if (isInsideSegment)
 8263            {
 8264                returnValue.m_Segments[returnValue.m_Count].y =
 265                    charactersLength - returnValue.m_Segments[returnValue.m_Count].x;
 8266                returnValue.m_Segments[returnValue.m_Count].z = segmentHashA + segmentHashB * 1566083941;
 8267                returnValue.m_Count++;
 8268            }
 269
 270            // Save final hash
 8271            returnValue.m_HashCode = hashA + hashB * 1566083941;
 272
 8273            return returnValue;
 8274        }
 275    }
 276}