hikyuu 2.1.0__cp311-none-win_amd64.whl → 2.1.1__cp311-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hikyuu/cpp/boost_date_time-mt.dll +0 -0
- hikyuu/cpp/boost_serialization-mt.dll +0 -0
- hikyuu/cpp/boost_wserialization-mt.dll +0 -0
- hikyuu/cpp/core311.pyd +0 -0
- hikyuu/cpp/hikyuu.dll +0 -0
- hikyuu/extend.py +3 -1
- hikyuu/gui/HikyuuTDX.py +11 -6
- hikyuu/include/hikyuu/DataType.h +2 -1
- hikyuu/include/hikyuu/KRecord.h +1 -1
- hikyuu/include/hikyuu/StockManager.h +3 -0
- hikyuu/include/hikyuu/analysis/combinate.h +1 -1
- hikyuu/include/hikyuu/config.h +0 -12
- hikyuu/include/hikyuu/strategy/AccountTradeManager.h +3 -1
- hikyuu/include/hikyuu/utilities/FilterNode.h +267 -0
- hikyuu/include/hikyuu/utilities/LRUCache11.h +230 -0
- hikyuu/include/hikyuu/{Log.h → utilities/Log.h} +91 -113
- hikyuu/include/hikyuu/utilities/Null.h +1 -0
- hikyuu/include/hikyuu/utilities/Parameter.h +2 -1
- hikyuu/include/hikyuu/utilities/ResourcePool.h +636 -0
- hikyuu/include/hikyuu/utilities/SpendTimer.h +10 -9
- hikyuu/include/hikyuu/utilities/TimerManager.h +2 -2
- hikyuu/include/hikyuu/utilities/any_to_string.h +142 -0
- hikyuu/include/hikyuu/utilities/arithmetic.h +69 -33
- hikyuu/include/hikyuu/utilities/base64.h +59 -0
- hikyuu/include/hikyuu/utilities/config.h +41 -0
- hikyuu/include/hikyuu/utilities/datetime/Datetime.h +41 -31
- hikyuu/include/hikyuu/utilities/datetime/TimeDelta.h +24 -13
- hikyuu/include/hikyuu/utilities/db_connect/DBCondition.h +48 -48
- hikyuu/include/hikyuu/utilities/db_connect/DBConnect.h +10 -0
- hikyuu/include/hikyuu/utilities/db_connect/DBConnectBase.h +5 -22
- hikyuu/include/hikyuu/utilities/db_connect/DBUpgrade.h +3 -3
- hikyuu/include/hikyuu/utilities/db_connect/SQLException.h +1 -1
- hikyuu/include/hikyuu/utilities/db_connect/SQLResultSet.h +1 -1
- hikyuu/include/hikyuu/utilities/db_connect/SQLStatementBase.h +7 -7
- hikyuu/include/hikyuu/utilities/db_connect/TableMacro.h +1 -2
- hikyuu/include/hikyuu/utilities/db_connect/mysql/MySQLConnect.h +9 -9
- hikyuu/include/hikyuu/utilities/db_connect/mysql/MySQLStatement.h +18 -18
- hikyuu/include/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h +3 -3
- hikyuu/include/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h +2 -2
- hikyuu/include/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.h +6 -6
- hikyuu/include/hikyuu/{exception.h → utilities/exception.h} +1 -0
- hikyuu/include/hikyuu/utilities/http_client/HttpClient.h +229 -0
- hikyuu/include/hikyuu/utilities/http_client/nng_wrap.h +517 -0
- hikyuu/include/hikyuu/utilities/http_client/url.h +25 -0
- hikyuu/include/hikyuu/utilities/{IniParser.h → ini_parser/IniParser.h} +10 -5
- hikyuu/include/hikyuu/utilities/ini_parser/__init__.py +1 -0
- hikyuu/include/hikyuu/utilities/md5.h +41 -0
- hikyuu/include/hikyuu/utilities/mo/__init__.py +1 -0
- hikyuu/include/hikyuu/utilities/mo/mo.h +48 -0
- hikyuu/include/hikyuu/utilities/mo/moFileReader.h +836 -0
- hikyuu/include/hikyuu/{global → utilities}/node/NodeClient.h +25 -18
- hikyuu/include/hikyuu/{global → utilities}/node/NodeError.h +1 -1
- hikyuu/include/hikyuu/{global → utilities}/node/NodeMessage.h +3 -2
- hikyuu/include/hikyuu/utilities/node/NodeServer.h +246 -0
- hikyuu/include/hikyuu/utilities/node/__init__.py +1 -0
- hikyuu/include/hikyuu/utilities/os.h +16 -15
- hikyuu/include/hikyuu/utilities/snowflake.h +110 -0
- hikyuu/include/hikyuu/utilities/string_view.h +70 -0
- hikyuu/include/hikyuu/utilities/thread/MQStealThreadPool.h +3 -3
- hikyuu/include/hikyuu/utilities/thread/MQThreadPool.h +3 -3
- hikyuu/include/hikyuu/utilities/thread/StealThreadPool.h +3 -3
- hikyuu/include/hikyuu/utilities/thread/ThreadPool.h +3 -3
- hikyuu/include/hikyuu/version.h +4 -4
- hikyuu/sqlite3.dll +0 -0
- hikyuu/vcruntime140.dll +0 -0
- hikyuu/vcruntime140_1.dll +0 -0
- hikyuu-2.1.1.dist-info/METADATA +115 -0
- {hikyuu-2.1.0.dist-info → hikyuu-2.1.1.dist-info}/RECORD +73 -53
- {hikyuu-2.1.0.dist-info → hikyuu-2.1.1.dist-info}/top_level.txt +4 -1
- hikyuu/README.rst +0 -79
- hikyuu-2.1.0.dist-info/METADATA +0 -126
- /hikyuu/include/hikyuu/{global/node → utilities/http_client}/__init__.py +0 -0
- {hikyuu-2.1.0.dist-info → hikyuu-2.1.1.dist-info}/LICENSE +0 -0
- {hikyuu-2.1.0.dist-info → hikyuu-2.1.1.dist-info}/WHEEL +0 -0
- {hikyuu-2.1.0.dist-info → hikyuu-2.1.1.dist-info}/entry_points.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
hikyuu/cpp/core311.pyd
CHANGED
|
Binary file
|
hikyuu/cpp/hikyuu.dll
CHANGED
|
Binary file
|
hikyuu/extend.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#
|
|
2
2
|
# 对 C++ 引出类和函数进行扩展, pybind11 对小函数到导出效率不如 python 直接执行
|
|
3
3
|
#
|
|
4
|
+
|
|
5
|
+
# 优先加载 hikyuu 库,防止 windows 公共依赖库不同导致DLL初始化失败
|
|
6
|
+
from .core import *
|
|
4
7
|
import numpy as np
|
|
5
8
|
import pandas as pd
|
|
6
9
|
from datetime import *
|
|
7
|
-
from .core import *
|
|
8
10
|
|
|
9
11
|
# ------------------------------------------------------------------
|
|
10
12
|
# 增加Datetime、Stock的hash支持,以便可做为dict的key
|
hikyuu/gui/HikyuuTDX.py
CHANGED
|
@@ -8,6 +8,10 @@ import datetime
|
|
|
8
8
|
import multiprocessing
|
|
9
9
|
from configparser import ConfigParser
|
|
10
10
|
from logging.handlers import QueueListener
|
|
11
|
+
|
|
12
|
+
# 优先加载,处理 VS 17.10 升级后依赖 dll 不兼容问题
|
|
13
|
+
import hikyuu
|
|
14
|
+
|
|
11
15
|
import PyQt5
|
|
12
16
|
|
|
13
17
|
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox
|
|
@@ -240,6 +244,13 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
|
|
|
240
244
|
self.mp_log_q_lisener.start()
|
|
241
245
|
|
|
242
246
|
def initUI(self):
|
|
247
|
+
# 读取配置文件放在 output 重定向之前,防止配置文件读取失败没有提示
|
|
248
|
+
# 读取保存的配置文件信息,如果不存在,则使用默认配置
|
|
249
|
+
this_dir = self.getUserConfigDir()
|
|
250
|
+
import_config = ConfigParser()
|
|
251
|
+
if os.path.exists(this_dir + '/importdata-gui.ini'):
|
|
252
|
+
import_config.read(this_dir + '/importdata-gui.ini', encoding='utf-8')
|
|
253
|
+
|
|
243
254
|
self._is_sched_import_running = False
|
|
244
255
|
self._is_collect_running = False
|
|
245
256
|
self._stream = None
|
|
@@ -272,12 +283,6 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
|
|
|
272
283
|
self.time_start_dateEdit.setMinimumDate(today - datetime.timedelta(300))
|
|
273
284
|
self.collect_status_label.setText("已停止")
|
|
274
285
|
|
|
275
|
-
# 读取保存的配置文件信息,如果不存在,则使用默认配置
|
|
276
|
-
this_dir = self.getUserConfigDir()
|
|
277
|
-
import_config = ConfigParser()
|
|
278
|
-
if os.path.exists(this_dir + '/importdata-gui.ini'):
|
|
279
|
-
import_config.read(this_dir + '/importdata-gui.ini', encoding='utf-8')
|
|
280
|
-
|
|
281
286
|
# 初始化导入行情数据类型配置
|
|
282
287
|
self.import_stock_checkBox.setChecked(import_config.getboolean('quotation', 'stock', fallback=True))
|
|
283
288
|
self.import_fund_checkBox.setChecked(import_config.getboolean('quotation', 'fund', fallback=True))
|
hikyuu/include/hikyuu/DataType.h
CHANGED
hikyuu/include/hikyuu/KRecord.h
CHANGED
hikyuu/include/hikyuu/config.h
CHANGED
|
@@ -19,18 +19,6 @@
|
|
|
19
19
|
// 检查下标越界
|
|
20
20
|
#define CHECK_ACCESS_BOUND 1
|
|
21
21
|
|
|
22
|
-
// 默认激活的日志级别
|
|
23
|
-
#define LOG_ACTIVE_LEVEL 2
|
|
24
|
-
|
|
25
|
-
// 是否使用 spdlog
|
|
26
|
-
#define USE_SPDLOG_LOGGER 1
|
|
27
|
-
|
|
28
|
-
// 使用异步 logger
|
|
29
|
-
#define HKU_USE_SPDLOG_ASYNC_LOGGER 0
|
|
30
|
-
|
|
31
|
-
// spdlog默认日志级别
|
|
32
|
-
#define SPDLOG_ACTIVE_LEVEL 2
|
|
33
|
-
|
|
34
22
|
// 启用MSVC内存泄漏检查
|
|
35
23
|
#define ENABLE_MSVC_LEAK_DETECT 0
|
|
36
24
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#if 0
|
|
10
11
|
#include <httplib.h>
|
|
11
12
|
#include "../trade_manage/TradeManagerBase.h"
|
|
12
13
|
|
|
@@ -426,4 +427,5 @@ inline TMPtr crtAccountTM(const string& name, const string& pwd) {
|
|
|
426
427
|
return std::make_shared<AccountTradeManager>(name, pwd);
|
|
427
428
|
}
|
|
428
429
|
|
|
429
|
-
} // namespace hku
|
|
430
|
+
} // namespace hku
|
|
431
|
+
#endif
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023 hikyuu.org
|
|
3
|
+
*
|
|
4
|
+
* Created on: 2023-01-13
|
|
5
|
+
* Author: fasiondog
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <memory>
|
|
11
|
+
#include <functional>
|
|
12
|
+
#include <forward_list>
|
|
13
|
+
#include <unordered_map>
|
|
14
|
+
#include "thread/ThreadPool.h"
|
|
15
|
+
#include "any_to_string.h"
|
|
16
|
+
#include "Log.h"
|
|
17
|
+
|
|
18
|
+
namespace hku {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @brief 过滤节点
|
|
22
|
+
*/
|
|
23
|
+
class FilterNode {
|
|
24
|
+
public:
|
|
25
|
+
FilterNode() = default;
|
|
26
|
+
FilterNode(const FilterNode&) = default;
|
|
27
|
+
virtual ~FilterNode() = default;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @brief 构造函数
|
|
31
|
+
* @param exclusive 是否排他。为 true 时,只执行第一个遇到的满足过滤条件的子节点
|
|
32
|
+
*/
|
|
33
|
+
explicit FilterNode(bool exclusive) : m_exclusive(exclusive) {}
|
|
34
|
+
|
|
35
|
+
FilterNode(FilterNode&& rv)
|
|
36
|
+
: m_value(std::move(rv.m_value)),
|
|
37
|
+
m_children(std::move(rv.m_children)),
|
|
38
|
+
m_exclusive(rv.m_exclusive) {}
|
|
39
|
+
|
|
40
|
+
FilterNode& operator=(const FilterNode& rv) {
|
|
41
|
+
if (this == &rv)
|
|
42
|
+
return *this;
|
|
43
|
+
m_value = rv.m_value;
|
|
44
|
+
m_children = rv.m_children;
|
|
45
|
+
m_exclusive = rv.m_exclusive;
|
|
46
|
+
return *this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
FilterNode& operator=(FilterNode&& rv) {
|
|
50
|
+
if (this == &rv)
|
|
51
|
+
return *this;
|
|
52
|
+
m_value = std::move(rv.m_value);
|
|
53
|
+
m_children = std::move(rv.m_children);
|
|
54
|
+
m_exclusive = rv.m_exclusive;
|
|
55
|
+
return *this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
using ptr_t = std::shared_ptr<FilterNode>;
|
|
59
|
+
|
|
60
|
+
ptr_t addChild(const ptr_t& child) {
|
|
61
|
+
HKU_CHECK(child, "Invalid input child! child is null!");
|
|
62
|
+
m_children.push_front(child);
|
|
63
|
+
return child;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
bool exclusive() const {
|
|
67
|
+
return m_exclusive;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void exclusive(bool exclusive) {
|
|
71
|
+
m_exclusive = exclusive;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
using const_iterator = std::forward_list<ptr_t>::const_iterator;
|
|
75
|
+
using iterator = std::forward_list<ptr_t>::iterator;
|
|
76
|
+
const_iterator cbegin() const {
|
|
77
|
+
return m_children.cbegin();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const_iterator cend() const {
|
|
81
|
+
return m_children.cend();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
iterator begin() {
|
|
85
|
+
return m_children.begin();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
iterator end() {
|
|
89
|
+
return m_children.end();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
bool run(const any_t& data) noexcept {
|
|
93
|
+
if (_filter(data)) {
|
|
94
|
+
_process(data);
|
|
95
|
+
for (auto& node : m_children) {
|
|
96
|
+
if (node->run(data) && m_exclusive) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
virtual bool filter(const any_t& data) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
virtual void process(const any_t& data) {}
|
|
110
|
+
|
|
111
|
+
template <typename ValueT>
|
|
112
|
+
ValueT value() const {
|
|
113
|
+
return any_cast<ValueT>(m_value);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
template <typename ValueT>
|
|
117
|
+
void value(const ValueT& value) {
|
|
118
|
+
m_value = value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
bool has_value() const {
|
|
122
|
+
#if !HKU_OS_IOS && CPP_STANDARD >= CPP_STANDARD_17
|
|
123
|
+
return m_value.has_value();
|
|
124
|
+
#else
|
|
125
|
+
return !m_value.empty();
|
|
126
|
+
#endif
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private:
|
|
130
|
+
bool _filter(const any_t& data) noexcept {
|
|
131
|
+
try {
|
|
132
|
+
return filter(data);
|
|
133
|
+
} catch (const std::exception& e) {
|
|
134
|
+
HKU_WARN("Node filter exist error! {}", e.what());
|
|
135
|
+
} catch (...) {
|
|
136
|
+
HKU_WARN("Node filter exist unknown error!");
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
void _process(const any_t& data) noexcept {
|
|
142
|
+
try {
|
|
143
|
+
process(data);
|
|
144
|
+
} catch (const std::exception& e) {
|
|
145
|
+
HKU_WARN("Node process exist error! {}", e.what());
|
|
146
|
+
} catch (...) {
|
|
147
|
+
HKU_WARN("Node process exist unknown error!");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
protected:
|
|
152
|
+
any_t m_value;
|
|
153
|
+
|
|
154
|
+
private:
|
|
155
|
+
std::forward_list<ptr_t> m_children;
|
|
156
|
+
bool m_exclusive = false;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
template <>
|
|
160
|
+
inline const any_t& FilterNode::value() const {
|
|
161
|
+
return m_value;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
typedef std::shared_ptr<FilterNode> FilterNodePtr;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @brief 绑定过滤节点,通过 std::function 绑定自定义的 filter 和 process 处理函数
|
|
168
|
+
*/
|
|
169
|
+
class BindFilterNode : public FilterNode {
|
|
170
|
+
public:
|
|
171
|
+
BindFilterNode() = default;
|
|
172
|
+
virtual ~BindFilterNode() = default;
|
|
173
|
+
|
|
174
|
+
using filter_func = std::function<bool(FilterNode*, const any_t&)>;
|
|
175
|
+
using process_func = std::function<void(FilterNode*, const any_t&)>;
|
|
176
|
+
|
|
177
|
+
explicit BindFilterNode(const process_func& process) : FilterNode(false), m_process(process) {}
|
|
178
|
+
explicit BindFilterNode(process_func&& process)
|
|
179
|
+
: FilterNode(false), m_process(std::move(process)) {}
|
|
180
|
+
|
|
181
|
+
BindFilterNode(const filter_func& filter, const process_func& process, bool exclusive = false)
|
|
182
|
+
: FilterNode(exclusive), m_filter(filter), m_process(process) {}
|
|
183
|
+
|
|
184
|
+
BindFilterNode(filter_func&& filter, process_func&& process, bool exclusive = false)
|
|
185
|
+
: FilterNode(exclusive), m_filter(std::move(filter)), m_process(std::move(process)) {}
|
|
186
|
+
|
|
187
|
+
virtual bool filter(const any_t& data) {
|
|
188
|
+
return m_filter ? m_filter(this, data) : true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
virtual void process(const any_t& data) {
|
|
192
|
+
if (m_process) {
|
|
193
|
+
m_process(this, data);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private:
|
|
198
|
+
filter_func m_filter;
|
|
199
|
+
process_func m_process;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @brief 异步串行事件处理器
|
|
204
|
+
* @tparam EventT
|
|
205
|
+
*/
|
|
206
|
+
template <class EventT>
|
|
207
|
+
class AsyncSerialEventProcessor {
|
|
208
|
+
public:
|
|
209
|
+
/**
|
|
210
|
+
* @brief 构造函数
|
|
211
|
+
* @param quit_wait 退出时等待所有任务完成
|
|
212
|
+
*/
|
|
213
|
+
explicit AsyncSerialEventProcessor(bool quit_wait = true) : m_quit_wait(quit_wait) {
|
|
214
|
+
m_tg = std::unique_ptr<ThreadPool>(new ThreadPool(1));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** 析构函数 */
|
|
218
|
+
virtual ~AsyncSerialEventProcessor() {
|
|
219
|
+
if (m_quit_wait) {
|
|
220
|
+
m_tg->join();
|
|
221
|
+
} else {
|
|
222
|
+
m_tg->stop();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @brief 添加事件处理节点
|
|
228
|
+
*
|
|
229
|
+
* @param event 事件
|
|
230
|
+
* @param action 对应的处理节点
|
|
231
|
+
* @return 返回加入的节点
|
|
232
|
+
*/
|
|
233
|
+
FilterNodePtr addAction(const EventT& event, const FilterNodePtr& action) {
|
|
234
|
+
HKU_CHECK(action, "Input action is null!");
|
|
235
|
+
std::lock_guard<std::mutex> lock(m_mutex);
|
|
236
|
+
auto iter = m_trees.find(event);
|
|
237
|
+
if (iter != m_trees.end()) {
|
|
238
|
+
iter->second->addChild(action);
|
|
239
|
+
} else {
|
|
240
|
+
m_trees[event] = action;
|
|
241
|
+
}
|
|
242
|
+
return action;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @brief 分派事件消息
|
|
247
|
+
*
|
|
248
|
+
* @param event 事件
|
|
249
|
+
* @param data 事件附加信息
|
|
250
|
+
*/
|
|
251
|
+
void dispatch(const EventT& event, const any_t& data) {
|
|
252
|
+
m_tg->submit([=] {
|
|
253
|
+
auto iter = m_trees.find(event);
|
|
254
|
+
HKU_WARN_IF_RETURN(iter == m_trees.end(), void(),
|
|
255
|
+
"There is no matching handling method for the event({})!", event);
|
|
256
|
+
iter->second->run(data);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private:
|
|
261
|
+
mutable std::mutex m_mutex;
|
|
262
|
+
std::unordered_map<EventT, FilterNodePtr> m_trees;
|
|
263
|
+
std::unique_ptr<ThreadPool> m_tg;
|
|
264
|
+
bool m_quit_wait = true;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
} // namespace hku
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* LRUCache11 - a templated C++11 based LRU cache class that allows
|
|
3
|
+
* specification of
|
|
4
|
+
* key, value and optionally the map container type (defaults to
|
|
5
|
+
* std::unordered_map)
|
|
6
|
+
* By using the std::unordered_map and a linked list of keys it allows O(1) insert, delete
|
|
7
|
+
* and
|
|
8
|
+
* refresh operations.
|
|
9
|
+
*
|
|
10
|
+
* This is a header-only library and all you need is the LRUCache11.hpp file
|
|
11
|
+
*
|
|
12
|
+
* Github: https://github.com/mohaps/lrucache11
|
|
13
|
+
*
|
|
14
|
+
* This is a follow-up to the LRUCache project -
|
|
15
|
+
* https://github.com/mohaps/lrucache
|
|
16
|
+
*
|
|
17
|
+
* Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com>
|
|
18
|
+
*
|
|
19
|
+
* Permission to use, copy, modify, and distribute this software for any
|
|
20
|
+
* purpose with or without fee is hereby granted, provided that the above
|
|
21
|
+
* copyright notice and this permission notice appear in all copies.
|
|
22
|
+
*
|
|
23
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
24
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
25
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
26
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
27
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
28
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
29
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
30
|
+
*/
|
|
31
|
+
#pragma once
|
|
32
|
+
#include <algorithm>
|
|
33
|
+
#include <cstdint>
|
|
34
|
+
#include <list>
|
|
35
|
+
#include <mutex>
|
|
36
|
+
#include <stdexcept>
|
|
37
|
+
#include <thread>
|
|
38
|
+
#include <unordered_map>
|
|
39
|
+
|
|
40
|
+
namespace lru11 {
|
|
41
|
+
/*
|
|
42
|
+
* a noop lockable concept that can be used in place of std::mutex
|
|
43
|
+
*/
|
|
44
|
+
class NullLock {
|
|
45
|
+
public:
|
|
46
|
+
void lock() {}
|
|
47
|
+
void unlock() {}
|
|
48
|
+
bool try_lock() {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* error raised when a key not in cache is passed to get()
|
|
55
|
+
*/
|
|
56
|
+
class KeyNotFound : public std::invalid_argument {
|
|
57
|
+
public:
|
|
58
|
+
KeyNotFound() : std::invalid_argument("key_not_found") {}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
template <typename K, typename V>
|
|
62
|
+
struct KeyValuePair {
|
|
63
|
+
public:
|
|
64
|
+
K key;
|
|
65
|
+
V value;
|
|
66
|
+
|
|
67
|
+
KeyValuePair(K k, V v) : key(std::move(k)), value(std::move(v)) {}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The LRU Cache class templated by
|
|
72
|
+
* Key - key type
|
|
73
|
+
* Value - value type
|
|
74
|
+
* MapType - an associative container like std::unordered_map
|
|
75
|
+
* LockType - a lock type derived from the Lock class (default:
|
|
76
|
+
*NullLock = no synchronization)
|
|
77
|
+
*
|
|
78
|
+
* The default NullLock based template is not thread-safe, however passing
|
|
79
|
+
*Lock=std::mutex will make it
|
|
80
|
+
* thread-safe
|
|
81
|
+
*/
|
|
82
|
+
template <class Key, class Value, class Lock = NullLock,
|
|
83
|
+
class Map =
|
|
84
|
+
std::unordered_map<Key, typename std::list<KeyValuePair<Key, Value>>::iterator>>
|
|
85
|
+
class Cache {
|
|
86
|
+
public:
|
|
87
|
+
typedef KeyValuePair<Key, Value> node_type;
|
|
88
|
+
typedef std::list<KeyValuePair<Key, Value>> list_type;
|
|
89
|
+
typedef Map map_type;
|
|
90
|
+
typedef Lock lock_type;
|
|
91
|
+
using Guard = std::lock_guard<lock_type>;
|
|
92
|
+
/**
|
|
93
|
+
* the maxSize is the soft limit of keys and (maxSize + elasticity) is the
|
|
94
|
+
* hard limit
|
|
95
|
+
* the cache is allowed to grow till (maxSize + elasticity) and is pruned back
|
|
96
|
+
* to maxSize keys
|
|
97
|
+
* set maxSize = 0 for an unbounded cache (but in that case, you're better off
|
|
98
|
+
* using a std::unordered_map
|
|
99
|
+
* directly anyway! :)
|
|
100
|
+
*/
|
|
101
|
+
explicit Cache(size_t maxSize = 64, size_t elasticity = 10)
|
|
102
|
+
: maxSize_(maxSize), elasticity_(elasticity) {}
|
|
103
|
+
virtual ~Cache() = default;
|
|
104
|
+
size_t size() const {
|
|
105
|
+
Guard g(lock_);
|
|
106
|
+
return cache_.size();
|
|
107
|
+
}
|
|
108
|
+
bool empty() const {
|
|
109
|
+
Guard g(lock_);
|
|
110
|
+
return cache_.empty();
|
|
111
|
+
}
|
|
112
|
+
void clear() {
|
|
113
|
+
Guard g(lock_);
|
|
114
|
+
cache_.clear();
|
|
115
|
+
keys_.clear();
|
|
116
|
+
}
|
|
117
|
+
void insert(const Key& k, const Value& v) {
|
|
118
|
+
Guard g(lock_);
|
|
119
|
+
const auto iter = cache_.find(k);
|
|
120
|
+
if (iter != cache_.end()) {
|
|
121
|
+
iter->second->value = v;
|
|
122
|
+
keys_.splice(keys_.begin(), keys_, iter->second);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
keys_.emplace_front(k, v);
|
|
127
|
+
cache_[k] = keys_.begin();
|
|
128
|
+
prune();
|
|
129
|
+
}
|
|
130
|
+
void insert(const Key& k, Value&& v) {
|
|
131
|
+
Guard g(lock_);
|
|
132
|
+
const auto iter = cache_.find(k);
|
|
133
|
+
if (iter != cache_.end()) {
|
|
134
|
+
iter->second->value = std::move(v);
|
|
135
|
+
keys_.splice(keys_.begin(), keys_, iter->second);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
keys_.emplace_front(k, std::move(v));
|
|
140
|
+
cache_[k] = keys_.begin();
|
|
141
|
+
prune();
|
|
142
|
+
}
|
|
143
|
+
bool tryGet(const Key& kIn, Value& vOut) {
|
|
144
|
+
Guard g(lock_);
|
|
145
|
+
const auto iter = cache_.find(kIn);
|
|
146
|
+
if (iter == cache_.end()) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
keys_.splice(keys_.begin(), keys_, iter->second);
|
|
150
|
+
vOut = iter->second->value;
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The const reference returned here is only
|
|
155
|
+
* guaranteed to be valid till the next insert/delete
|
|
156
|
+
* 修改为非常量引用,以便修改。但请注意这是危险操作!
|
|
157
|
+
*/
|
|
158
|
+
Value& get(const Key& k) {
|
|
159
|
+
Guard g(lock_);
|
|
160
|
+
const auto iter = cache_.find(k);
|
|
161
|
+
if (iter == cache_.end()) {
|
|
162
|
+
throw KeyNotFound();
|
|
163
|
+
}
|
|
164
|
+
keys_.splice(keys_.begin(), keys_, iter->second);
|
|
165
|
+
return iter->second->value;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* returns a copy of the stored object (if found)
|
|
169
|
+
*/
|
|
170
|
+
Value getCopy(const Key& k) {
|
|
171
|
+
return get(k);
|
|
172
|
+
}
|
|
173
|
+
bool remove(const Key& k) {
|
|
174
|
+
Guard g(lock_);
|
|
175
|
+
auto iter = cache_.find(k);
|
|
176
|
+
if (iter == cache_.end()) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
keys_.erase(iter->second);
|
|
180
|
+
cache_.erase(iter);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
bool contains(const Key& k) const {
|
|
184
|
+
Guard g(lock_);
|
|
185
|
+
return cache_.find(k) != cache_.end();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
size_t getMaxSize() const {
|
|
189
|
+
return maxSize_;
|
|
190
|
+
}
|
|
191
|
+
size_t getElasticity() const {
|
|
192
|
+
return elasticity_;
|
|
193
|
+
}
|
|
194
|
+
size_t getMaxAllowedSize() const {
|
|
195
|
+
return maxSize_ + elasticity_;
|
|
196
|
+
}
|
|
197
|
+
template <typename F>
|
|
198
|
+
void cwalk(F& f) const {
|
|
199
|
+
Guard g(lock_);
|
|
200
|
+
std::for_each(keys_.begin(), keys_.end(), f);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
protected:
|
|
204
|
+
size_t prune() {
|
|
205
|
+
size_t maxAllowed = maxSize_ + elasticity_;
|
|
206
|
+
if (maxSize_ == 0 || cache_.size() < maxAllowed) {
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
size_t count = 0;
|
|
210
|
+
while (cache_.size() > maxSize_) {
|
|
211
|
+
cache_.erase(keys_.back().key);
|
|
212
|
+
keys_.pop_back();
|
|
213
|
+
++count;
|
|
214
|
+
}
|
|
215
|
+
return count;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private:
|
|
219
|
+
// Disallow copying.
|
|
220
|
+
Cache(const Cache&) = delete;
|
|
221
|
+
Cache& operator=(const Cache&) = delete;
|
|
222
|
+
|
|
223
|
+
mutable Lock lock_;
|
|
224
|
+
Map cache_;
|
|
225
|
+
list_type keys_;
|
|
226
|
+
size_t maxSize_;
|
|
227
|
+
size_t elasticity_;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
} // namespace lru11
|