The Labs \ Source Viewer \ SSCLI \ System.Xml.Schema \ XsdDuration

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XsdDuration.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. // <owner current="true" primary="true">akimball</owner>
  15. //------------------------------------------------------------------------------
  16. namespace System.Xml.Schema
  17. {
  18.     using System;
  19.     using System.Diagnostics;
  20.     using System.Text;
  21.    
  22.     /// <summary>
  23.     /// This structure holds components of an Xsd Duration. It is used internally to support Xsd durations without loss
  24.     /// of fidelity. XsdDuration structures are immutable once they've been created.
  25.     /// </summary>
  26.     internal struct XsdDuration
  27.     {
  28.         private int years;
  29.         private int months;
  30.         private int days;
  31.         private int hours;
  32.         private int minutes;
  33.         private int seconds;
  34.         private uint nanoseconds;
  35.         // High bit is used to indicate whether duration is negative
  36.         private const uint NegativeBit = 2147483648u;
  37.        
  38.         private enum Parts
  39.         {
  40.             HasNone = 0,
  41.             HasYears = 1,
  42.             HasMonths = 2,
  43.             HasDays = 4,
  44.             HasHours = 8,
  45.             HasMinutes = 16,
  46.             HasSeconds = 32
  47.         }
  48.        
  49.         internal enum DurationType
  50.         {
  51.             Duration,
  52.             YearMonthDuration,
  53.             DayTimeDuration
  54.         }
  55.        
  56.         /// <summary>
  57.         /// Construct an XsdDuration from component parts.
  58.         /// </summary>
  59.         public XsdDuration(bool isNegative, int years, int months, int days, int hours, int minutes, int seconds, int nanoseconds)
  60.         {
  61.             if (years < 0)
  62.                 throw new ArgumentOutOfRangeException("years");
  63.             if (months < 0)
  64.                 throw new ArgumentOutOfRangeException("months");
  65.             if (days < 0)
  66.                 throw new ArgumentOutOfRangeException("days");
  67.             if (hours < 0)
  68.                 throw new ArgumentOutOfRangeException("hours");
  69.             if (minutes < 0)
  70.                 throw new ArgumentOutOfRangeException("minutes");
  71.             if (seconds < 0)
  72.                 throw new ArgumentOutOfRangeException("seconds");
  73.             if (nanoseconds < 0 || nanoseconds > 999999999)
  74.                 throw new ArgumentOutOfRangeException("nanoseconds");
  75.            
  76.             this.years = years;
  77.             this.months = months;
  78.             this.days = days;
  79.             this.hours = hours;
  80.             this.minutes = minutes;
  81.             this.seconds = seconds;
  82.             this.nanoseconds = (uint)nanoseconds;
  83.            
  84.             if (isNegative)
  85.                 this.nanoseconds |= NegativeBit;
  86.         }
  87.        
  88.         /// <summary>
  89.         /// Construct an XsdDuration from a TimeSpan value.
  90.         /// </summary>
  91.         public XsdDuration(TimeSpan timeSpan) : this(timeSpan, DurationType.Duration)
  92.         {
  93.         }
  94.        
  95.         /// <summary>
  96.         /// Construct an XsdDuration from a TimeSpan value that represents an xsd:duration, an xdt:dayTimeDuration, or
  97.         /// an xdt:yearMonthDuration.
  98.         /// </summary>
  99.         internal XsdDuration(TimeSpan timeSpan, DurationType durationType)
  100.         {
  101.             long ticks = timeSpan.Ticks;
  102.             ulong ticksPos;
  103.             bool isNegative;
  104.            
  105.             if (ticks < 0) {
  106.                 // Note that (ulong) -Int64.MinValue = Int64.MaxValue + 1, which is what we want for that special case
  107.                 isNegative = true;
  108.                 ticksPos = (ulong)-ticks;
  109.             }
  110.             else {
  111.                 isNegative = false;
  112.                 ticksPos = (ulong)ticks;
  113.             }
  114.            
  115.             if (durationType == DurationType.YearMonthDuration) {
  116.                 int years = (int)(ticksPos / ((ulong)TimeSpan.TicksPerDay * 365));
  117.                 int months = (int)((ticksPos % ((ulong)TimeSpan.TicksPerDay * 365)) / ((ulong)TimeSpan.TicksPerDay * 30));
  118.                
  119.                 if (months == 12) {
  120.                     // If remaining days >= 360 and < 365, then round off to year
  121.                     years++;
  122.                     months = 0;
  123.                 }
  124.                
  125.                 this = new XsdDuration(isNegative, years, months, 0, 0, 0, 0, 0);
  126.             }
  127.             else {
  128.                 Debug.Assert(durationType == DurationType.Duration || durationType == DurationType.DayTimeDuration);
  129.                
  130.                 // Tick count is expressed in 100 nanosecond intervals
  131.                 this.nanoseconds = (uint)(ticksPos % 10000000) * 100;
  132.                 if (isNegative)
  133.                     this.nanoseconds |= NegativeBit;
  134.                
  135.                 this.years = 0;
  136.                 this.months = 0;
  137.                 this.days = (int)(ticksPos / (ulong)TimeSpan.TicksPerDay);
  138.                 this.hours = (int)((ticksPos / (ulong)TimeSpan.TicksPerHour) % 24);
  139.                 this.minutes = (int)((ticksPos / (ulong)TimeSpan.TicksPerMinute) % 60);
  140.                 this.seconds = (int)((ticksPos / (ulong)TimeSpan.TicksPerSecond) % 60);
  141.             }
  142.         }
  143.        
  144.         /// <summary>
  145.         /// Constructs an XsdDuration from a string in the xsd:duration format. Components are stored with loss
  146.         /// of fidelity (except in the case of overflow).
  147.         /// </summary>
  148.         public XsdDuration(string s) : this(s, DurationType.Duration)
  149.         {
  150.         }
  151.        
  152.         /// <summary>
  153.         /// Constructs an XsdDuration from a string in the xsd:duration format. Components are stored without loss
  154.         /// of fidelity (except in the case of overflow).
  155.         /// </summary>
  156.         public XsdDuration(string s, DurationType durationType)
  157.         {
  158.             XsdDuration result;
  159.             Exception exception = TryParse(s, durationType, out result);
  160.             if (exception != null) {
  161.                 throw exception;
  162.             }
  163.             this.years = result.Years;
  164.             this.months = result.Months;
  165.             this.days = result.Days;
  166.             this.hours = result.Hours;
  167.             this.minutes = result.Minutes;
  168.             this.seconds = result.Seconds;
  169.             this.nanoseconds = (uint)result.Nanoseconds;
  170.             if (result.IsNegative) {
  171.                 this.nanoseconds |= NegativeBit;
  172.             }
  173.             return;
  174.         }
  175.        
  176.         /// <summary>
  177.         /// Return true if this duration is negative.
  178.         /// </summary>
  179.         public bool IsNegative {
  180.             get { return (this.nanoseconds & NegativeBit) != 0; }
  181.         }
  182.        
  183.         /// <summary>
  184.         /// Return number of years in this duration (stored in 31 bits).
  185.         /// </summary>
  186.         public int Years {
  187.             get { return this.years; }
  188.         }
  189.        
  190.         /// <summary>
  191.         /// Return number of months in this duration (stored in 31 bits).
  192.         /// </summary>
  193.         public int Months {
  194.             get { return this.months; }
  195.         }
  196.        
  197.         /// <summary>
  198.         /// Return number of days in this duration (stored in 31 bits).
  199.         /// </summary>
  200.         public int Days {
  201.             get { return this.days; }
  202.         }
  203.        
  204.         /// <summary>
  205.         /// Return number of hours in this duration (stored in 31 bits).
  206.         /// </summary>
  207.         public int Hours {
  208.             get { return this.hours; }
  209.         }
  210.        
  211.         /// <summary>
  212.         /// Return number of minutes in this duration (stored in 31 bits).
  213.         /// </summary>
  214.         public int Minutes {
  215.             get { return this.minutes; }
  216.         }
  217.        
  218.         /// <summary>
  219.         /// Return number of seconds in this duration (stored in 31 bits).
  220.         /// </summary>
  221.         public int Seconds {
  222.             get { return this.seconds; }
  223.         }
  224.        
  225.         /// <summary>
  226.         /// Return number of nanoseconds in this duration.
  227.         /// </summary>
  228.         public int Nanoseconds {
  229.             get { return (int)(this.nanoseconds & ~NegativeBit); }
  230.         }
  231.        
  232.         /// <summary>
  233.         /// Return number of microseconds in this duration.
  234.         /// </summary>
  235.         public int Microseconds {
  236.             get { return Nanoseconds / 1000; }
  237.         }
  238.        
  239.         /// <summary>
  240.         /// Return number of milliseconds in this duration.
  241.         /// </summary>
  242.         public int Milliseconds {
  243.             get { return Nanoseconds / 1000000; }
  244.         }
  245.        
  246.         /// <summary>
  247.         /// Normalize year-month part and day-time part so that month < 12, hour < 24, minute < 60, and second < 60.
  248.         /// </summary>
  249.         public XsdDuration Normalize()
  250.         {
  251.             int years = Years;
  252.             int months = Months;
  253.             int days = Days;
  254.             int hours = Hours;
  255.             int minutes = Minutes;
  256.             int seconds = Seconds;
  257.            
  258.             try {
  259.                 checked {
  260.                     if (months >= 12) {
  261.                         years += months / 12;
  262.                         months %= 12;
  263.                     }
  264.                    
  265.                     if (seconds >= 60) {
  266.                         minutes += seconds / 60;
  267.                         seconds %= 60;
  268.                     }
  269.                    
  270.                     if (minutes >= 60) {
  271.                         hours += minutes / 60;
  272.                         minutes %= 60;
  273.                     }
  274.                    
  275.                     if (hours >= 24) {
  276.                         days += hours / 24;
  277.                         hours %= 24;
  278.                     }
  279.                 }
  280.             }
  281.             catch (OverflowException) {
  282.                 throw new OverflowException(Res.GetString(Res.XmlConvert_Overflow, ToString(), "Duration"));
  283.             }
  284.            
  285.             return new XsdDuration(IsNegative, years, months, days, hours, minutes, seconds, Nanoseconds);
  286.         }
  287.        
  288.         /// <summary>
  289.         /// Internal helper method that converts an Xsd duration to a TimeSpan value. This code uses the estimate
  290.         /// that there are 365 days in the year and 30 days in a month.
  291.         /// </summary>
  292.         public TimeSpan ToTimeSpan()
  293.         {
  294.             return ToTimeSpan(DurationType.Duration);
  295.         }
  296.        
  297.         /// <summary>
  298.         /// Internal helper method that converts an Xsd duration to a TimeSpan value. This code uses the estimate
  299.         /// that there are 365 days in the year and 30 days in a month.
  300.         /// </summary>
  301.         internal TimeSpan ToTimeSpan(DurationType durationType)
  302.         {
  303.             TimeSpan result;
  304.             Exception exception = TryToTimeSpan(durationType, out result);
  305.             if (exception != null) {
  306.                 throw exception;
  307.             }
  308.             return result;
  309.         }
  310.        
  311.         internal Exception TryToTimeSpan(out TimeSpan result)
  312.         {
  313.             return TryToTimeSpan(DurationType.Duration, out result);
  314.         }
  315.        
  316.         internal Exception TryToTimeSpan(DurationType durationType, out TimeSpan result)
  317.         {
  318.             Exception exception = null;
  319.             ulong ticks = 0;
  320.            
  321.             // Throw error if result cannot fit into a long
  322.             try {
  323.                 checked {
  324.                     // Discard year and month parts if constructing TimeSpan for DayTimeDuration
  325.                     if (durationType != DurationType.DayTimeDuration) {
  326.                         ticks += ((ulong)this.years + (ulong)this.months / 12) * 365;
  327.                         ticks += ((ulong)this.months % 12) * 30;
  328.                     }
  329.                    
  330.                     // Discard day and time parts if constructing TimeSpan for YearMonthDuration
  331.                     if (durationType != DurationType.YearMonthDuration) {
  332.                         ticks += (ulong)this.days;
  333.                        
  334.                         ticks *= 24;
  335.                         ticks += (ulong)this.hours;
  336.                        
  337.                         ticks *= 60;
  338.                         ticks += (ulong)this.minutes;
  339.                        
  340.                         ticks *= 60;
  341.                         ticks += (ulong)this.seconds;
  342.                        
  343.                         // Tick count interval is in 100 nanosecond intervals (7 digits)
  344.                         ticks *= (ulong)TimeSpan.TicksPerSecond;
  345.                         ticks += (ulong)Nanoseconds / 100;
  346.                     }
  347.                     else {
  348.                         // Multiply YearMonth duration by number of ticks per day
  349.                         ticks *= (ulong)TimeSpan.TicksPerDay;
  350.                     }
  351.                    
  352.                     if (IsNegative) {
  353.                         // Handle special case of Int64.MaxValue + 1 before negation, since it would otherwise overflow
  354.                         if (ticks == (ulong)Int64.MaxValue + 1) {
  355.                             result = new TimeSpan(Int64.MinValue);
  356.                         }
  357.                         else {
  358.                             result = new TimeSpan(-((long)ticks));
  359.                         }
  360.                     }
  361.                     else {
  362.                         result = new TimeSpan((long)ticks);
  363.                     }
  364.                     return null;
  365.                 }
  366.             }
  367.             catch (OverflowException) {
  368.                 result = TimeSpan.MinValue;
  369.                 exception = new OverflowException(Res.GetString(Res.XmlConvert_Overflow, durationType, "TimeSpan"));
  370.             }
  371.             return exception;
  372.         }
  373.        
  374.         /// <summary>
  375.         /// Return the string representation of this Xsd duration.
  376.         /// </summary>
  377.         public override string ToString()
  378.         {
  379.             return ToString(DurationType.Duration);
  380.         }
  381.        
  382.         /// <summary>
  383.         /// Return the string representation according to xsd:duration rules, xdt:dayTimeDuration rules, or
  384.         /// xdt:yearMonthDuration rules.
  385.         /// </summary>
  386.         internal string ToString(DurationType durationType)
  387.         {
  388.             StringBuilder sb = new StringBuilder(20);
  389.             int nanoseconds;
  390.             int digit;
  391.             int zeroIdx;
  392.             int len;
  393.            
  394.             if (IsNegative)
  395.                 sb.Append('-');
  396.            
  397.             sb.Append('P');
  398.            
  399.             if (durationType != DurationType.DayTimeDuration) {
  400.                
  401.                 if (this.years != 0) {
  402.                     sb.Append(XmlConvert.ToString(this.years));
  403.                     sb.Append('Y');
  404.                 }
  405.                
  406.                 if (this.months != 0) {
  407.                     sb.Append(XmlConvert.ToString(this.months));
  408.                     sb.Append('M');
  409.                 }
  410.             }
  411.            
  412.             if (durationType != DurationType.YearMonthDuration) {
  413.                 if (this.days != 0) {
  414.                     sb.Append(XmlConvert.ToString(this.days));
  415.                     sb.Append('D');
  416.                 }
  417.                
  418.                 if (this.hours != 0 || this.minutes != 0 || this.seconds != 0 || Nanoseconds != 0) {
  419.                     sb.Append('T');
  420.                     if (this.hours != 0) {
  421.                         sb.Append(XmlConvert.ToString(this.hours));
  422.                         sb.Append('H');
  423.                     }
  424.                    
  425.                     if (this.minutes != 0) {
  426.                         sb.Append(XmlConvert.ToString(this.minutes));
  427.                         sb.Append('M');
  428.                     }
  429.                    
  430.                     nanoseconds = Nanoseconds;
  431.                     if (this.seconds != 0 || nanoseconds != 0) {
  432.                         sb.Append(XmlConvert.ToString(this.seconds));
  433.                         if (nanoseconds != 0) {
  434.                             sb.Append('.');
  435.                            
  436.                             len = sb.Length;
  437.                             sb.Length += 9;
  438.                             zeroIdx = sb.Length - 1;
  439.                            
  440.                             for (int idx = zeroIdx; idx >= len; idx--) {
  441.                                 digit = nanoseconds % 10;
  442.                                 sb[idx] = (char)(digit + '0');
  443.                                
  444.                                 if (zeroIdx == idx && digit == 0)
  445.                                     zeroIdx--;
  446.                                
  447.                                 nanoseconds /= 10;
  448.                             }
  449.                            
  450.                             sb.Length = zeroIdx + 1;
  451.                         }
  452.                         sb.Append('S');
  453.                     }
  454.                 }
  455.                
  456.                 // Zero is represented as "PT0S"
  457.                 if (sb[sb.Length - 1] == 'P')
  458.                     sb.Append("T0S");
  459.             }
  460.             else {
  461.                 // Zero is represented as "T0M"
  462.                 if (sb[sb.Length - 1] == 'P')
  463.                     sb.Append("0M");
  464.             }
  465.            
  466.             return sb.ToString();
  467.         }
  468.        
  469.         static internal Exception TryParse(string s, out XsdDuration result)
  470.         {
  471.             return TryParse(s, DurationType.Duration, out result);
  472.         }
  473.        
  474.         static internal Exception TryParse(string s, DurationType durationType, out XsdDuration result)
  475.         {
  476.             string errorCode;
  477.             int length;
  478.             int value;
  479.             int pos;
  480.             int numDigits;
  481.             Parts parts = Parts.HasNone;
  482.            
  483.             result = new XsdDuration();
  484.            
  485.             s = s.Trim();
  486.             length = s.Length;
  487.            
  488.             pos = 0;
  489.             numDigits = 0;
  490.            
  491.             if (pos >= length)
  492.                 goto InvalidFormat;
  493.            
  494.             if (s[pos] == '-') {
  495.                 pos++;
  496.                 result.nanoseconds = NegativeBit;
  497.             }
  498.             else {
  499.                 result.nanoseconds = 0;
  500.             }
  501.            
  502.             if (pos >= length)
  503.                 goto InvalidFormat;
  504.            
  505.             if (s[pos++] != 'P')
  506.                 goto InvalidFormat;
  507.            
  508.             errorCode = TryParseDigits(s, ref pos, false, out value, out numDigits);
  509.             if (errorCode != null)
  510.                 goto Error;
  511.            
  512.             if (pos >= length)
  513.                 goto InvalidFormat;
  514.            
  515.             if (s[pos] == 'Y') {
  516.                 if (numDigits == 0)
  517.                     goto InvalidFormat;
  518.                
  519.                 parts |= Parts.HasYears;
  520.                 result.years = value;
  521.                 if (++pos == length)
  522.                     goto Done;
  523.                
  524.                 errorCode = TryParseDigits(s, ref pos, false, out value, out numDigits);
  525.                 if (errorCode != null)
  526.                     goto Error;
  527.                
  528.                 if (pos >= length)
  529.                     goto InvalidFormat;
  530.             }
  531.            
  532.             if (s[pos] == 'M') {
  533.                 if (numDigits == 0)
  534.                     goto InvalidFormat;
  535.                
  536.                 parts |= Parts.HasMonths;
  537.                 result.months = value;
  538.                 if (++pos == length)
  539.                     goto Done;
  540.                
  541.                 errorCode = TryParseDigits(s, ref pos, false, out value, out numDigits);
  542.                 if (errorCode != null)
  543.                     goto Error;
  544.                
  545.                 if (pos >= length)
  546.                     goto InvalidFormat;
  547.             }
  548.            
  549.             if (s[pos] == 'D') {
  550.                 if (numDigits == 0)
  551.                     goto InvalidFormat;
  552.                
  553.                 parts |= Parts.HasDays;
  554.                 result.days = value;
  555.                 if (++pos == length)
  556.                     goto Done;
  557.                
  558.                 errorCode = TryParseDigits(s, ref pos, false, out value, out numDigits);
  559.                 if (errorCode != null)
  560.                     goto Error;
  561.                
  562.                 if (pos >= length)
  563.                     goto InvalidFormat;
  564.             }
  565.            
  566.             if (s[pos] == 'T') {
  567.                 if (numDigits != 0)
  568.                     goto InvalidFormat;
  569.                
  570.                 pos++;
  571.                 errorCode = TryParseDigits(s, ref pos, false, out value, out numDigits);
  572.                 if (errorCode != null)
  573.                     goto Error;
  574.                
  575.                 if (pos >= length)
  576.                     goto InvalidFormat;
  577.                
  578.                 if (s[pos] == 'H') {
  579.                     if (numDigits == 0)
  580.                         goto InvalidFormat;
  581.                    
  582.                     parts |= Parts.HasHours;
  583.                     result.hours = value;
  584.                     if (++pos == length)
  585.                         goto Done;
  586.                    
  587.                     errorCode = TryParseDigits(s, ref pos, false, out value, out numDigits);
  588.                     if (errorCode != null)
  589.                         goto Error;
  590.                    
  591.                     if (pos >= length)
  592.                         goto InvalidFormat;
  593.                 }
  594.                
  595.                 if (s[pos] == 'M') {
  596.                     if (numDigits == 0)
  597.                         goto InvalidFormat;
  598.                    
  599.                     parts |= Parts.HasMinutes;
  600.                     result.minutes = value;
  601.                     if (++pos == length)
  602.                         goto Done;
  603.                    
  604.                     errorCode = TryParseDigits(s, ref pos, false, out value, out numDigits);
  605.                     if (errorCode != null)
  606.                         goto Error;
  607.                    
  608.                     if (pos >= length)
  609.                         goto InvalidFormat;
  610.                 }
  611.                
  612.                 if (s[pos] == '.') {
  613.                     pos++;
  614.                    
  615.                     parts |= Parts.HasSeconds;
  616.                     result.seconds = value;
  617.                    
  618.                     errorCode = TryParseDigits(s, ref pos, true, out value, out numDigits);
  619.                     if (errorCode != null)
  620.                         goto Error;
  621.                    
  622.                     if (numDigits == 0) {
  623.                         //If there are no digits after the decimal point, assume 0
  624.                         value = 0;
  625.                     }
  626.                     // Normalize to nanosecond intervals
  627.                     for (; numDigits > 9; numDigits--)
  628.                         value /= 10;
  629.                    
  630.                     for (; numDigits < 9; numDigits++)
  631.                         value *= 10;
  632.                    
  633.                     result.nanoseconds |= (uint)value;
  634.                    
  635.                     if (pos >= length)
  636.                         goto InvalidFormat;
  637.                    
  638.                     if (s[pos] != 'S')
  639.                         goto InvalidFormat;
  640.                     if (++pos == length)
  641.                         goto Done;
  642.                 }
  643.                 else if (s[pos] == 'S') {
  644.                     if (numDigits == 0)
  645.                         goto InvalidFormat;
  646.                    
  647.                     parts |= Parts.HasSeconds;
  648.                     result.seconds = value;
  649.                     if (++pos == length)
  650.                         goto Done;
  651.                 }
  652.             }
  653.            
  654.             // Duration cannot end with digits
  655.             if (numDigits != 0)
  656.                 goto InvalidFormat;
  657.            
  658.             // No further characters are allowed
  659.             if (pos != length)
  660.                 goto InvalidFormat;
  661.             Done:
  662.            
  663.             // At least one part must be defined
  664.             if (parts == Parts.HasNone)
  665.                 goto InvalidFormat;
  666.            
  667.             if (durationType == DurationType.DayTimeDuration) {
  668.                 if ((parts & (Parts.HasYears | Parts.HasMonths)) != 0)
  669.                     goto InvalidFormat;
  670.             }
  671.             else if (durationType == DurationType.YearMonthDuration) {
  672.                 if ((parts & ~(XsdDuration.Parts.HasYears | XsdDuration.Parts.HasMonths)) != 0)
  673.                     goto InvalidFormat;
  674.             }
  675.             return null;
  676.             InvalidFormat:
  677.            
  678.             return new FormatException(Res.GetString(Res.XmlConvert_BadFormat, s, durationType));
  679.             Error:
  680.            
  681.             return new OverflowException(Res.GetString(Res.XmlConvert_Overflow, s, durationType));
  682.         }
  683.        
  684.         /// Helper method that constructs an integer from leading digits starting at s[offset]. "offset" is
  685.         /// updated to contain an offset just beyond the last digit. The number of digits consumed is returned in
  686.         /// cntDigits. The integer is returned (0 if no digits). If the digits cannot fit into an Int32:
  687.         /// 1. If eatDigits is true, then additional digits will be silently discarded (don't count towards numDigits)
  688.         /// 2. If eatDigits is false, an overflow exception is thrown
  689.         private static string TryParseDigits(string s, ref int offset, bool eatDigits, out int result, out int numDigits)
  690.         {
  691.             int offsetStart = offset;
  692.             int offsetEnd = s.Length;
  693.             int digit;
  694.            
  695.             result = 0;
  696.             numDigits = 0;
  697.            
  698.             while (offset < offsetEnd && s[offset] >= '0' && s[offset] <= '9') {
  699.                 digit = s[offset] - '0';
  700.                
  701.                 if (result > (Int32.MaxValue - digit) / 10) {
  702.                     if (!eatDigits) {
  703.                         return Res.XmlConvert_Overflow;
  704.                     }
  705.                    
  706.                     // Skip past any remaining digits
  707.                     numDigits = offset - offsetStart;
  708.                    
  709.                     while (offset < offsetEnd && s[offset] >= '0' && s[offset] <= '9') {
  710.                         offset++;
  711.                     }
  712.                    
  713.                     return null;
  714.                 }
  715.                
  716.                 result = result * 10 + digit;
  717.                 offset++;
  718.             }
  719.            
  720.             numDigits = offset - offsetStart;
  721.             return null;
  722.         }
  723.     }
  724. }

Developer Fusion