Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
2dot5
ClickHouse
提交
13a45bce
C
ClickHouse
项目概览
2dot5
/
ClickHouse
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
ClickHouse
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
13a45bce
编写于
9月 29, 2015
作者:
A
Alexey Milovidov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Addition to prev. revision [#METR-18202].
上级
1f6def09
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
1921 addition
and
0 deletion
+1921
-0
libs/libcommon/include/common/ApplicationExt.h
libs/libcommon/include/common/ApplicationExt.h
+40
-0
libs/libcommon/include/common/ApplicationServerExt.h
libs/libcommon/include/common/ApplicationServerExt.h
+32
-0
libs/libcommon/include/common/Common.h
libs/libcommon/include/common/Common.h
+55
-0
libs/libcommon/include/common/DateLUT.h
libs/libcommon/include/common/DateLUT.h
+77
-0
libs/libcommon/include/common/DateLUTImpl.h
libs/libcommon/include/common/DateLUTImpl.h
+501
-0
libs/libcommon/include/common/ErrorHandlers.h
libs/libcommon/include/common/ErrorHandlers.h
+37
-0
libs/libcommon/include/common/JSON.h
libs/libcommon/include/common/JSON.h
+197
-0
libs/libcommon/include/common/MultiVersion.h
libs/libcommon/include/common/MultiVersion.h
+62
-0
libs/libcommon/include/common/Revision.h
libs/libcommon/include/common/Revision.h
+6
-0
libs/libcommon/include/common/likely.h
libs/libcommon/include/common/likely.h
+4
-0
libs/libcommon/include/common/logger_useful.h
libs/libcommon/include/common/logger_useful.h
+61
-0
libs/libcommon/include/common/singleton.h
libs/libcommon/include/common/singleton.h
+38
-0
libs/libcommon/include/common/strong_typedef.h
libs/libcommon/include/common/strong_typedef.h
+23
-0
libs/libcommon/src/JSON.cpp
libs/libcommon/src/JSON.cpp
+788
-0
未找到文件。
libs/libcommon/include/common/ApplicationExt.h
0 → 100644
浏览文件 @
13a45bce
/**
* @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
libs/libcommon/include/common/ApplicationServerExt.h
0 → 100644
浏览文件 @
13a45bce
#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; \
}
libs/libcommon/include/common/Common.h
0 → 100644
浏览文件 @
13a45bce
#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
;
}
};
}
libs/libcommon/include/common/DateLUT.h
0 → 100644
浏览文件 @
13a45bce
#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
;
};
libs/libcommon/include/common/DateLUTImpl.h
0 → 100644
浏览文件 @
13a45bce
#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
;
}
};
}
libs/libcommon/include/common/ErrorHandlers.h
0 → 100644
浏览文件 @
13a45bce
#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"
);
};
libs/libcommon/include/common/JSON.h
0 → 100644
浏览文件 @
13a45bce
#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
libs/libcommon/include/common/MultiVersion.h
0 → 100644
浏览文件 @
13a45bce
#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
;
};
libs/libcommon/include/common/Revision.h
0 → 100644
浏览文件 @
13a45bce
#pragma once
namespace
Revision
{
unsigned
get
();
}
libs/libcommon/include/common/likely.h
0 → 100644
浏览文件 @
13a45bce
#pragma once
#define likely(x) (__builtin_expect(!!(x), 1))
#define unlikely(x) (__builtin_expect(!!(x), 0))
libs/libcommon/include/common/logger_useful.h
0 → 100644
浏览文件 @
13a45bce
#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)
libs/libcommon/include/common/singleton.h
0 → 100644
浏览文件 @
13a45bce
#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
&
);
};
libs/libcommon/include/common/strong_typedef.h
0 → 100644
浏览文件 @
13a45bce
#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; } \
};
libs/libcommon/src/JSON.cpp
0 → 100644
浏览文件 @
13a45bce
#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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录