mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 00:30:49 +00:00
Addition to prev. revision [#METR-18202].
This commit is contained in:
parent
1f6def094f
commit
13a45bce25
40
libs/libcommon/include/common/ApplicationExt.h
Normal file
40
libs/libcommon/include/common/ApplicationExt.h
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @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
|
32
libs/libcommon/include/common/ApplicationServerExt.h
Normal file
32
libs/libcommon/include/common/ApplicationServerExt.h
Normal file
@ -0,0 +1,32 @@
|
||||
#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; \
|
||||
}
|
55
libs/libcommon/include/common/Common.h
Normal file
55
libs/libcommon/include/common/Common.h
Normal file
@ -0,0 +1,55 @@
|
||||
#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; }
|
||||
};
|
||||
}
|
77
libs/libcommon/include/common/DateLUT.h
Normal file
77
libs/libcommon/include/common/DateLUT.h
Normal file
@ -0,0 +1,77 @@
|
||||
#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;
|
||||
};
|
501
libs/libcommon/include/common/DateLUTImpl.h
Normal file
501
libs/libcommon/include/common/DateLUTImpl.h
Normal file
@ -0,0 +1,501 @@
|
||||
#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;
|
||||
}
|
||||
};
|
||||
}
|
37
libs/libcommon/include/common/ErrorHandlers.h
Normal file
37
libs/libcommon/include/common/ErrorHandlers.h
Normal file
@ -0,0 +1,37 @@
|
||||
#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");
|
||||
};
|
197
libs/libcommon/include/common/JSON.h
Normal file
197
libs/libcommon/include/common/JSON.h
Normal file
@ -0,0 +1,197 @@
|
||||
#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
|
62
libs/libcommon/include/common/MultiVersion.h
Normal file
62
libs/libcommon/include/common/MultiVersion.h
Normal file
@ -0,0 +1,62 @@
|
||||
#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;
|
||||
};
|
6
libs/libcommon/include/common/Revision.h
Normal file
6
libs/libcommon/include/common/Revision.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace Revision
|
||||
{
|
||||
unsigned get();
|
||||
}
|
4
libs/libcommon/include/common/likely.h
Normal file
4
libs/libcommon/include/common/likely.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define likely(x) (__builtin_expect(!!(x), 1))
|
||||
#define unlikely(x) (__builtin_expect(!!(x), 0))
|
61
libs/libcommon/include/common/logger_useful.h
Normal file
61
libs/libcommon/include/common/logger_useful.h
Normal file
@ -0,0 +1,61 @@
|
||||
#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)
|
38
libs/libcommon/include/common/singleton.h
Normal file
38
libs/libcommon/include/common/singleton.h
Normal file
@ -0,0 +1,38 @@
|
||||
#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&);
|
||||
};
|
23
libs/libcommon/include/common/strong_typedef.h
Normal file
23
libs/libcommon/include/common/strong_typedef.h
Normal file
@ -0,0 +1,23 @@
|
||||
#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; } \
|
||||
};
|
788
libs/libcommon/src/JSON.cpp
Normal file
788
libs/libcommon/src/JSON.cpp
Normal file
@ -0,0 +1,788 @@
|
||||
#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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user