< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 152
Coverable lines: 152
Total lines: 464
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 59
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
Append(...)0%39390%
.ctor(...)100%110%
.cctor()100%110%
ParseByChar(...)0%14140%
IsDigit(...)0%660%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewNumber.cs

#LineLine coverage
 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
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Numerics;
 7using System.Runtime.InteropServices;
 8using System.Text;
 9
 10namespace System.Globalization
 11{
 12    ////////////////////////////////////////////////////////////////////////////
 13    //
 14    // Used in HebrewNumber.ParseByChar to maintain the context information (
 15    // the state in the state machine and current Hebrew number values, etc.)
 16    // when parsing Hebrew number character by character.
 17    //
 18    ////////////////////////////////////////////////////////////////////////////
 19
 20    internal struct HebrewNumberParsingContext
 21    {
 22        // The current state of the state machine for parsing Hebrew numbers.
 23        internal HebrewNumber.HS state;
 24        // The current value of the Hebrew number.
 25        // The final value is determined when state is FoundEndOfHebrewNumber.
 26        internal int result;
 27
 28        public HebrewNumberParsingContext(int result)
 29        {
 30            // Set the start state of the state machine for parsing Hebrew numbers.
 31            state = HebrewNumber.HS.Start;
 32            this.result = result;
 33        }
 34    }
 35
 36    ////////////////////////////////////////////////////////////////////////////
 37    //
 38    // Please see ParseByChar() for comments about different states defined here.
 39    //
 40    ////////////////////////////////////////////////////////////////////////////
 41
 42    internal enum HebrewNumberParsingState
 43    {
 44        InvalidHebrewNumber,
 45        NotHebrewDigit,
 46        FoundEndOfHebrewNumber,
 47        ContinueParsing,
 48    }
 49
 50    ////////////////////////////////////////////////////////////////////////////
 51    //
 52    // class HebrewNumber
 53    //
 54    //  Provides static methods for formatting integer values into
 55    //  Hebrew text and parsing Hebrew number text.
 56    //
 57    //  Limitations:
 58    //      Parse can only handle value 1 ~ 999.
 59    //      Append() can only handle 1 ~ 999. If value is greater than 5000,
 60    //      5000 will be subtracted from the value.
 61    //
 62    ////////////////////////////////////////////////////////////////////////////
 63
 64    internal static class HebrewNumber
 65    {
 66        ////////////////////////////////////////////////////////////////////////////
 67        //
 68        //  Append
 69        //
 70        //  Converts the given number to Hebrew letters according to the numeric
 71        //  value of each Hebrew letter, appending to the supplied StringBuilder.
 72        //  Basically, this converts the lunar year and the lunar month to letters.
 73        //
 74        //  The character of a year is described by three letters of the Hebrew
 75        //  alphabet, the first and third giving, respectively, the days of the
 76        //  weeks on which the New Year occurs and Passover begins, while the
 77        //  second is the initial of the Hebrew word for defective, normal, or
 78        //  complete.
 79        //
 80        //  Defective Year : Both Heshvan and Kislev are defective (353 or 383 days)
 81        //  Normal Year    : Heshvan is defective, Kislev is full  (354 or 384 days)
 82        //  Complete Year  : Both Heshvan and Kislev are full      (355 or 385 days)
 83        //
 84        ////////////////////////////////////////////////////////////////////////////
 85
 86        internal static void Append<TChar>(ref ValueListBuilder<TChar> outputBuffer, int Number) where TChar : unmanaged
 87        {
 088            int outputBufferStartingLength = outputBuffer.Length;
 89
 090            char cTens = '\x0';
 91            char cUnits;               // tens and units chars
 92            int Hundreds, Tens;              // hundreds and tens values
 93
 94            //
 95            //  Adjust the number if greater than 5000.
 96            //
 097            if (Number > 5000)
 98            {
 099                Number -= 5000;
 100            }
 101
 0102            Debug.Assert(Number > 0 && Number <= 999, "Number is out of range.");
 103
 104            //
 105            //  Get the Hundreds.
 106            //
 0107            Hundreds = Number / 100;
 108
 0109            if (Hundreds > 0)
 110            {
 0111                Number -= Hundreds * 100;
 112                // \x05e7 = 100
 113                // \x05e8 = 200
 114                // \x05e9 = 300
 115                // \x05ea = 400
 116                // If the number is greater than 400, use the multiples of 400.
 0117                for (int i = 0; i < (Hundreds / 4); i++)
 118                {
 0119                    DateTimeFormat.AppendChar(ref outputBuffer, '\x05ea');
 120                }
 121
 0122                int remains = Hundreds % 4;
 0123                if (remains > 0)
 124                {
 0125                    DateTimeFormat.AppendChar(ref outputBuffer, (char)('\x05e6' + remains));
 126                }
 127            }
 128
 129            //
 130            //  Get the Tens.
 131            //
 0132            Tens = Number / 10;
 0133            Number %= 10;
 134
 135            switch (Tens)
 136            {
 137                case (0):
 0138                    cTens = '\x0';
 0139                    break;
 140                case (1):
 0141                    cTens = '\x05d9';          // Hebrew Letter Yod
 0142                    break;
 143                case (2):
 0144                    cTens = '\x05db';          // Hebrew Letter Kaf
 0145                    break;
 146                case (3):
 0147                    cTens = '\x05dc';          // Hebrew Letter Lamed
 0148                    break;
 149                case (4):
 0150                    cTens = '\x05de';          // Hebrew Letter Mem
 0151                    break;
 152                case (5):
 0153                    cTens = '\x05e0';          // Hebrew Letter Nun
 0154                    break;
 155                case (6):
 0156                    cTens = '\x05e1';          // Hebrew Letter Samekh
 0157                    break;
 158                case (7):
 0159                    cTens = '\x05e2';          // Hebrew Letter Ayin
 0160                    break;
 161                case (8):
 0162                    cTens = '\x05e4';          // Hebrew Letter Pe
 0163                    break;
 164                case (9):
 0165                    cTens = '\x05e6';          // Hebrew Letter Tsadi
 166                    break;
 167            }
 168
 169            //
 170            //  Get the Units.
 171            //
 0172            cUnits = (char)(Number > 0 ? ((int)'\x05d0' + Number - 1) : 0);
 173
 0174            if ((cUnits == '\x05d4') &&            // Hebrew Letter He  (5)
 0175                (cTens == '\x05d9'))
 176            {              // Hebrew Letter Yod (10)
 0177                cUnits = '\x05d5';                 // Hebrew Letter Vav (6)
 0178                cTens = '\x05d8';                 // Hebrew Letter Tet (9)
 179            }
 180
 0181            if ((cUnits == '\x05d5') &&            // Hebrew Letter Vav (6)
 0182                (cTens == '\x05d9'))
 183            {               // Hebrew Letter Yod (10)
 0184                cUnits = '\x05d6';                 // Hebrew Letter Zayin (7)
 0185                cTens = '\x05d8';                 // Hebrew Letter Tet (9)
 186            }
 187
 188            //
 189            //  Copy the appropriate info to the given buffer.
 190            //
 191
 0192            if (cTens != '\x0')
 193            {
 0194                DateTimeFormat.AppendChar(ref outputBuffer, cTens);
 195            }
 196
 0197            if (cUnits != '\x0')
 198            {
 0199                DateTimeFormat.AppendChar(ref outputBuffer, cUnits);
 200            }
 201
 0202            if (outputBuffer.Length - outputBufferStartingLength > 1)
 203            {
 0204                if (typeof(TChar) == typeof(char))
 205                {
 0206                    TChar last = outputBuffer[^1];
 0207                    outputBuffer.Length--;
 0208                    outputBuffer.Append(TChar.CastFrom('"'));
 0209                    outputBuffer.Append(last);
 210                }
 211                else
 212                {
 0213                    Debug.Assert(typeof(TChar) == typeof(byte));
 0214                    Rune.DecodeLastFromUtf8(MemoryMarshal.AsBytes(outputBuffer.AsSpan()), out Rune value, out int bytesC
 0215                    outputBuffer.Length -= bytesConsumed;
 0216                    outputBuffer.Append(TChar.CastFrom('"'));
 0217                    DateTimeFormat.AppendChar(ref outputBuffer, (char)value.Value);
 218                }
 219            }
 220            else
 221            {
 0222                DateTimeFormat.AppendChar(ref outputBuffer, '\'');
 223            }
 0224        }
 225
 226        ////////////////////////////////////////////////////////////////////////////
 227        //
 228        // Token used to tokenize a Hebrew word into tokens so that we can use in the
 229        // state machine.
 230        //
 231        ////////////////////////////////////////////////////////////////////////////
 232
 233        private enum HebrewToken : short
 234        {
 235            Invalid = -1,
 236            Digit400 = 0,
 237            Digit200_300 = 1,
 238            Digit100 = 2,
 239            Digit10 = 3,    // 10 ~ 90
 240            Digit1 = 4,     // 1, 2, 3, 4, 5, 8,
 241            Digit6_7 = 5,
 242            Digit7 = 6,
 243            Digit9 = 7,
 244            SingleQuote = 8,
 245            DoubleQuote = 9,
 246        }
 247
 248        ////////////////////////////////////////////////////////////////////////////
 249        //
 250        // This class is used to map a token into its Hebrew digit value.
 251        //
 252        ////////////////////////////////////////////////////////////////////////////
 253
 254        private readonly struct HebrewValue
 255        {
 256            internal readonly HebrewToken token;
 257            internal readonly short value;
 258
 259            internal HebrewValue(HebrewToken token, short value)
 260            {
 0261                this.token = token;
 0262                this.value = value;
 0263            }
 264        }
 265
 266        //
 267        // Map a Hebrew character from U+05D0 ~ U+05EA to its digit value.
 268        // The value is -1 if the Hebrew character does not have a associated value.
 269        //
 0270        private static readonly HebrewValue[] s_hebrewValues = [
 0271            new HebrewValue(HebrewToken.Digit1, 1), // '\x05d0
 0272            new HebrewValue(HebrewToken.Digit1, 2), // '\x05d1
 0273            new HebrewValue(HebrewToken.Digit1, 3), // '\x05d2
 0274            new HebrewValue(HebrewToken.Digit1, 4), // '\x05d3
 0275            new HebrewValue(HebrewToken.Digit1, 5), // '\x05d4
 0276            new HebrewValue(HebrewToken.Digit6_7, 6), // '\x05d5
 0277            new HebrewValue(HebrewToken.Digit6_7, 7), // '\x05d6
 0278            new HebrewValue(HebrewToken.Digit1, 8), // '\x05d7
 0279            new HebrewValue(HebrewToken.Digit9, 9), // '\x05d8
 0280            new HebrewValue(HebrewToken.Digit10, 10), // '\x05d9; // Hebrew Letter Yod
 0281            new HebrewValue(HebrewToken.Invalid, -1), // '\x05da;
 0282            new HebrewValue(HebrewToken.Digit10, 20), // '\x05db; // Hebrew Letter Kaf
 0283            new HebrewValue(HebrewToken.Digit10, 30), // '\x05dc; // Hebrew Letter Lamed
 0284            new HebrewValue(HebrewToken.Invalid, -1), // '\x05dd;
 0285            new HebrewValue(HebrewToken.Digit10, 40), // '\x05de; // Hebrew Letter Mem
 0286            new HebrewValue(HebrewToken.Invalid, -1), // '\x05df;
 0287            new HebrewValue(HebrewToken.Digit10, 50), // '\x05e0; // Hebrew Letter Nun
 0288            new HebrewValue(HebrewToken.Digit10, 60), // '\x05e1; // Hebrew Letter Samekh
 0289            new HebrewValue(HebrewToken.Digit10, 70), // '\x05e2; // Hebrew Letter Ayin
 0290            new HebrewValue(HebrewToken.Invalid, -1), // '\x05e3;
 0291            new HebrewValue(HebrewToken.Digit10, 80), // '\x05e4; // Hebrew Letter Pe
 0292            new HebrewValue(HebrewToken.Invalid, -1), // '\x05e5;
 0293            new HebrewValue(HebrewToken.Digit10, 90), // '\x05e6; // Hebrew Letter Tsadi
 0294            new HebrewValue(HebrewToken.Digit100, 100), // '\x05e7;
 0295            new HebrewValue(HebrewToken.Digit200_300, 200), // '\x05e8;
 0296            new HebrewValue(HebrewToken.Digit200_300, 300), // '\x05e9;
 0297            new HebrewValue(HebrewToken.Digit400, 400), // '\x05ea;
 0298        ];
 299
 300        private const int minHebrewNumberCh = 0x05d0;
 0301        private static readonly char s_maxHebrewNumberCh = (char)(minHebrewNumberCh + s_hebrewValues.Length - 1);
 302
 303        ////////////////////////////////////////////////////////////////////////////
 304        //
 305        // Hebrew number parsing State
 306        // The current state and the next token will lead to the next state in the state machine.
 307        // DQ = Double Quote
 308        //
 309        ////////////////////////////////////////////////////////////////////////////
 310
 311        internal enum HS : sbyte
 312        {
 313            _err = -1,          // an error state
 314            Start = 0,
 315            S400 = 1,           // a Hebrew digit 400
 316            S400_400 = 2,       // Two Hebrew digit 400
 317            S400_X00 = 3,       // Two Hebrew digit 400 and followed by 100
 318            S400_X0 = 4,       // Hebrew digit 400 and followed by 10 ~ 90
 319            X00_DQ = 5,         // A hundred number and followed by a double quote.
 320            S400_X00_X0 = 6,
 321            X0_DQ = 7,          // A two-digit number and followed by a double quote.
 322            X = 8,              // A single digit Hebrew number.
 323            X0 = 9,            // A two-digit Hebrew number
 324            X00 = 10,           // A three-digit Hebrew number
 325            S400_DQ = 11,       // A Hebrew digit 400 and followed by a double quote.
 326            S400_400_DQ = 12,
 327            S400_400_100 = 13,
 328            S9 = 14,            // Hebrew digit 9
 329            X00_S9 = 15,        // A hundered number and followed by a digit 9
 330            S9_DQ = 16,         // Hebrew digit 9 and followed by a double quote
 331            END = 100,          // A terminial state is reached.
 332        }
 333
 334        //
 335        // The state machine for Hebrew number passing.
 336        //
 0337        private static readonly HS[] s_numberPassingState =
 0338        [
 0339            // 400            300/200         100             90~10           8~1      6,       7,       9,          '  
 0340            /* 0 */
 0341                             HS.S400,       HS.X00,         HS.X00,          HS.X0,          HS.X,    HS.X,    HS.X,    
 0342            /* 1: S400 */
 0343                             HS.S400_400,   HS.S400_X00,    HS.S400_X00,     HS.S400_X0,     HS._err, HS._err, HS._err, 
 0344            /* 2: S400_400 */
 0345                             HS._err,       HS._err,        HS.S400_400_100, HS.S400_X0,     HS._err, HS._err, HS._err, 
 0346            /* 3: S400_X00 */
 0347                             HS._err,       HS._err,        HS._err,         HS.S400_X00_X0, HS._err, HS._err, HS._err, 
 0348            /* 4: S400_X0 */
 0349                             HS._err,       HS._err,        HS._err,         HS._err,        HS._err, HS._err, HS._err, 
 0350            /* 5: X00_DQ */
 0351                             HS._err,       HS._err,        HS._err,         HS.END,         HS.END,  HS.END,  HS.END,  
 0352            /* 6: S400_X00_X0 */
 0353                             HS._err,       HS._err,        HS._err,         HS._err,        HS._err, HS._err, HS._err, 
 0354            /* 7: X0_DQ */
 0355                             HS._err,       HS._err,        HS._err,         HS._err,        HS.END,  HS.END,  HS.END,  
 0356            /* 8: X */
 0357                             HS._err,       HS._err,        HS._err,         HS._err,        HS._err, HS._err, HS._err, 
 0358            /* 9: X0 */
 0359                             HS._err,       HS._err,        HS._err,         HS._err,        HS._err, HS._err, HS._err, 
 0360            /* 10: X00 */
 0361                             HS._err,       HS._err,        HS._err,         HS.S400_X0,     HS._err, HS._err, HS._err, 
 0362            /* 11: S400_DQ */
 0363                             HS.END,        HS.END,         HS.END,          HS.END,         HS.END,  HS.END,  HS.END,  
 0364            /* 12: S400_400_DQ*/
 0365                             HS._err,       HS._err,        HS.END,          HS.END,         HS.END,  HS.END,  HS.END,  
 0366            /* 13: S400_400_100*/
 0367                             HS._err,       HS._err,        HS._err,         HS.S400_X00_X0, HS._err, HS._err, HS._err, 
 0368            /* 14: S9 */
 0369                             HS._err,       HS._err,        HS._err,         HS._err,        HS._err, HS._err, HS._err, 
 0370            /* 15: X00_S9 */
 0371                             HS._err,       HS._err,        HS._err,         HS._err,        HS._err, HS._err, HS._err, 
 0372            /* 16: S9_DQ */
 0373                             HS._err,       HS._err,        HS._err,         HS._err,        HS._err, HS.END,  HS.END,  
 0374        ];
 375
 376        // Count of valid HebrewToken, column count in the NumberPassingState array
 377        private const int HebrewTokenCount = 10;
 378
 379        ////////////////////////////////////////////////////////////////////////
 380        //
 381        //  Actions:
 382        //      Parse the Hebrew number by passing one character at a time.
 383        //      The state between characters are maintained at HebrewNumberPassingContext.
 384        //  Returns:
 385        //      Return a enum of HebrewNumberParsingState.
 386        //          NotHebrewDigit: The specified ch is not a valid Hebrew digit.
 387        //          InvalidHebrewNumber: After parsing the specified ch, it will lead into
 388        //              an invalid Hebrew number text.
 389        //          FoundEndOfHebrewNumber: A terminal state is reached.  This means that
 390        //              we find a valid Hebrew number text after the specified ch is parsed.
 391        //          ContinueParsing: The specified ch is a valid Hebrew digit, and
 392        //              it will lead into a valid state in the state machine, we should
 393        //              continue to parse incoming characters.
 394        //
 395        ////////////////////////////////////////////////////////////////////////
 396
 397        internal static HebrewNumberParsingState ParseByChar(char ch, ref HebrewNumberParsingContext context)
 398        {
 0399            Debug.Assert(s_numberPassingState.Length == HebrewTokenCount * ((int)HS.S9_DQ + 1));
 400
 401            HebrewToken token;
 0402            if (ch == '\'')
 403            {
 0404                token = HebrewToken.SingleQuote;
 405            }
 0406            else if (ch == '\"')
 407            {
 0408                token = HebrewToken.DoubleQuote;
 409            }
 410            else
 411            {
 0412                int index = (int)ch - minHebrewNumberCh;
 0413                if (index >= 0 && index < s_hebrewValues.Length)
 414                {
 0415                    token = s_hebrewValues[index].token;
 0416                    if (token == HebrewToken.Invalid)
 417                    {
 0418                        return HebrewNumberParsingState.NotHebrewDigit;
 419                    }
 0420                    context.result += s_hebrewValues[index].value;
 421                }
 422                else
 423                {
 424                    // Not in valid Hebrew digit range.
 0425                    return HebrewNumberParsingState.NotHebrewDigit;
 426                }
 427            }
 0428            context.state = s_numberPassingState[(int)context.state * (int)HebrewTokenCount + (int)token];
 0429            if (context.state == HS._err)
 430            {
 431                // Invalid Hebrew state.  This indicates an incorrect Hebrew number.
 0432                return HebrewNumberParsingState.InvalidHebrewNumber;
 433            }
 0434            if (context.state == HS.END)
 435            {
 436                // Reach a terminal state.
 0437                return HebrewNumberParsingState.FoundEndOfHebrewNumber;
 438            }
 439            // We should continue to parse.
 0440            return HebrewNumberParsingState.ContinueParsing;
 441        }
 442
 443        ////////////////////////////////////////////////////////////////////////
 444        //
 445        // Actions:
 446        //  Check if the ch is a valid Hebrew number digit.
 447        //  This function will return true if the specified char is a legal Hebrew
 448        //  digit character, single quote, or double quote.
 449        // Returns:
 450        //  true if the specified character is a valid Hebrew number character.
 451        //
 452        ////////////////////////////////////////////////////////////////////////
 453
 454        internal static bool IsDigit(char ch)
 455        {
 0456            if (ch >= minHebrewNumberCh && ch <= s_maxHebrewNumberCh)
 457            {
 0458                return s_hebrewValues[ch - minHebrewNumberCh].value >= 0;
 459            }
 0460            return ch == '\'' || ch == '\"';
 461        }
 462    }
 463}
 464