1 | #include <IO/ReadHelpers.h> |
2 | |
3 | |
4 | namespace DB |
5 | { |
6 | |
7 | namespace ErrorCodes |
8 | { |
9 | extern const int CANNOT_PARSE_NUMBER; |
10 | extern const int ARGUMENT_OUT_OF_BOUND; |
11 | } |
12 | |
13 | |
14 | template <bool _throw_on_error, typename T> |
15 | inline bool readDigits(ReadBuffer & buf, T & x, unsigned int & digits, int & exponent, bool digits_only = false) |
16 | { |
17 | x = 0; |
18 | exponent = 0; |
19 | unsigned int max_digits = digits; |
20 | digits = 0; |
21 | unsigned int places = 0; |
22 | typename T::NativeType sign = 1; |
23 | bool leading_zeroes = true; |
24 | bool after_point = false; |
25 | |
26 | if (buf.eof()) |
27 | { |
28 | if constexpr (_throw_on_error) |
29 | throwReadAfterEOF(); |
30 | return false; |
31 | } |
32 | |
33 | if (!buf.eof()) |
34 | { |
35 | switch (*buf.position()) |
36 | { |
37 | case '-': |
38 | sign = -1; |
39 | [[fallthrough]]; |
40 | case '+': |
41 | ++buf.position(); |
42 | break; |
43 | } |
44 | } |
45 | |
46 | bool stop = false; |
47 | while (!buf.eof() && !stop) |
48 | { |
49 | const char & byte = *buf.position(); |
50 | switch (byte) |
51 | { |
52 | case '.': |
53 | after_point = true; |
54 | leading_zeroes = false; |
55 | break; |
56 | case '0': |
57 | { |
58 | if (leading_zeroes) |
59 | break; |
60 | |
61 | if (after_point) |
62 | { |
63 | ++places; /// Count trailing zeroes. They would be used only if there's some other digit after them. |
64 | break; |
65 | } |
66 | [[fallthrough]]; |
67 | } |
68 | case '1': [[fallthrough]]; |
69 | case '2': [[fallthrough]]; |
70 | case '3': [[fallthrough]]; |
71 | case '4': [[fallthrough]]; |
72 | case '5': [[fallthrough]]; |
73 | case '6': [[fallthrough]]; |
74 | case '7': [[fallthrough]]; |
75 | case '8': [[fallthrough]]; |
76 | case '9': |
77 | { |
78 | leading_zeroes = false; |
79 | |
80 | ++places; // num zeroes before + current digit |
81 | if (digits + places > max_digits) |
82 | { |
83 | if constexpr (_throw_on_error) |
84 | throw Exception("Too many digits (" + std::to_string(digits + places) + " > " + std::to_string(max_digits) |
85 | + ") in decimal value" , ErrorCodes::ARGUMENT_OUT_OF_BOUND); |
86 | return false; |
87 | } |
88 | |
89 | digits += places; |
90 | if (after_point) |
91 | exponent -= places; |
92 | |
93 | // TODO: accurate shift10 for big integers |
94 | for (; places; --places) |
95 | x *= 10; |
96 | x += (byte - '0'); |
97 | break; |
98 | } |
99 | case 'e': [[fallthrough]]; |
100 | case 'E': |
101 | { |
102 | ++buf.position(); |
103 | Int32 addition_exp = 0; |
104 | readIntText(addition_exp, buf); |
105 | exponent += addition_exp; |
106 | stop = true; |
107 | continue; |
108 | } |
109 | |
110 | default: |
111 | if (digits_only) |
112 | { |
113 | if constexpr (_throw_on_error) |
114 | throw Exception("Unexpected symbol while reading decimal" , ErrorCodes::CANNOT_PARSE_NUMBER); |
115 | return false; |
116 | } |
117 | stop = true; |
118 | continue; |
119 | } |
120 | ++buf.position(); |
121 | } |
122 | |
123 | x *= sign; |
124 | return true; |
125 | } |
126 | |
127 | template <typename T> |
128 | inline void readDecimalText(ReadBuffer & buf, T & x, unsigned int precision, unsigned int & scale, bool digits_only = false) |
129 | { |
130 | unsigned int digits = precision; |
131 | int exponent; |
132 | readDigits<true>(buf, x, digits, exponent, digits_only); |
133 | |
134 | if (static_cast<int>(digits) + exponent > static_cast<int>(precision - scale)) |
135 | throw Exception("Decimal value is too big" , ErrorCodes::ARGUMENT_OUT_OF_BOUND); |
136 | if (static_cast<int>(scale) + exponent < 0) |
137 | throw Exception("Decimal value is too small" , ErrorCodes::ARGUMENT_OUT_OF_BOUND); |
138 | |
139 | scale += exponent; |
140 | } |
141 | |
142 | template <typename T> |
143 | inline bool tryReadDecimalText(ReadBuffer & buf, T & x, unsigned int precision, unsigned int & scale) |
144 | { |
145 | unsigned int digits = precision; |
146 | int exponent; |
147 | |
148 | if (!readDigits<false>(buf, x, digits, exponent, true) || |
149 | static_cast<int>(digits) + exponent > static_cast<int>(precision - scale) || |
150 | static_cast<int>(scale) + exponent < 0) |
151 | return false; |
152 | |
153 | scale += exponent; |
154 | return true; |
155 | } |
156 | |
157 | template <typename T> |
158 | inline void readCSVDecimalText(ReadBuffer & buf, T & x, unsigned int precision, unsigned int & scale) |
159 | { |
160 | if (buf.eof()) |
161 | throwReadAfterEOF(); |
162 | |
163 | char maybe_quote = *buf.position(); |
164 | |
165 | if (maybe_quote == '\'' || maybe_quote == '\"') |
166 | ++buf.position(); |
167 | |
168 | readDecimalText(buf, x, precision, scale, false); |
169 | |
170 | if (maybe_quote == '\'' || maybe_quote == '\"') |
171 | assertChar(maybe_quote, buf); |
172 | } |
173 | |
174 | } |
175 | |