aboutsummaryrefslogtreecommitdiff
path: root/libxsde/xsde/cxx/parser/validating/number.cxx
blob: e0db7d9ab3505da9029fd37df1af899a4ea615ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// file      : xsde/cxx/parser/validating/number.cxx
// copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC
// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file

#include <string.h> // memcpy

#include <xsde/cxx/parser/validating/number.hxx>

using xsde::cxx::ro_string;

static bool
trim_leading_zeros (ro_string& s)
{
  ro_string::size_type size = s.size ();

  if (size != 0)
  {
    const char* f = s.data ();
    const char* l = f + size;
    const char* of = f;

    while (f < l && *f == '0')
      ++f;

    if (f != of)
    {
      s.assign ((f <= l ? f : 0), (f <= l ? l - f : 0));
      return true;
    }
  }

  return false;
}

namespace xsde
{
  namespace cxx
  {
    namespace parser
    {
      namespace validating
      {
        bool number::
        parse (const ro_string& s, char* str, size_t max)
        {
          bool r = true;

          ro_string tmp (s.data (), s.size ());
          size_t size = tmp.size ();

          switch (state_)
          {
          case leading_ws:
            {
              size = trim_left (tmp);

              if (size != 0)
                state_ = sign;
              else
                break;
              // Fall through.
            }
          case sign:
            {
              if (tmp[0] == '-')
                sign_ = minus;
              else if (tmp[0] == '+')
                sign_ = plus;

              if (sign_ != none)
                tmp.assign (tmp.data () + 1, --size);

              state_ = leading_zeros;

              if (size == 0)
                break;
              // Fall through.
            }
          case leading_zeros:
            {
              if (trim_leading_zeros (tmp))
              {
                if (size_ == 0)
                {
                  str[0] = '0';
                  size_ = 1;
                }

                size = tmp.size ();
              }

              if (size != 0)
                state_ = literal;
              else
                break;
              // Fall through.
            }
          case literal:
            {
              // If this chunk is too long then it has to be the last so trim
              // trailing ws.
              //
              if ((max - size_) < size)
              {
                size = trim_right (tmp);
                state_ = trailing_ws; // It either had ws or is too large.
              }

              if ((max - size_) >= size)
              {
                memcpy (str + size_, tmp.data (), size);
                size_ += size;
              }
              else
                r = false;

              break;
            }
          case trailing_ws:
            {
              if (trim_left (tmp) != 0)
                r = false;
            }
          }

          return r;
        }
      }
    }
  }
}