Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:42:41

0001 //
0002 // Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_TEXT_FIELD_IPP
0009 #define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_TEXT_FIELD_IPP
0010 
0011 #pragma once
0012 
0013 #include <boost/mysql/blob_view.hpp>
0014 #include <boost/mysql/datetime.hpp>
0015 #include <boost/mysql/field_view.hpp>
0016 #include <boost/mysql/metadata.hpp>
0017 #include <boost/mysql/string_view.hpp>
0018 
0019 #include <boost/mysql/detail/config.hpp>
0020 #include <boost/mysql/detail/datetime.hpp>
0021 
0022 #include <boost/mysql/impl/internal/protocol/bit_deserialization.hpp>
0023 #include <boost/mysql/impl/internal/protocol/constants.hpp>
0024 #include <boost/mysql/impl/internal/protocol/deserialize_text_field.hpp>
0025 #include <boost/mysql/impl/internal/protocol/serialization.hpp>
0026 
0027 #include <boost/assert.hpp>
0028 #include <boost/lexical_cast/try_lexical_convert.hpp>
0029 
0030 #include <cmath>
0031 #include <cstddef>
0032 #include <cstdlib>
0033 #include <type_traits>
0034 
0035 namespace boost {
0036 namespace mysql {
0037 namespace detail {
0038 
0039 #ifdef BOOST_MSVC
0040 #pragma warning(push)
0041 #pragma warning(disable : 4996)  // MSVC doesn't like my sscanf's
0042 #endif
0043 
0044 // Constants
0045 BOOST_MYSQL_STATIC_IF_COMPILED constexpr unsigned max_decimals = 6u;
0046 
0047 namespace textc {
0048 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t year_sz = 4;
0049 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t month_sz = 2;
0050 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t day_sz = 2;
0051 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t hours_min_sz = 2;  // in TIME, it may be longer
0052 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t mins_sz = 2;
0053 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t secs_sz = 2;
0054 
0055 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t date_sz = year_sz + month_sz + day_sz + 2;  // delimiters
0056 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t time_min_sz = hours_min_sz + mins_sz + secs_sz +
0057                                                                    2;  // delimiters
0058 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t time_max_sz = time_min_sz + max_decimals +
0059                                                                    3;  // sign, period, hour extra character
0060 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t datetime_min_sz = date_sz + time_min_sz +
0061                                                                        1;  // delimiter
0062 BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t datetime_max_sz = datetime_min_sz + max_decimals +
0063                                                                        1;  // period
0064 
0065 BOOST_MYSQL_STATIC_IF_COMPILED constexpr unsigned time_max_hour = 838;
0066 }  // namespace textc
0067 
0068 // Integers
0069 template <class T>
0070 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
0071 deserialize_text_value_int_impl(string_view from, field_view& to) noexcept
0072 {
0073     T v;
0074     bool ok = boost::conversion::try_lexical_convert(from.data(), from.size(), v);
0075     if (!ok)
0076         return deserialize_errc::protocol_value_error;
0077     to = field_view(v);
0078     return deserialize_errc::ok;
0079 }
0080 
0081 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
0082 deserialize_text_value_int(string_view from, field_view& to, const metadata& meta) noexcept
0083 {
0084     return meta.is_unsigned() ? deserialize_text_value_int_impl<std::uint64_t>(from, to)
0085                               : deserialize_text_value_int_impl<std::int64_t>(from, to);
0086 }
0087 
0088 // Floating points
0089 template <class T>
0090 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
0091 deserialize_text_value_float(string_view from, field_view& to) noexcept
0092 {
0093     T val;
0094     bool ok = boost::conversion::try_lexical_convert(from.data(), from.size(), val);
0095     if (!ok || std::isnan(val) || std::isinf(val))  // SQL std forbids these values
0096         return deserialize_errc::protocol_value_error;
0097     to = field_view(val);
0098     return deserialize_errc::ok;
0099 }
0100 
0101 // Strings
0102 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
0103 deserialize_text_value_string(string_view from, field_view& to) noexcept
0104 {
0105     to = field_view(from);
0106     return deserialize_errc::ok;
0107 }
0108 
0109 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
0110 deserialize_text_value_blob(string_view from, field_view& to) noexcept
0111 {
0112     to = field_view(to_span(from));
0113     return deserialize_errc::ok;
0114 }
0115 
0116 // Date/time types
0117 BOOST_MYSQL_STATIC_OR_INLINE unsigned sanitize_decimals(unsigned decimals) noexcept
0118 {
0119     return (std::min)(decimals, max_decimals);
0120 }
0121 
0122 // Computes the meaning of the parsed microsecond number, taking into
0123 // account decimals (85 with 2 decimals means 850000us)
0124 BOOST_MYSQL_STATIC_OR_INLINE unsigned compute_micros(unsigned parsed_micros, unsigned decimals) noexcept
0125 {
0126     return parsed_micros * static_cast<unsigned>(std::pow(10, max_decimals - decimals));
0127 }
0128 
0129 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc deserialize_text_ymd(string_view from, date& to)
0130 {
0131     using namespace textc;
0132 
0133     // Size check
0134     if (from.size() != date_sz)
0135         return deserialize_errc::protocol_value_error;
0136 
0137     // Copy to a NULL-terminated buffer
0138     char buffer[date_sz + 1]{};
0139     std::memcpy(buffer, from.data(), from.size());
0140 
0141     // Parse individual components
0142     unsigned year, month, day;
0143     char extra_char;
0144     int parsed = sscanf(buffer, "%4u-%2u-%2u%c", &year, &month, &day, &extra_char);
0145     if (parsed != 3)
0146         return deserialize_errc::protocol_value_error;
0147 
0148     // Range check for individual components. MySQL doesn't allow invidiual components
0149     // to be out of range, although they may be zero or representing an invalid date
0150     if (year > max_year || month > max_month || day > max_day)
0151         return deserialize_errc::protocol_value_error;
0152 
0153     to = date(
0154         static_cast<std::uint16_t>(year),
0155         static_cast<std::uint8_t>(month),
0156         static_cast<std::uint8_t>(day)
0157     );
0158     return deserialize_errc::ok;
0159 }
0160 
0161 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
0162 deserialize_text_value_date(string_view from, field_view& to) noexcept
0163 {
0164     date d;
0165     auto err = deserialize_text_ymd(from, d);
0166     if (err != deserialize_errc::ok)
0167         return err;
0168     to = field_view(d);
0169     return deserialize_errc::ok;
0170 }
0171 
0172 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
0173 deserialize_text_value_datetime(string_view from, field_view& to, const metadata& meta) noexcept
0174 {
0175     using namespace textc;
0176 
0177     // Sanitize decimals
0178     unsigned decimals = sanitize_decimals(meta.decimals());
0179 
0180     // Length check
0181     std::size_t expected_size = datetime_min_sz + (decimals ? decimals + 1 : 0);
0182     if (from.size() != expected_size)
0183         return deserialize_errc::protocol_value_error;
0184 
0185     // Deserialize date part
0186     date d;
0187     auto err = deserialize_text_ymd(from.substr(0, date_sz), d);
0188     if (err != deserialize_errc::ok)
0189         return err;
0190 
0191     // Copy to NULL-terminated buffer
0192     constexpr std::size_t datetime_time_first = date_sz + 1;  // date + space
0193     char buffer[datetime_max_sz - datetime_time_first + 1]{};
0194     std::memcpy(buffer, from.data() + datetime_time_first, from.size() - datetime_time_first);
0195 
0196     // Parse
0197     unsigned hours, minutes, seconds;
0198     unsigned micros = 0;
0199     char extra_char;
0200     if (decimals)
0201     {
0202         int parsed = sscanf(buffer, "%2u:%2u:%2u.%6u%c", &hours, &minutes, &seconds, &micros, &extra_char);
0203         if (parsed != 4)
0204             return deserialize_errc::protocol_value_error;
0205         micros = compute_micros(micros, decimals);
0206     }
0207     else
0208     {
0209         int parsed = sscanf(buffer, "%2u:%2u:%2u%c", &hours, &minutes, &seconds, &extra_char);
0210         if (parsed != 3)
0211             return deserialize_errc::protocol_value_error;
0212     }
0213 
0214     // Validity check. Although MySQL allows invalid and zero datetimes, it doesn't allow
0215     // individual components to be out of range.
0216     if (hours > max_hour || minutes > max_min || seconds > max_sec || micros > max_micro)
0217     {
0218         return deserialize_errc::protocol_value_error;
0219     }
0220 
0221     datetime dt(
0222         d.year(),
0223         d.month(),
0224         d.day(),
0225         static_cast<std::uint8_t>(hours),
0226         static_cast<std::uint8_t>(minutes),
0227         static_cast<std::uint8_t>(seconds),
0228         static_cast<std::uint32_t>(micros)
0229     );
0230     to = field_view(dt);
0231     return deserialize_errc::ok;
0232 }
0233 
0234 BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
0235 deserialize_text_value_time(string_view from, field_view& to, const metadata& meta) noexcept
0236 {
0237     using namespace textc;
0238 
0239     // Sanitize decimals
0240     unsigned decimals = sanitize_decimals(meta.decimals());
0241 
0242     // size check
0243     std::size_t actual_min_size = time_min_sz + (decimals ? decimals + 1 : 0);
0244     std::size_t actual_max_size = actual_min_size + 1 + 1;  // hour extra character and sign
0245     BOOST_ASSERT(actual_max_size <= time_max_sz);
0246     if (from.size() < actual_min_size || from.size() > actual_max_size)
0247         return deserialize_errc::protocol_value_error;
0248 
0249     // Copy to NULL-terminated buffer
0250     char buffer[time_max_sz + 1]{};
0251     memcpy(buffer, from.data(), from.size());
0252 
0253     // Sign
0254     bool is_negative = from[0] == '-';
0255     const char* first = is_negative ? buffer + 1 : buffer;
0256 
0257     // Parse it
0258     unsigned hours, minutes, seconds;
0259     unsigned micros = 0;
0260     char extra_char;
0261     if (decimals)
0262     {
0263         int parsed = sscanf(first, "%3u:%2u:%2u.%6u%c", &hours, &minutes, &seconds, &micros, &extra_char);
0264         if (parsed != 4)
0265             return deserialize_errc::protocol_value_error;
0266         micros = compute_micros(micros, decimals);
0267     }
0268     else
0269     {
0270         int parsed = sscanf(first, "%3u:%2u:%2u%c", &hours, &minutes, &seconds, &extra_char);
0271         if (parsed != 3)
0272             return deserialize_errc::protocol_value_error;
0273     }
0274 
0275     // Range check
0276     if (hours > time_max_hour || minutes > max_min || seconds > max_sec || micros > max_micro)
0277     {
0278         return deserialize_errc::protocol_value_error;
0279     }
0280 
0281     // Sum it
0282     auto res = std::chrono::hours(hours) + std::chrono::minutes(minutes) + std::chrono::seconds(seconds) +
0283                std::chrono::microseconds(micros);
0284     if (is_negative)
0285     {
0286         res = -res;
0287     }
0288 
0289     // Done
0290     to = field_view(res);
0291     return deserialize_errc::ok;
0292 }
0293 
0294 }  // namespace detail
0295 }  // namespace mysql
0296 }  // namespace boost
0297 
0298 boost::mysql::detail::deserialize_errc boost::mysql::detail::deserialize_text_field(
0299     string_view from,
0300     const metadata& meta,
0301     field_view& output
0302 )
0303 {
0304     switch (meta.type())
0305     {
0306     case column_type::tinyint:
0307     case column_type::smallint:
0308     case column_type::mediumint:
0309     case column_type::int_:
0310     case column_type::bigint:
0311     case column_type::year: return deserialize_text_value_int(from, output, meta);
0312     case column_type::bit: return deserialize_bit(from, output);
0313     case column_type::float_: return deserialize_text_value_float<float>(from, output);
0314     case column_type::double_: return deserialize_text_value_float<double>(from, output);
0315     case column_type::timestamp:
0316     case column_type::datetime: return deserialize_text_value_datetime(from, output, meta);
0317     case column_type::date: return deserialize_text_value_date(from, output);
0318     case column_type::time: return deserialize_text_value_time(from, output, meta);
0319     // True string types
0320     case column_type::char_:
0321     case column_type::varchar:
0322     case column_type::text:
0323     case column_type::enum_:
0324     case column_type::set:
0325     case column_type::decimal:
0326     case column_type::json: return deserialize_text_value_string(from, output);
0327     // Blobs and anything else
0328     case column_type::binary:
0329     case column_type::varbinary:
0330     case column_type::blob:
0331     case column_type::geometry:
0332     default: return deserialize_text_value_blob(from, output);
0333     }
0334 }
0335 
0336 #endif