提交 13a45bce 编写于 作者: A Alexey Milovidov

Addition to prev. revision [#METR-18202].

上级 1f6def09
/**
* @file
* @author Sergey N. Yatskevich
* @brief YANDEX_APP_MAIN macros
*/
/*
* $Id$
*/
#ifndef __APPLICATION_EXT_H
#define __APPLICATION_EXT_H
#include <Poco/TextEncoding.h>
#include <Poco/Util/Application.h>
#define YANDEX_APP_MAIN(AppClassName) \
int \
main (int _argc, char* _argv[]) \
{ \
AppClassName app; \
try \
{ \
app.init (_argc, _argv); \
return app.run (); \
} \
catch (const Poco::Exception& _ex) \
{ \
app.logger ().log (_ex); \
} \
catch (const std::exception& _ex) \
{ \
app.logger ().error (Poco::Logger::format ("Got exception: $0", _ex.what ())); \
} \
catch (...) \
{ \
app.logger ().error ("Unknown exception"); \
} \
return Poco::Util::Application::EXIT_CONFIG; \
}
#endif
#pragma once
#include <iostream>
#include <Poco/TextEncoding.h>
#include <Poco/Util/ServerApplication.h>
#define YANDEX_APP_SERVER_MAIN(AppServerClassName) \
int \
main (int _argc, char* _argv[]) \
{ \
AppServerClassName app; \
try \
{ \
return app.run (_argc, _argv); \
} \
catch (const Poco::Exception& _ex) \
{ \
std::cerr << "POCO ERROR: " << _ex.displayText() << std::endl; \
app.logger().log (_ex); \
} \
catch (const std::exception& _ex) \
{ \
std::cerr << "STD ERROR: " << _ex.what() << std::endl; \
app.logger().error (Poco::Logger::format ("Got exception: $0", _ex.what ())); \
} \
catch (...) \
{ \
std::cerr << "UNKNOWN ERROR" << std::endl; \
app.logger().error ("Unknown exception"); \
} \
return Poco::Util::Application::EXIT_CONFIG; \
}
#pragma once
#include <string>
#include <functional>
#include <Poco/Types.h>
#include <common/strong_typedef.h>
typedef Poco::Int8 Int8;
typedef Poco::Int16 Int16;
typedef Poco::Int32 Int32;
typedef Poco::Int64 Int64;
typedef Poco::UInt8 UInt8;
typedef Poco::UInt16 UInt16;
typedef Poco::UInt32 UInt32;
typedef Poco::UInt64 UInt64;
/// Обход проблемы с тем, что KDevelop не видит time_t и size_t (для подсветки синтаксиса).
#ifdef IN_KDEVELOP_PARSER
typedef Int64 time_t;
typedef UInt64 size_t;
#endif
/** Тип данных для хранения идентификатора пользователя. */
typedef UInt64 UserID_t;
/** Тип данных для хранения идентификатора счетчика. */
typedef UInt32 CounterID_t;
/** Идентификатор хита */
typedef UInt64 WatchID_t;
/** Идентификатор визита */
STRONG_TYPEDEF(UInt64, VisitID_t);
/** Идентификатор клика */
typedef UInt64 ClickID_t;
/** Идентификатор цели */
typedef UInt32 GoalID_t;
namespace std
{
template<>
struct hash<VisitID_t> : public unary_function<VisitID_t, size_t>
{
size_t operator()(VisitID_t x) const { return x; }
};
}
#pragma once
#include <common/DateLUTImpl.h>
#include <common/singleton.h>
#include <Poco/Exception.h>
#include <unordered_map>
#include <vector>
#include <atomic>
#include <mutex>
#include <memory>
/** Этот класс предоставляет метод для того, чтобы создать объект DateLUTImpl
* для заданного часового пояса, если он не существует.
*/
class DateLUT : public Singleton<DateLUT>
{
friend class Singleton<DateLUT>;
public:
DateLUT(const DateLUT &) = delete;
DateLUT & operator=(const DateLUT &) = delete;
/// Вернуть единственный экземпляр объекта DateLUTImpl для часового пояса по-умолчанию.
static __attribute__((__always_inline__)) const DateLUTImpl & instance()
{
const auto & date_lut = Singleton<DateLUT>::instance();
return *date_lut.default_date_lut_impl;
}
/// Вернуть единственный экземпляр объекта DateLUTImpl для заданного часового пояса.
static __attribute__((__always_inline__)) const DateLUTImpl & instance(const std::string & time_zone)
{
const auto & date_lut = Singleton<DateLUT>::instance();
return date_lut.get(time_zone);
}
public:
/// Отображение часового пояса в группу эквивалентных часовый поясов.
/// Два часовых пояса эквивалентные, если они обладают одними и теми же свойствами.
using TimeZoneToGroup = std::unordered_map<std::string, size_t>;
/// Хранилище для lookup таблиц DateLUTImpl.
using DateLUTImplList = std::vector<std::atomic<DateLUTImpl *> >;
protected:
DateLUT();
private:
__attribute__((__always_inline__)) const DateLUTImpl & get(const std::string & time_zone) const
{
if (time_zone.empty())
return *default_date_lut_impl;
auto it = time_zone_to_group.find(time_zone);
if (it == time_zone_to_group.end())
throw Poco::Exception("Invalid time zone " + time_zone);
const auto & group_id = it->second;
if (group_id == default_group_id)
return *default_date_lut_impl;
return getImplementation(time_zone, group_id);
}
const DateLUTImpl & getImplementation(const std::string & time_zone, size_t group_id) const;
private:
/// Указатель на реализацию для часового пояса по-умолчанию.
DateLUTImpl * default_date_lut_impl;
/// Соответствующиая группа часовых поясов по-умолчанию.
size_t default_group_id;
///
TimeZoneToGroup time_zone_to_group;
/// Lookup таблица для каждой группы часовых поясов.
mutable std::unique_ptr<DateLUTImplList> date_lut_impl_list;
mutable std::mutex mutex;
};
#pragma once
#include <common/Common.h>
#include <common/singleton.h>
#include <common/likely.h>
#include <common/strong_typedef.h>
#include <iostream>
#include <vector>
#include <unordered_map>
#include <ctime>
#define DATE_LUT_MIN 0
#define DATE_LUT_MAX (0x7FFFFFFF - 86400)
#define DATE_LUT_MAX_DAY_NUM (0x7FFFFFFF / 86400)
#define DATE_LUT_MIN_YEAR 1970
#define DATE_LUT_MAX_YEAR 2037 /// Последний полный год
#define DATE_LUT_YEARS 68 /// Количество лет в lookup таблице
STRONG_TYPEDEF(UInt16, DayNum_t);
/** Lookup таблица для преобразования времени в дату, а также в месяц или в год или в день недели или в день месяца.
* Сейчас она используется для ускорения OLAPServer-а, который делает такие преобразования миллиардами.
*/
class DateLUTImpl
{
public:
DateLUTImpl(const std::string & time_zone);
public:
struct Values
{
/// 32 бита из time_t начала дня.
/// Знаковость важна, чтобы поддержать начало 1970-01-01 MSK, которое имело time_t == -10800.
/// Измените на time_t, если надо поддержать времена после 2038 года.
Int32 date;
UInt16 year;
UInt8 month;
UInt8 day_of_month;
UInt8 day_of_week;
};
private:
/// Сравнительно много данных. То есть, лучше не класть объект на стек.
/// По сравнению с std::vector, на один indirection меньше.
Values lut[DATE_LUT_MAX_DAY_NUM + 1];
/// lookup таблица начал годов
DayNum_t years_lut[DATE_LUT_YEARS];
/// Смещение от UTC в начале Unix эпохи.
time_t offset_at_start_of_epoch;
inline size_t findIndex(time_t t) const
{
/// первое приближение
size_t precision = t / 86400;
if (precision >= DATE_LUT_MAX_DAY_NUM)
return 0;
if (t >= lut[precision].date && t < lut[precision + 1].date)
return precision;
for (size_t i = 1;; ++i)
{
if (precision + i >= DATE_LUT_MAX_DAY_NUM)
return 0;
if (t >= lut[precision + i].date && t < lut[precision + i + 1].date)
return precision + i;
if (precision < i)
return 0;
if (t >= lut[precision - i].date && t < lut[precision - i + 1].date)
return precision - i;
}
}
inline const Values & find(time_t t) const
{
return lut[findIndex(t)];
}
static inline DayNum_t fixDay(DayNum_t day)
{
return day > DATE_LUT_MAX_DAY_NUM ? static_cast<DayNum_t>(0) : day;
}
public:
/// всё ниже thread-safe; корректность входных данных не проверяется
inline time_t toDate(time_t t) const { return find(t).date; }
inline unsigned toMonth(time_t t) const { return find(t).month; }
inline unsigned toYear(time_t t) const { return find(t).year; }
inline unsigned toDayOfWeek(time_t t) const { return find(t).day_of_week; }
inline unsigned toDayOfMonth(time_t t) const { return find(t).day_of_month; }
/// номер недели, начиная с какой-то недели в прошлом; неделя начинается с понедельника
/// (переводим к понедельнику и делим DayNum на 7; будем исходить из допущения,
/// что в области применения этой функции не было и не будет недель, состоящих не из семи дней)
inline unsigned toRelativeWeekNum(DayNum_t d) const
{
return (d - (lut[d].day_of_week - 1)) / 7;
}
inline unsigned toRelativeWeekNum(time_t t) const
{
size_t index = findIndex(t);
return (index - (lut[index].day_of_week - 1)) / 7;
}
/// номер месяца, начиная с какого-то месяца в прошлом (год * 12 + номер месяца в году)
inline unsigned toRelativeMonthNum(DayNum_t d) const
{
return lut[d].year * 12 + lut[d].month;
}
inline unsigned toRelativeMonthNum(time_t t) const
{
size_t index = findIndex(t);
return lut[index].year * 12 + lut[index].month;
}
/// делим unix timestamp на 3600;
/// (таким образом, учитываются прошедшие интервалы времени длительностью в час, не зависимо от перевода стрелок;
/// поддерживаются только часовые пояса, в которых перевод стрелок осуществлялся только на целое число часов)
inline time_t toRelativeHourNum(time_t t) const
{
return t / 3600;
}
/// делим unix timestamp на 60
inline time_t toRelativeMinuteNum(time_t t) const
{
return t / 60;
}
/// округление вниз до понедельника
inline time_t toFirstDayOfWeek(time_t t) const
{
size_t index = findIndex(t);
return lut[index - (lut[index].day_of_week - 1)].date;
}
inline DayNum_t toFirstDayNumOfWeek(DayNum_t d) const
{
return DayNum_t(d - (lut[d].day_of_week - 1));
}
inline DayNum_t toFirstDayNumOfWeek(time_t t) const
{
size_t index = findIndex(t);
return DayNum_t(index - (lut[index].day_of_week - 1));
}
/// округление вниз до первого числа месяца
inline time_t toFirstDayOfMonth(time_t t) const
{
size_t index = findIndex(t);
return lut[index - (lut[index].day_of_month - 1)].date;
}
inline DayNum_t toFirstDayNumOfMonth(DayNum_t d) const
{
return DayNum_t(d - (lut[fixDay(d)].day_of_month - 1));
}
inline DayNum_t toFirstDayNumOfMonth(time_t t) const
{
size_t index = findIndex(t);
return DayNum_t(index - (lut[index].day_of_month - 1));
}
/// округление до первого числа квартала
inline time_t toFirstDayOfQuarter(time_t t) const
{
size_t index = findIndex(t);
switch (lut[index].month % 3)
{
case 0:
index = index - lut[index].day_of_month;
case 2:
index = index - lut[index].day_of_month;
case 1:
index = index - lut[index].day_of_month + 1;
}
return DayNum_t(index);
}
inline DayNum_t toFirstDayNumOfQuarter(DayNum_t d) const
{
size_t index = fixDay(d);
switch (lut[index].month % 3)
{
case 0:
index = index - lut[index].day_of_month;
case 2:
index = index - lut[index].day_of_month;
case 1:
index = index - lut[index].day_of_month + 1;
}
return DayNum_t(index);
}
inline DayNum_t toFirstDayNumOfQuarter(time_t t) const
{
size_t index = findIndex(t);
switch (lut[index].month % 3)
{
case 0:
index = index - lut[index].day_of_month;
case 2:
index = index - lut[index].day_of_month;
case 1:
index = index - lut[index].day_of_month + 1;
}
return DayNum_t(index);
}
/// округление вниз до первого числа года
inline time_t toFirstDayOfYear(time_t t) const
{
return lut[years_lut[lut[findIndex(t)].year - DATE_LUT_MIN_YEAR]].date;
}
inline DayNum_t toFirstDayNumOfYear(DayNum_t d) const
{
return years_lut[lut[fixDay(d)].year - DATE_LUT_MIN_YEAR];
}
inline time_t toFirstDayNumOfYear(time_t t) const
{
return lut[years_lut[lut[findIndex(t)].year - DATE_LUT_MIN_YEAR]].date;
}
/// первое число следующего месяца
inline time_t toFirstDayOfNextMonth(time_t t) const
{
size_t index = findIndex(t);
index += 32 - lut[index].day_of_month;
return lut[index - (lut[index].day_of_month - 1)].date;
}
/// первое число предыдущего месяца
inline time_t toFirstDayOfPrevMonth(time_t t) const
{
size_t index = findIndex(t);
index -= lut[index].day_of_month;
return lut[index - (lut[index].day_of_month - 1)].date;
}
/// количество дней в месяце
inline size_t daysInMonth(time_t t) const
{
size_t today = findIndex(t);
size_t start_of_month = today - (lut[today].day_of_month - 1);
size_t next_month = start_of_month + 31;
size_t start_of_next_month = next_month - (lut[next_month].day_of_month - 1);
return start_of_next_month - start_of_month;
}
/** Округление до даты; затем сдвиг на указанное количество дней.
* Замечание: результат сдвига должен находиться в пределах LUT.
*/
inline time_t toDateAndShift(time_t t, int days = 1) const
{
return lut[findIndex(t) + days].date;
}
/** функции ниже исходят из допущения, что перевод стрелок вперёд, если осуществляется, то на час, в два часа ночи,
* а перевод стрелок назад, если осуществляется, то на час, в три часа ночи.
* (что, в общем, не верно, так как в Москве один раз перевод стрелок был осуществлён в другое время)
*/
inline time_t toTimeInaccurate(time_t t) const
{
size_t index = findIndex(t);
time_t day_length = lut[index + 1].date - lut[index].date;
time_t res = t - lut[index].date;
if (unlikely(day_length == 90000 && res >= 10800)) /// был произведён перевод стрелок назад
res -= 3600;
else if (unlikely(day_length == 82800 && res >= 7200)) /// был произведён перевод стрелок вперёд
res += 3600;
return res - offset_at_start_of_epoch; /// Отсчёт от 1970-01-01 00:00:00 по локальному времени
}
inline unsigned toHourInaccurate(time_t t) const
{
size_t index = findIndex(t);
time_t day_length = lut[index + 1].date - lut[index].date;
unsigned res = (t - lut[index].date) / 3600;
if (unlikely(day_length == 90000 && res >= 3)) /// был произведён перевод стрелок назад
--res;
else if (unlikely(day_length == 82800 && res >= 2)) /// был произведён перевод стрелок вперёд
++res;
return res;
}
inline unsigned toMinute(time_t t) const { return ((t - find(t).date) % 3600) / 60; }
inline unsigned toSecond(time_t t) const { return (t - find(t).date) % 60; }
inline unsigned toStartOfMinute(time_t t) const
{
time_t date = find(t).date;
return date + (t - date) / 60 * 60;
}
inline unsigned toStartOfHour(time_t t) const
{
time_t date = find(t).date;
return date + (t - date) / 3600 * 3600;
}
/** Только для часовых поясов, отличающихся от UTC на значение, кратное часу и без перевода стрелок не значение не кратное часу */
inline unsigned toMinuteInaccurate(time_t t) const { return (t / 60) % 60; }
inline unsigned toSecondInaccurate(time_t t) const { return t % 60; }
inline unsigned toStartOfMinuteInaccurate(time_t t) const { return t / 60 * 60; }
inline unsigned toStartOfFiveMinuteInaccurate(time_t t) const { return t / 300 * 300; }
inline unsigned toStartOfHourInaccurate(time_t t) const { return t / 3600 * 3600; }
/// Номер дня в пределах UNIX эпохи (и немного больше) - позволяет хранить дату в двух байтах
inline DayNum_t toDayNum(time_t t) const { return static_cast<DayNum_t>(findIndex(t)); }
inline time_t fromDayNum(DayNum_t d) const { return lut[fixDay(d)].date; }
inline time_t toDate(DayNum_t d) const { return lut[fixDay(d)].date; }
inline unsigned toMonth(DayNum_t d) const { return lut[fixDay(d)].month; }
inline unsigned toYear(DayNum_t d) const { return lut[fixDay(d)].year; }
inline unsigned toDayOfWeek(DayNum_t d) const { return lut[fixDay(d)].day_of_week; }
inline unsigned toDayOfMonth(DayNum_t d) const { return lut[fixDay(d)].day_of_month; }
inline const Values & getValues(DayNum_t d) const { return lut[fixDay(d)]; }
inline const Values & getValues(time_t t) const { return lut[findIndex(t)]; }
/// получает DayNum_t из года, месяца, дня
inline DayNum_t makeDayNum(short year, char month, char day_of_month) const
{
if (unlikely(year < DATE_LUT_MIN_YEAR || year > DATE_LUT_MAX_YEAR || month < 1 || month > 12 || day_of_month < 1 || day_of_month > 31))
return DayNum_t(0);
DayNum_t any_day_of_month(years_lut[year - DATE_LUT_MIN_YEAR] + 31 * (month - 1));
return DayNum_t(any_day_of_month - toDayOfMonth(any_day_of_month) + day_of_month);
}
inline time_t makeDate(short year, char month, char day_of_month) const
{
return lut[makeDayNum(year, month, day_of_month)].date;
}
/** Функция ниже исходит из допущения, что перевод стрелок вперёд, если осуществляется, то на час, в два часа ночи,
* а перевод стрелок назад, если осуществляется, то на час, в три часа ночи.
* (что, в общем, не верно, так как в Москве один раз перевод стрелок был осуществлён в другое время).
* Также, выдаётся лишь один из двух возможных вариантов при переводе стрелок назад.
*/
inline time_t makeDateTime(short year, char month, char day_of_month, char hour, char minute, char second) const
{
size_t index = makeDayNum(year, month, day_of_month);
time_t res = lut[index].date + hour * 3600 + minute * 60 + second;
time_t day_length = lut[index + 1].date - lut[index].date;
if (unlikely(day_length == 90000 && hour >= 3)) /// был произведён перевод стрелок назад
res += 3600;
else if (unlikely(day_length == 82800 && hour >= 2)) /// был произведён перевод стрелок вперёд
res -= 3600;
return res;
}
inline UInt32 toNumYYYYMMDD(time_t t) const
{
const Values & values = find(t);
return values.year * 10000 + values.month * 100 + values.day_of_month;
}
inline UInt32 toNumYYYYMMDD(DayNum_t d) const
{
const Values & values = lut[fixDay(d)];
return values.year * 10000 + values.month * 100 + values.day_of_month;
}
inline time_t YYYYMMDDToDate(UInt32 num) const
{
return makeDate(num / 10000, num / 100 % 100, num % 100);
}
inline DayNum_t YYYYMMDDToDayNum(UInt32 num) const
{
return makeDayNum(num / 10000, num / 100 % 100, num % 100);
}
inline UInt64 toNumYYYYMMDDhhmmss(time_t t) const
{
const Values & values = find(t);
return
toSecondInaccurate(t)
+ toMinuteInaccurate(t) * 100
+ toHourInaccurate(t) * 10000
+ UInt64(values.day_of_month) * 1000000
+ UInt64(values.month) * 100000000
+ UInt64(values.year) * 10000000000;
}
inline time_t YYYYMMDDhhmmssToTime(UInt64 num) const
{
return makeDateTime(
num / 10000000000,
num / 100000000 % 100,
num / 1000000 % 100,
num / 10000 % 100,
num / 100 % 100,
num % 100);
}
inline std::string timeToString(time_t t) const
{
const Values & values = find(t);
std::string s {"0000-00-00 00:00:00"};
s[0] += values.year / 1000;
s[1] += (values.year / 100) % 10;
s[2] += (values.year / 10) % 10;
s[3] += values.year % 10;
s[5] += values.month / 10;
s[6] += values.month % 10;
s[8] += values.day_of_month / 10;
s[9] += values.day_of_month % 10;
auto hour = toHourInaccurate(t);
auto minute = toMinuteInaccurate(t);
auto second = toSecondInaccurate(t);
s[11] += hour / 10;
s[12] += hour % 10;
s[14] += minute / 10;
s[15] += minute % 10;
s[17] += second / 10;
s[18] += second % 10;
return s;
}
inline std::string dateToString(time_t t) const
{
const Values & values = find(t);
std::string s {"0000-00-00"};
s[0] += values.year / 1000;
s[1] += (values.year / 100) % 10;
s[2] += (values.year / 10) % 10;
s[3] += values.year % 10;
s[5] += values.month / 10;
s[6] += values.month % 10;
s[8] += values.day_of_month / 10;
s[9] += values.day_of_month % 10;
return s;
}
inline std::string dateToString(DayNum_t d) const
{
const Values & values = lut[fixDay(d)];
std::string s {"0000-00-00"};
s[0] += values.year / 1000;
s[1] += (values.year / 100) % 10;
s[2] += (values.year / 10) % 10;
s[3] += values.year % 10;
s[5] += values.month / 10;
s[6] += values.month % 10;
s[8] += values.day_of_month / 10;
s[9] += values.day_of_month % 10;
return s;
}
};
namespace std
{
template<>
struct hash<DayNum_t>
{
size_t operator() (DayNum_t x) const
{
return x;
}
};
}
#pragma once
#include <Poco/ErrorHandler.h>
#include <Poco/Net/SocketDefs.h>
#include <common/logger_useful.h>
/** ErrorHandler для потоков, который в случае неперехваченного исключения,
* выводит ошибку в лог и завершает демон.
*/
class KillingErrorHandler : public Poco::ErrorHandler
{
public:
void exception(const Poco::Exception & e) { std::terminate(); }
void exception(const std::exception & e) { std::terminate(); }
void exception() { std::terminate(); }
};
/** То же самое, но не завершает работу в случае эксепшена типа Socket is not connected.
* Этот эксепшен возникает внутри реализаций Poco::Net::HTTPServer, Poco::Net::TCPServer,
* и иначе его не удаётся перехватить, и сервер завершает работу.
*/
class ServerErrorHandler : public KillingErrorHandler
{
public:
void exception(const Poco::Exception & e)
{
if (e.code() == POCO_ENOTCONN)
LOG_WARNING(log, "Client has gone away.");
else
std::terminate();
}
private:
Logger * log = &Logger::get("ServerErrorHandler");
};
#pragma once
#include <typeinfo>
#include <Poco/Exception.h>
#include <DB/Core/StringRef.h>
#include <common/Common.h>
#define PURE __attribute__((pure))
/** Очень простой класс для чтения JSON (или его кусочков).
* Представляет собой ссылку на кусок памяти, в котором содержится JSON (или его кусочек).
* Не создаёт никаких структур данных в оперативке. Не выделяет память (кроме std::string).
* Не парсит JSON до конца (парсит только часть, необходимую для выполнения вызванного метода).
* Парсинг необходимой части запускается каждый раз при вызове методов.
* Может работать с обрезанным JSON-ом.
* При этом, (в отличие от SAX-подобных парсеров), предоставляет удобные методы для работы.
*
* Эта структура данных более оптимальна, если нужно доставать несколько элементов из большого количества маленьких JSON-ов.
* То есть, подходит для обработки "параметров визитов" и "параметров интернет магазинов" в Яндекс.Метрике.
* Если нужно много работать с одним большим JSON-ом, то этот класс может быть менее оптимальным.
*
* Имеются следующие соглашения:
* 1. Предполагается, что в JSON-е нет пробельных символов.
* 2. Предполагается, что строки в JSON в кодировке UTF-8; также могут использоваться \u-последовательности.
* Строки возвращаются в кодировке UTF-8, \u-последовательности переводятся в UTF-8.
* 3. Но суррогатная пара из двух \uXXXX\uYYYY переводится не в UTF-8, а в CESU-8.
* 4. Корректный JSON парсится корректно.
* При работе с некорректным JSON-ом, кидается исключение или возвращаются неверные результаты.
* (пример: считается, что если встретился символ 'n', то после него идёт 'ull' (null);
* если после него идёт ',1,', то исключение не кидается, и, таким образом, возвращается неверный результат)
* 5. Глубина вложенности JSON ограничена (см. MAX_JSON_DEPTH в cpp файле).
* При необходимости спуститься на большую глубину, кидается исключение.
* 6. В отличие от JSON, пользоволяет парсить значения вида 64-битное число, со знаком, или без.
* При этом, если число дробное - то дробная часть тихо отбрасывается.
* 7. Числа с плавающей запятой парсятся не с максимальной точностью.
*
* Подходит только для чтения JSON, модификация не предусмотрена.
* Все методы immutable, кроме operator++.
*/
POCO_DECLARE_EXCEPTION(Foundation_API, JSONException, Poco::Exception);
class JSON
{
private:
typedef const char * Pos;
Pos ptr_begin;
Pos ptr_end;
unsigned level;
public:
JSON(Pos ptr_begin_, Pos ptr_end_, unsigned level_ = 0) : ptr_begin(ptr_begin_), ptr_end(ptr_end_), level(level_)
{
checkInit();
}
JSON(const std::string & s) : ptr_begin(s.data()), ptr_end(s.data() + s.size()), level(0)
{
checkInit();
}
JSON(const JSON & rhs) : ptr_begin(rhs.ptr_begin), ptr_end(rhs.ptr_end), level(rhs.level) {}
/// Для вставки в контейнеры (создаёт некорректный объект)
JSON() : ptr_begin(nullptr), ptr_end(ptr_begin + 1) {}
const char * data() const PURE { return ptr_begin; }
const char * dataEnd() const PURE { return ptr_end; }
enum ElementType
{
TYPE_OBJECT,
TYPE_ARRAY,
TYPE_NUMBER,
TYPE_STRING,
TYPE_BOOL,
TYPE_NULL,
TYPE_NAME_VALUE_PAIR,
TYPE_NOTYPE,
};
ElementType getType() const PURE;
bool isObject() const PURE { return getType() == TYPE_OBJECT; };
bool isArray() const PURE { return getType() == TYPE_ARRAY; };
bool isNumber() const PURE { return getType() == TYPE_NUMBER; };
bool isString() const PURE { return getType() == TYPE_STRING; };
bool isBool() const PURE { return getType() == TYPE_BOOL; };
bool isNull() const PURE { return getType() == TYPE_NULL; };
bool isNameValuePair() const PURE { return getType() == TYPE_NAME_VALUE_PAIR; };
/// Количество элементов в массиве или объекте; если элемент - не массив или объект, то исключение.
size_t size() const PURE;
/// Является ли массив или объект пустыми; если элемент - не массив или объект, то исключение.
bool empty() const PURE;
/// Получить элемент массива по индексу; если элемент - не массив, то исключение.
JSON operator[] (size_t n) const PURE;
/// Получить элемент объекта по имени; если элемент - не объект, то исключение.
JSON operator[] (const std::string & name) const PURE;
/// Есть ли в объекте элемент с заданным именем; если элемент - не объект, то исключение.
bool has(const std::string & name) const PURE { return has(name.data(), name.size()); }
bool has(const char * data, size_t size) const PURE;
/// Получить значение элемента; исключение, если элемент имеет неправильный тип.
template <class T>
T get() const PURE;
template <class T>
T getWithDefault(const std::string & key, const T & default_ = T()) const PURE;
double getDouble() const PURE;
Int64 getInt() const PURE; /// Отбросить дробную часть.
UInt64 getUInt() const PURE; /// Отбросить дробную часть. Если число отрицательное - исключение.
std::string getString() const PURE;
bool getBool() const PURE;
std::string getName() const PURE; /// Получить имя name-value пары.
JSON getValue() const PURE; /// Получить значение name-value пары.
StringRef getRawString() const PURE;
StringRef getRawName() const PURE;
/// Получить значение элемента; если элемент - строка, то распарсить значение из строки; если не строка или число - то исключение.
double toDouble() const PURE;
Int64 toInt() const PURE;
UInt64 toUInt() const PURE;
/** Преобразовать любой элемент в строку.
* Для строки возвращается её значение, для всех остальных элементов - сериализованное представление.
*/
std::string toString() const PURE;
/// Класс JSON одновременно является итератором по самому себе.
typedef JSON iterator;
typedef JSON const_iterator;
iterator operator* () const PURE { return *this; }
const JSON * operator-> () const PURE { return this; }
bool operator== (const JSON & rhs) const PURE { return ptr_begin == rhs.ptr_begin; }
bool operator!= (const JSON & rhs) const PURE { return ptr_begin != rhs.ptr_begin; }
/** Если элемент - массив или объект, то begin() возвращает iterator,
* который указывает на первый элемент массива или первую name-value пару объекта.
*/
iterator begin() const PURE;
/** end() - значение, которое нельзя использовать; сигнализирует о том, что элементы закончились.
*/
iterator end() const PURE;
/// Перейти к следующему элементу массива или следующей name-value паре объекта.
iterator & operator++();
iterator operator++(int);
/// Есть ли в строке escape-последовательности
bool hasEscapes() const PURE;
/// Есть ли в строке спец-символы из набора \, ', \0, \b, \f, \r, \n, \t, возможно, заэскейпленные.
bool hasSpecialChars() const PURE;
private:
/// Проверить глубину рекурсии, а также корректность диапазона памяти.
void checkInit() const PURE;
/// Проверить, что pos лежит внутри диапазона памяти.
void checkPos(Pos pos) const PURE;
/// Вернуть позицию после заданного элемента.
Pos skipString() const PURE;
Pos skipNumber() const PURE;
Pos skipBool() const PURE;
Pos skipNull() const PURE;
Pos skipNameValuePair() const PURE;
Pos skipObject() const PURE;
Pos skipArray() const PURE;
Pos skipElement() const PURE;
/// Найти name-value пару с заданным именем в объекте.
Pos searchField(const std::string & name) const PURE { return searchField(name.data(), name.size()); }
Pos searchField(const char * data, size_t size) const PURE;
};
template <class T>
T JSON::getWithDefault(const std::string & key, const T & default_) const
{
if (has(key))
return (*this)[key].get<T>();
else
return default_;
}
#undef PURE
#pragma once
#include <Poco/Mutex.h>
#include <Poco/SharedPtr.h>
/** Позволяет хранить некоторый объект, использовать его read-only в разных потоках,
* и заменять его на другой в других потоках.
* Замена производится атомарно, при этом, читающие потоки могут работать с разными версиями объекта.
*
* Использование:
* MultiVersion<T> x;
* - при обновлении данных:
* x.set(new value);
* - при использовании данных для чтения в разных потоках:
* {
* MultiVersion<T>::Version current_version = x.get();
* // используем для чего-нибудь *current_version
* } // здесь перестаём владеть версией; если версия устарела, и её никто больше не использует - она будет уничтожена
*
* Все методы thread-safe.
*/
template <typename T, typename Ptr = Poco::SharedPtr<T> >
class MultiVersion
{
public:
/// Конкретная версия объекта для использования. SharedPtr определяет время жизни версии.
typedef Ptr Version;
/// Инициализация по-умолчанию (NULL-ом).
MultiVersion() = default;
/// Инициализация первой версией.
MultiVersion(const Version & value)
{
set(value);
}
MultiVersion(Version && value)
{
set(std::move(value));
}
/// Получить текущую версию для использования. Возвращает SharedPtr, который определяет время жизни версии.
const Version get() const
{
/// TODO: можно ли заменять SharedPtr lock-free? (Можно, если сделать свою реализацию с использованием cmpxchg16b.)
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
return current_version;
}
/// Обновить объект новой версией.
void set(Version value)
{
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
current_version = value;
}
private:
Version current_version;
mutable Poco::FastMutex mutex;
};
#pragma once
namespace Revision
{
unsigned get();
}
#pragma once
#define likely(x) (__builtin_expect(!!(x), 1))
#define unlikely(x) (__builtin_expect(!!(x), 0))
#pragma once
/// Вспомогательные определения облегчающие работу с PoCo logging.
#include <sstream>
#include <Poco/Logger.h>
#ifndef QUERY_PREVIEW_LENGTH
#define QUERY_PREVIEW_LENGTH 160
#endif
using Poco::Logger;
/// Logs a message to a specified logger with that level.
#define LOG_TRACE(logger, message) do { \
if ((logger)->trace()) {\
std::stringstream oss; \
oss << message; \
(logger)->trace(oss.str());}} while(0)
#define LOG_DEBUG(logger, message) do { \
if ((logger)->debug()) {\
std::stringstream oss; \
oss << message; \
(logger)->debug(oss.str());}} while(0)
#define LOG_INFO(logger, message) do { \
if ((logger)->information()) {\
std::stringstream oss; \
oss << message; \
(logger)->information(oss.str());}} while(0)
#define LOG_NOTICE(logger, message) do { \
if ((logger)->notice()) {\
std::stringstream oss; \
oss << message; \
(logger)->notice(oss.str());}} while(0)
#define LOG_WARNING(logger, message) do { \
if ((logger)->warning()) {\
std::stringstream oss; \
oss << message; \
(logger)->warning(oss.str());}} while(0)
#define LOG_ERROR(logger, message) do { \
if ((logger)->error()) {\
std::stringstream oss; \
oss << message; \
(logger)->error(oss.str());}} while(0)
#define LOG_CRITICAL(logger, message) do { \
if ((logger)->critical()) {\
std::stringstream oss; \
oss << message; \
(logger)->critical(oss.str());}} while(0)
#define LOG_FATAL(logger, message) do { \
if ((logger)->fatal()) {\
std::stringstream oss; \
oss << message; \
(logger)->fatal(oss.str());}} while(0)
#pragma once
/** Пример:
*
* class Derived : public Singleton<Derived>
* {
* friend class Singleton<Derived>;
* ...
* protected:
* Derived() {};
* };
*
* Или так:
*
* class Some
* {
* ...
* };
*
* class SomeSingleton : public Some, public Singleton<SomeSingleton> {}
*/
template<class Subject> class Singleton
{
public:
static Subject & instance()
{
/// Нормально при включенных thread safe statics в gcc (по-умолчанию).
static Subject instance;
return instance;
}
protected:
Singleton(){};
private:
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
};
#pragma once
#include <boost/operators.hpp>
/** https://svn.boost.org/trac/boost/ticket/5182
*/
#define STRONG_TYPEDEF(T, D) \
struct D \
: boost::totally_ordered1< D \
, boost::totally_ordered2< D, T \
> > \
{ \
T t; \
explicit D(const T t_) : t(t_) {}; \
D(): t() {}; \
D(const D & t_) : t(t_.t){} \
D & operator=(const D & rhs) { t = rhs.t; return *this;} \
D & operator=(const T & rhs) { t = rhs; return *this;} \
operator const T & () const {return t; } \
operator T & () { return t; } \
bool operator==(const D & rhs) const { return t == rhs.t; } \
bool operator<(const D & rhs) const { return t < rhs.t; } \
};
#include <string.h>
#include <Poco/UTF8Encoding.h>
#include <Poco/NumberFormatter.h>
#include <Poco/NumberParser.h>
#include <common/JSON.h>
#include <iostream>
#define JSON_MAX_DEPTH 100
POCO_IMPLEMENT_EXCEPTION(JSONException, Poco::Exception, "JSONException")
/// Прочитать беззнаковое целое в простом формате из не-0-terminated строки.
static UInt64 readUIntText(const char * buf, const char * end)
{
UInt64 x = 0;
if (buf == end)
throw JSONException("JSON: cannot parse unsigned integer: unexpected end of data.");
while (buf != end)
{
switch (*buf)
{
case '+':
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
x *= 10;
x += *buf - '0';
break;
default:
return x;
}
++buf;
}
return x;
}
/// Прочитать знаковое целое в простом формате из не-0-terminated строки.
static Int64 readIntText(const char * buf, const char * end)
{
bool negative = false;
Int64 x = 0;
if (buf == end)
throw JSONException("JSON: cannot parse signed integer: unexpected end of data.");
bool run = true;
while (buf != end && run)
{
switch (*buf)
{
case '+':
break;
case '-':
negative = true;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
x *= 10;
x += *buf - '0';
break;
default:
run = false;
break;
}
++buf;
}
if (negative)
x = -x;
return x;
}
/// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки.
static double readFloatText(const char * buf, const char * end)
{
bool negative = false;
double x = 0;
bool after_point = false;
double power_of_ten = 1;
if (buf == end)
throw JSONException("JSON: cannot parse floating point number: unexpected end of data.");
bool run = true;
while (buf != end && run)
{
switch (*buf)
{
case '+':
break;
case '-':
negative = true;
break;
case '.':
after_point = true;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (after_point)
{
power_of_ten /= 10;
x += (*buf - '0') * power_of_ten;
}
else
{
x *= 10;
x += *buf - '0';
}
break;
case 'e':
case 'E':
{
++buf;
Int32 exponent = readIntText(buf, end);
x *= exp10(exponent);
run = false;
break;
}
default:
run = false;
break;
}
++buf;
}
if (negative)
x = -x;
return x;
}
void JSON::checkInit() const
{
if (!(ptr_begin < ptr_end))
throw JSONException("JSON: begin >= end.");
if (level > JSON_MAX_DEPTH)
throw JSONException("JSON: too deep.");
}
JSON::ElementType JSON::getType() const
{
switch (*ptr_begin)
{
case '{':
return TYPE_OBJECT;
case '[':
return TYPE_ARRAY;
case 't':
case 'f':
return TYPE_BOOL;
case 'n':
return TYPE_NULL;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return TYPE_NUMBER;
case '"':
{
/// Проверим - это просто строка или name-value pair
Pos after_string = skipString();
if (after_string < ptr_end && *after_string == ':')
return TYPE_NAME_VALUE_PAIR;
else
return TYPE_STRING;
}
default:
throw JSONException(std::string("JSON: unexpected char ") + *ptr_begin + ", expected one of '{[tfn-0123456789\"'");
}
}
void JSON::checkPos(Pos pos) const
{
if (pos >= ptr_end)
throw JSONException("JSON: unexpected end of data.");
}
JSON::Pos JSON::skipString() const
{
//std::cerr << "skipString()\t" << data() << std::endl;
Pos pos = ptr_begin;
if (*pos != '"')
throw JSONException(std::string("JSON: expected \", got ") + *pos);
++pos;
/// fast path: находим следующую двойную кавычку. Если перед ней нет бэкслеша - значит это конец строки (при допущении корректности JSON).
Pos closing_quote = reinterpret_cast<const char *>(memchr(reinterpret_cast<const void *>(pos), '\"', ptr_end - pos));
if (nullptr != closing_quote && closing_quote[-1] != '\\')
return closing_quote + 1;
/// slow path
while (pos < ptr_end && *pos != '"')
{
if (*pos == '\\')
{
++pos;
checkPos(pos);
if (*pos == 'u')
{
pos += 4;
checkPos(pos);
}
}
++pos;
}
if (*pos != '"')
throw JSONException(std::string("JSON: expected \", got ") + *pos);
++pos;
return pos;
}
JSON::Pos JSON::skipNumber() const
{
//std::cerr << "skipNumber()\t" << data() << std::endl;
Pos pos = ptr_begin;
if (*pos == '-')
++pos;
checkPos(pos);
while (pos < ptr_end && *pos >= '0' && *pos <= '9')
++pos;
if (pos < ptr_end && *pos == '.')
++pos;
while (pos < ptr_end && *pos >= '0' && *pos <= '9')
++pos;
if (pos < ptr_end && (*pos == 'e' || *pos == 'E'))
++pos;
if (pos < ptr_end && *pos == '-')
++pos;
while (pos < ptr_end && *pos >= '0' && *pos <= '9')
++pos;
return pos;
}
JSON::Pos JSON::skipBool() const
{
//std::cerr << "skipBool()\t" << data() << std::endl;
Pos pos = ptr_begin;
if (*ptr_begin == 't')
pos += 4;
else if (*ptr_begin == 'f')
pos += 5;
else
throw JSONException("JSON: expected true or false.");
return pos;
}
JSON::Pos JSON::skipNull() const
{
//std::cerr << "skipNull()\t" << data() << std::endl;
return ptr_begin + 4;
}
JSON::Pos JSON::skipNameValuePair() const
{
//std::cerr << "skipNameValuePair()\t" << data() << std::endl;
Pos pos = skipString();
checkPos(pos);
if (*pos != ':')
throw JSONException("JSON: expected :.");
++pos;
return JSON(pos, ptr_end, level + 1).skipElement();
}
JSON::Pos JSON::skipArray() const
{
//std::cerr << "skipArray()\t" << data() << std::endl;
if (!isArray())
throw JSONException("JSON: expected [");
Pos pos = ptr_begin;
++pos;
checkPos(pos);
if (*pos == ']')
return ++pos;
while (1)
{
pos = JSON(pos, ptr_end, level + 1).skipElement();
if (*pos != ',')
break;
++pos;
}
return ++pos;
}
JSON::Pos JSON::skipObject() const
{
//std::cerr << "skipObject()\t" << data() << std::endl;
if (!isObject())
throw JSONException("JSON: expected {");
Pos pos = ptr_begin;
++pos;
checkPos(pos);
if (*pos == '}')
return ++pos;
while (1)
{
pos = JSON(pos, ptr_end, level + 1).skipNameValuePair();
if (*pos != ',')
break;
++pos;
}
return ++pos;
}
JSON::Pos JSON::skipElement() const
{
//std::cerr << "skipElement()\t" << data() << std::endl;
ElementType type = getType();
switch(type)
{
case TYPE_NULL:
return skipNull();
case TYPE_BOOL:
return skipBool();
case TYPE_NUMBER:
return skipNumber();
case TYPE_STRING:
return skipString();
case TYPE_NAME_VALUE_PAIR:
return skipNameValuePair();
case TYPE_ARRAY:
return skipArray();
case TYPE_OBJECT:
return skipObject();
default:
throw JSONException("Logical error in JSON: unknown element type: " + Poco::NumberFormatter::format(type));
}
}
size_t JSON::size() const
{
size_t i = 0;
for (const_iterator it = begin(); it != end(); ++it)
++i;
return i;
}
bool JSON::empty() const
{
return size() == 0;
}
JSON JSON::operator[] (size_t n) const
{
ElementType type = getType();
if (type != TYPE_ARRAY)
throw JSONException("JSON: not array when calling operator[](size_t) method.");
Pos pos = ptr_begin;
++pos;
checkPos(pos);
size_t i = 0;
const_iterator it = begin();
while (i < n && it != end())
++it, ++i;
if (i != n)
throw JSONException("JSON: array index " + Poco::NumberFormatter::format(n) + " out of bounds.");
return *it;
}
JSON::Pos JSON::searchField(const char * data, size_t size) const
{
ElementType type = getType();
if (type != TYPE_OBJECT)
throw JSONException("JSON: not object when calling operator[](const char *) or has(const char *) method.");
const_iterator it = begin();
for (; it != end(); ++it)
{
if (!it->hasEscapes())
{
if (static_cast<int>(size) + 2 > it->dataEnd() - it->data())
continue;
if (!strncmp(data, it->data() + 1, size))
break;
}
else
{
std::string current_name = it->getName();
if (current_name.size() == size && 0 == memcmp(current_name.data(), data, size))
break;
}
}
if (it == end())
return nullptr;
else
return it->data();
}
bool JSON::hasEscapes() const
{
Pos pos = ptr_begin + 1;
while (pos < ptr_end && *pos != '"' && *pos != '\\')
++pos;
if (*pos == '"')
return false;
else if (*pos == '\\')
return true;
throw JSONException("JSON: unexpected end of data.");
}
bool JSON::hasSpecialChars() const
{
Pos pos = ptr_begin + 1;
while (pos < ptr_end && *pos != '"'
&& *pos != '\\' && *pos != '\r' && *pos != '\n' && *pos != '\t'
&& *pos != '\f' && *pos != '\b' && *pos != '\0' && *pos != '\'')
++pos;
if (*pos == '"')
return false;
else if (pos < ptr_end)
return true;
throw JSONException("JSON: unexpected end of data.");
}
JSON JSON::operator[] (const std::string & name) const
{
Pos pos = searchField(name);
if (!pos)
throw JSONException("JSON: there is no element '" + std::string(name) + "' in object.");
return JSON(pos, ptr_end, level + 1).getValue();
}
bool JSON::has(const char * data, size_t size) const
{
return nullptr != searchField(data, size);
}
double JSON::getDouble() const
{
return readFloatText(ptr_begin, ptr_end);
}
Int64 JSON::getInt() const
{
return readIntText(ptr_begin, ptr_end);
}
UInt64 JSON::getUInt() const
{
return readUIntText(ptr_begin, ptr_end);
}
bool JSON::getBool() const
{
if (*ptr_begin == 't')
return true;
if (*ptr_begin == 'f')
return false;
throw JSONException("JSON: cannot parse boolean.");
}
std::string JSON::getString() const
{
Pos s = ptr_begin;
if (*s != '"')
throw JSONException(std::string("JSON: expected \", got ") + *s);
++s;
checkPos(s);
std::string buf;
while (s < ptr_end)
{
switch (*s)
{
case '\\':
++s;
checkPos(s);
switch(*s)
{
case '"':
buf += '"';
break;
case '\\':
buf += '\\';
break;
case '/':
buf += '/';
break;
case 'b':
buf += '\b';
break;
case 'f':
buf += '\f';
break;
case 'n':
buf += '\n';
break;
case 'r':
buf += '\r';
break;
case 't':
buf += '\t';
break;
case 'u':
{
Poco::UTF8Encoding utf8;
++s;
checkPos(s + 4);
std::string hex(s, 4);
s += 3;
int unicode;
try
{
unicode = Poco::NumberParser::parseHex(hex);
}
catch (const Poco::SyntaxException & e)
{
throw JSONException("JSON: incorrect syntax: incorrect HEX code.");
}
buf.resize(buf.size() + 6); /// максимальный размер UTF8 многобайтовой последовательности
int res = utf8.convert(unicode,
reinterpret_cast<unsigned char *>(const_cast<char*>(buf.data())) + buf.size() - 6, 6);
if (!res)
throw JSONException("JSON: cannot convert unicode " + Poco::NumberFormatter::format(unicode)
+ " to UTF8.");
buf.resize(buf.size() - 6 + res);
break;
}
default:
buf += *s;
break;
}
++s;
break;
case '"':
return buf;
default:
buf += *s;
++s;
break;
}
}
throw JSONException("JSON: incorrect syntax (expected end of string, found end of JSON).");
}
std::string JSON::getName() const
{
return getString();
}
StringRef JSON::getRawString() const
{
Pos s = ptr_begin;
if (*s != '"')
throw JSONException(std::string("JSON: expected \", got ") + *s);
while (++s != ptr_end && *s != '"');
if (s != ptr_end )
return StringRef(ptr_begin + 1, s - ptr_begin - 1);
throw JSONException("JSON: incorrect syntax (expected end of string, found end of JSON).");
}
StringRef JSON::getRawName() const
{
return getRawString();
}
JSON JSON::getValue() const
{
Pos pos = skipString();
checkPos(pos);
if (*pos != ':')
throw JSONException("JSON: expected :.");
++pos;
checkPos(pos);
return JSON(pos, ptr_end, level + 1);
}
double JSON::toDouble() const
{
ElementType type = getType();
if (type == TYPE_NUMBER)
return getDouble();
else if (type == TYPE_STRING)
return JSON(ptr_begin + 1, ptr_end, level + 1).getDouble();
else
throw JSONException("JSON: cannot convert value to double.");
}
Int64 JSON::toInt() const
{
ElementType type = getType();
if (type == TYPE_NUMBER)
return getInt();
else if (type == TYPE_STRING)
return JSON(ptr_begin + 1, ptr_end, level + 1).getInt();
else
throw JSONException("JSON: cannot convert value to signed integer.");
}
UInt64 JSON::toUInt() const
{
ElementType type = getType();
if (type == TYPE_NUMBER)
return getUInt();
else if (type == TYPE_STRING)
return JSON(ptr_begin + 1, ptr_end, level + 1).getUInt();
else
throw JSONException("JSON: cannot convert value to unsigned integer.");
}
std::string JSON::toString() const
{
ElementType type = getType();
if (type == TYPE_STRING)
return getString();
else
{
Pos pos = skipElement();
return std::string(ptr_begin, pos - ptr_begin);
}
}
JSON::iterator JSON::iterator::begin() const
{
ElementType type = getType();
if (type != TYPE_ARRAY && type != TYPE_OBJECT)
throw JSONException("JSON: not array or object when calling begin() method.");
//std::cerr << "begin()\t" << data() << std::endl;
Pos pos = ptr_begin + 1;
checkPos(pos);
if (*pos == '}' || *pos == ']')
return end();
return JSON(pos, ptr_end, level + 1);
}
JSON::iterator JSON::iterator::end() const
{
return JSON(nullptr, ptr_end, level + 1);
}
JSON::iterator & JSON::iterator::operator++()
{
Pos pos = skipElement();
checkPos(pos);
if (*pos != ',')
ptr_begin = nullptr;
else
{
++pos;
checkPos(pos);
ptr_begin = pos;
}
return *this;
}
JSON::iterator JSON::iterator::operator++(int)
{
iterator copy(*this);
++*this;
return copy;
}
template <>
double JSON::get<double>() const
{
return getDouble();
}
template <>
std::string JSON::get<std::string>() const
{
return getString();
}
template <>
Int64 JSON::get<Int64>() const
{
return getInt();
}
template <>
UInt64 JSON::get<UInt64>() const
{
return getUInt();
}
template <>
bool JSON::get<bool>() const
{
return getBool();
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册