first commit
This commit is contained in:
commit
dbb49afc6a
|
@ -0,0 +1,67 @@
|
||||||
|
#ifndef _BUFFERS_HPP_
|
||||||
|
#define _BUFFERS_HPP_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "objpool.hpp"
|
||||||
|
|
||||||
|
template<uint32_t BUFSIZE>
|
||||||
|
struct MemBlock { unsigned char mem[BUFSIZE]; };
|
||||||
|
|
||||||
|
class StaticBuffer {
|
||||||
|
public:
|
||||||
|
StaticBuffer(unsigned char* buf, uint32_t sz, std::function<void()> cb) : buffer(buf), size(sz), release_cb(cb) {};
|
||||||
|
StaticBuffer(const StaticBuffer& sb) = delete;
|
||||||
|
StaticBuffer(StaticBuffer&& sb) {
|
||||||
|
buffer = sb.buffer;
|
||||||
|
sb.buffer = nullptr;
|
||||||
|
size = sb.size;
|
||||||
|
sb.size = 0;
|
||||||
|
release_cb = std::move(sb.release_cb);
|
||||||
|
}
|
||||||
|
~StaticBuffer() {
|
||||||
|
if(release_cb)
|
||||||
|
release_cb();
|
||||||
|
}
|
||||||
|
unsigned char* Data() { return buffer; }
|
||||||
|
uint32_t Capacity() { return size; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned char* buffer = nullptr;
|
||||||
|
uint32_t size = 0;
|
||||||
|
std::function<void()> release_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferMgr {
|
||||||
|
public:
|
||||||
|
StaticBuffer AllocStaticBuffer(uint32_t size) {
|
||||||
|
if(size <= 128) {
|
||||||
|
auto memblk = pool1.Alloc();
|
||||||
|
return StaticBuffer(memblk->mem, 128, [this, memblk]() {
|
||||||
|
pool1.Recycle(memblk);
|
||||||
|
});
|
||||||
|
} else if(size <= 1024) {
|
||||||
|
auto memblk = pool2.Alloc();
|
||||||
|
return StaticBuffer(memblk->mem, 1024, [this, memblk]() {
|
||||||
|
pool2.Recycle(memblk);
|
||||||
|
});
|
||||||
|
} else if(size <= 4096) {
|
||||||
|
auto memblk = pool3.Alloc();
|
||||||
|
return StaticBuffer(memblk->mem, 4096, [this, memblk]() {
|
||||||
|
pool3.Recycle(memblk);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
auto memblk = new unsigned char[size];
|
||||||
|
return StaticBuffer(memblk, size, [memblk]() {
|
||||||
|
delete[] memblk;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ObjectPoolNoLock<MemBlock<128>> pool1;
|
||||||
|
ObjectPoolNoLock<MemBlock<1024>> pool2;
|
||||||
|
ObjectPoolNoLock<MemBlock<4096>> pool3;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,201 @@
|
||||||
|
#ifndef _HPROSE_CLI_HPP_
|
||||||
|
#define _HPROSE_CLI_HPP_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "hprose_ed.hpp"
|
||||||
|
#include "hprose_types.hpp"
|
||||||
|
|
||||||
|
template<typename... RETS>
|
||||||
|
struct HproseRPCCallback {
|
||||||
|
template<typename T>
|
||||||
|
void CallImpl(HproseReader& reader, T rcb, const std::function<void()>& arg_ref) {
|
||||||
|
auto vals = HproseValueHelperTuple<RETS...>().FromReader(reader);
|
||||||
|
arg_ref();
|
||||||
|
CallImplMultiArg(rcb, vals, std::make_index_sequence<sizeof...(RETS)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t... I>
|
||||||
|
void CallImplMultiArg(T rcb, std::tuple<RETS...>& vals, std::index_sequence<I...>) {
|
||||||
|
rcb(std::get<I>(vals)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename RET>
|
||||||
|
struct HproseRPCCallback<RET> {
|
||||||
|
template<typename T>
|
||||||
|
void CallImpl(HproseReader& reader, T rcb, const std::function<void()>& arg_ref) {
|
||||||
|
auto val = HproseValueHelper<RET>().FromReader(reader);
|
||||||
|
arg_ref();
|
||||||
|
rcb(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct HproseRPCCallback<void> {
|
||||||
|
template<typename T>
|
||||||
|
void CallImpl(HproseReader& reader, T rcb, const std::function<void()>& arg_ref) {
|
||||||
|
reader.SkipValue();
|
||||||
|
arg_ref();
|
||||||
|
rcb();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct HproseResultArg {
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename RET>
|
||||||
|
struct HproseResultArg<std::function<RET()>> {
|
||||||
|
using type = HproseRPCCallback<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename RET, typename... ARGS>
|
||||||
|
struct HproseResultArg<std::function<RET(ARGS...)>> {
|
||||||
|
using type = HproseRPCCallback<std::remove_cv_t<std::remove_reference_t<ARGS>>...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HproseRPCInfo {
|
||||||
|
public:
|
||||||
|
HproseRPCInfo(const std::string& name) : method(name) {}
|
||||||
|
|
||||||
|
template<typename... ARGS>
|
||||||
|
HproseRPCInfo& PushArgs(ARGS... args) {
|
||||||
|
arg_cb = [args = std::make_tuple(args...)](HproseWriter& writer) {
|
||||||
|
HproseValueHelper<std::tuple<ARGS...>>().ToWriter(args, writer);
|
||||||
|
};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... ARGS>
|
||||||
|
HproseRPCInfo& PushRefArgs(ARGS&... args) {
|
||||||
|
ref_cb = [argrefs = std::tie(std::forward<ARGS&>(args)...)](HproseReader& reader) mutable {
|
||||||
|
argrefs = std::move(HproseValueHelperTuple<ARGS...>().FromReader(reader));
|
||||||
|
};
|
||||||
|
ref = true;
|
||||||
|
return PushArgs(std::forward<ARGS&>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
HproseRPCInfo& Expect(T rcb) {
|
||||||
|
res_cb = [rcb](HproseReader& reader, const std::function<void()>& arg_ref) {
|
||||||
|
using rpc_arg_type = HproseResultArg<typename FunctionType<T>::type>;
|
||||||
|
typename rpc_arg_type::type().CallImpl(reader, rcb, arg_ref);
|
||||||
|
};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
HproseRPCInfo& Catch(std::function<void(const std::string&)> ecb) {
|
||||||
|
err_cb = ecb;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteRequest(HproseWriter& writer) const {
|
||||||
|
writer.WriteTag(Tags::Call);
|
||||||
|
HproseValueHelperString().ToWriter(method, writer);
|
||||||
|
if(arg_cb)
|
||||||
|
arg_cb(writer);
|
||||||
|
if(ref)
|
||||||
|
writer.WriteTag(Tags::True);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Return(HproseReader& reader) {
|
||||||
|
auto tag = reader.ReadTag();
|
||||||
|
if(tag == Tags::Error) {
|
||||||
|
auto msg = HproseValueHelperString().FromReader(reader);
|
||||||
|
if(err_cb)
|
||||||
|
err_cb(msg);
|
||||||
|
} else {
|
||||||
|
auto cb = [this, &reader]() {
|
||||||
|
if(ref && ref_cb && reader.CheckTag(Tags::Argument, true))
|
||||||
|
ref_cb(reader);
|
||||||
|
};
|
||||||
|
if(res_cb)
|
||||||
|
res_cb(reader, cb);
|
||||||
|
else {
|
||||||
|
reader.SkipValue();
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Error(const std::string& msg) { if(err_cb) err_cb(msg); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool ref = false;
|
||||||
|
std::string method;
|
||||||
|
std::function<void(HproseReader&, std::function<void()>)> res_cb;
|
||||||
|
std::function<void(HproseReader&)> ref_cb;
|
||||||
|
std::function<void(HproseWriter&)> arg_cb;
|
||||||
|
std::function<void(const std::string&)> err_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HproseClient {
|
||||||
|
public:
|
||||||
|
template<typename SVC>
|
||||||
|
void UseConnector(SVC& svc) {
|
||||||
|
apply_handler = [&svc, this](uint32_t id, std::shared_ptr<std::vector<HproseRPCInfo>> rpcs) {
|
||||||
|
if(rpcs == nullptr) {
|
||||||
|
auto& rpcs = results[id];
|
||||||
|
svc.SendRequest(id, rpcs);
|
||||||
|
} else
|
||||||
|
svc.SendRequestNoReturn(id, rpcs);
|
||||||
|
};
|
||||||
|
svc.SetPacketHandler([this](uint32_t id, HproseReader& reader) {
|
||||||
|
auto iter = results.find(id);
|
||||||
|
if(iter != results.end()) {
|
||||||
|
for(auto& inf : iter->second)
|
||||||
|
inf.Return(reader);
|
||||||
|
}
|
||||||
|
results.erase(iter);
|
||||||
|
});
|
||||||
|
svc.SetErrorHandler([this](uint32_t id, const std::string& msg) {
|
||||||
|
auto& infos = results[id];
|
||||||
|
for(auto& inf : infos)
|
||||||
|
inf.Error(msg);
|
||||||
|
results.erase(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
HproseRPCInfo& Call(const std::string& name) {
|
||||||
|
auto pre_multiid = multiid;
|
||||||
|
multiid = 0;
|
||||||
|
auto& ret = MultiCall(name);
|
||||||
|
MultiApply();
|
||||||
|
multiid = pre_multiid;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
HproseRPCInfo& MultiCall(const std::string& name) {
|
||||||
|
if(multiid == 0)
|
||||||
|
multiid = callid++;
|
||||||
|
auto& infos = results[multiid];
|
||||||
|
infos.emplace_back(name);
|
||||||
|
return infos.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiApply() {
|
||||||
|
if(multiid == 0)
|
||||||
|
return;
|
||||||
|
apply_handler(multiid, nullptr);
|
||||||
|
multiid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HproseRPCInfo& CallNoReturn(const std::string& name) {
|
||||||
|
auto rpcinfos = std::make_shared<std::vector<HproseRPCInfo>>();
|
||||||
|
rpcinfos->emplace_back(name);
|
||||||
|
apply_handler(callid++, rpcinfos);
|
||||||
|
return rpcinfos->at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t callid = 1;
|
||||||
|
uint32_t multiid = 0;
|
||||||
|
std::function<void(uint32_t, std::shared_ptr<std::vector<HproseRPCInfo>>)> apply_handler;
|
||||||
|
std::unordered_map<uint32_t, std::vector<HproseRPCInfo>> results;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,267 @@
|
||||||
|
#ifndef _HPROSE_SVC_HPP_
|
||||||
|
#define _HPROSE_SVC_HPP_
|
||||||
|
|
||||||
|
#include "hprose_srv.hpp"
|
||||||
|
#include "hprose_cli.hpp"
|
||||||
|
#include "buffers.hpp"
|
||||||
|
#include "tcp.hpp"
|
||||||
|
#include "udp.hpp"
|
||||||
|
#include "worker.hpp"
|
||||||
|
|
||||||
|
class HproseDataHandler {
|
||||||
|
public:
|
||||||
|
using data_handler_cb = std::function<void(uint32_t dup_id, const unsigned char* data, size_t datalen)>;
|
||||||
|
|
||||||
|
const unsigned char* HandleData(const unsigned char* data, size_t datalen) {
|
||||||
|
auto begin = data;
|
||||||
|
auto left = datalen;
|
||||||
|
while(left >= 4) {
|
||||||
|
uint32_t length = ((uint32_t)begin[0] << 24) | ((uint32_t)begin[1] << 16) | ((uint32_t)begin[2] << 8) | ((uint32_t)begin[3]);
|
||||||
|
bool duplex = (length & 0x80000000) != 0;
|
||||||
|
length &= 0x7fffffff;
|
||||||
|
uint32_t offset = duplex ? 8 : 4;
|
||||||
|
if(left < offset + length)
|
||||||
|
return begin;
|
||||||
|
uint32_t dup_id = duplex ? *(uint32_t*)&begin[4] : 0;
|
||||||
|
if(data_handler)
|
||||||
|
data_handler(dup_id, &begin[offset], length);
|
||||||
|
begin += offset + length;
|
||||||
|
left -= offset + length;
|
||||||
|
}
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteHeader(uint32_t dup_id, unsigned char* begin, size_t length) {
|
||||||
|
if(dup_id) {
|
||||||
|
length |= 0x80000000;
|
||||||
|
*(uint32_t*)&begin[4] = dup_id;
|
||||||
|
}
|
||||||
|
begin[0] = (unsigned char)((length >> 24) & 0xff);
|
||||||
|
begin[1] = (unsigned char)((length >> 16) & 0xff);
|
||||||
|
begin[2] = (unsigned char)((length >> 8) & 0xff);
|
||||||
|
begin[3] = (unsigned char)(length & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDataHandler(const data_handler_cb& cb) { data_handler = cb; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
data_handler_cb data_handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HproseSessionTCP : public HproseDataHandler, public TCPSession<HproseSessionTCP> {
|
||||||
|
public:
|
||||||
|
using TCPSession<HproseSessionTCP>::TCPSession;
|
||||||
|
|
||||||
|
void PushData(const unsigned char* data, size_t length) {
|
||||||
|
if(!pending_data.empty()) {
|
||||||
|
pending_data.insert(pending_data.end(), data, data + length);
|
||||||
|
auto end = HandleData(pending_data.data(), pending_data.size());
|
||||||
|
auto left = pending_data.data() + pending_data.size() - end;
|
||||||
|
if(left == 0) {
|
||||||
|
pending_data.clear();
|
||||||
|
} else {
|
||||||
|
memmove(pending_data.data(), end, left);
|
||||||
|
pending_data.resize(left);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto end = HandleData(data, length);
|
||||||
|
auto left = data + length - end;
|
||||||
|
if(left != 0)
|
||||||
|
pending_data.assign(end, end + left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendPacket(uint32_t dup_id, StaticBuffer& buf, size_t length) {
|
||||||
|
WriteHeader(dup_id, buf.Data(), length);
|
||||||
|
this->SendData(std::move(buf), (length & 0x7fffffff) + (dup_id ? 8 : 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<unsigned char> pending_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename OBJECT>
|
||||||
|
class HproseServerConnectorTCP {
|
||||||
|
public:
|
||||||
|
HproseServerConnectorTCP(Worker* wk, uint16_t pt) : worker(wk), port(pt) {}
|
||||||
|
void Start() {
|
||||||
|
auto srv = std::make_shared<TCPServer<HproseSessionTCP>>(*worker, port);
|
||||||
|
srv->SetConnectedCallback([this](tcp::socket& sock) -> std::shared_ptr<HproseSessionTCP> {
|
||||||
|
tcp::socket handler_socket(handler_workers[worker_index], tcp::v4(), sock.release());
|
||||||
|
auto sess = std::make_shared<HproseSessionTCP>(std::move(handler_socket));
|
||||||
|
sess->SetReceivedCallback([](HproseSessionTCP* sess, const unsigned char* data, size_t length) {
|
||||||
|
sess->PushData(data, length);
|
||||||
|
}).SetDataHandler([this, idx = worker_index, sess, object = obj_alloc()](uint32_t dup_id, const unsigned char* data, size_t datalen) {
|
||||||
|
static const int32_t buflen = 4096;
|
||||||
|
auto reader = HproseReader(data, datalen);
|
||||||
|
auto buf = buff_mgr[idx].AllocStaticBuffer(buflen);
|
||||||
|
auto begin = buf.Data();
|
||||||
|
HproseWriter writer(begin + (dup_id ? 8 : 4), buflen - (dup_id ? 8 : 4));
|
||||||
|
handler(object.get(), reader, writer);
|
||||||
|
writer.WriteTag(Tags::End);
|
||||||
|
sess->SendPacket(dup_id, buf, writer.Length());
|
||||||
|
});
|
||||||
|
worker_index = (worker_index + 1) % 4;
|
||||||
|
return sess;
|
||||||
|
}).Start();
|
||||||
|
for(uint32_t i = 0; i < 4; ++i) {
|
||||||
|
std::thread([this, i](){
|
||||||
|
auto work = asio::make_work_guard(handler_workers[i]);
|
||||||
|
handler_workers[i].run();
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetObjectAllocator(const std::function<std::shared_ptr<OBJECT>()>& alloc) { obj_alloc = alloc; }
|
||||||
|
void SetPacketHandler(const std::function<void(OBJECT*, HproseReader&, HproseWriter&)>& cb) { handler = cb; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Worker* worker;
|
||||||
|
uint16_t port;
|
||||||
|
uint32_t worker_index = 0;
|
||||||
|
TimerdWorker handler_workers[4];
|
||||||
|
BufferMgr buff_mgr[4];
|
||||||
|
std::function<std::shared_ptr<OBJECT>()> obj_alloc;
|
||||||
|
std::function<void(OBJECT*, HproseReader&, HproseWriter&)> handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HproseClientConnectorTCP {
|
||||||
|
public:
|
||||||
|
HproseClientConnectorTCP(Worker* wk, BufferMgr* mgr, const std::string& addr, uint16_t pt) : worker(wk), buff_mgr(mgr), address(addr), port(pt) {}
|
||||||
|
void SendRequest(uint32_t id, std::vector<HproseRPCInfo>& rpcs) {
|
||||||
|
auto srv = std::make_shared<TCPClient<HproseSessionTCP>>(*worker);
|
||||||
|
srv->SetConnectedCallback([this, &rpcs, id](tcp::socket& sock) -> std::shared_ptr<HproseSessionTCP> {
|
||||||
|
auto sess = std::make_shared<HproseSessionTCP>(std::move(sock));
|
||||||
|
sess->SetReceivedCallback([](HproseSessionTCP* sess, const unsigned char* data, size_t length) {
|
||||||
|
sess->PushData(data, length);
|
||||||
|
}).SetDataHandler([this, sess](uint32_t dup_id, const unsigned char* data, size_t datalen) {
|
||||||
|
auto reader = HproseReader(data, datalen);
|
||||||
|
if(handler)
|
||||||
|
handler(dup_id, reader);
|
||||||
|
sess->Close();
|
||||||
|
});
|
||||||
|
DoSendRequests(sess.get(), rpcs, id);
|
||||||
|
return sess;
|
||||||
|
}).Connect(address.c_str(), port, 30, [this, id](const std::string& msg) {
|
||||||
|
if(ehandler)
|
||||||
|
ehandler(id, msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendRequestNoReturn(uint32_t id, std::shared_ptr<std::vector<HproseRPCInfo>> rpcs) {
|
||||||
|
auto srv = std::make_shared<TCPClient<HproseSessionTCP>>(*worker);
|
||||||
|
srv->SetConnectedCallback([this, rpcs, id](tcp::socket& sock) -> std::shared_ptr<HproseSessionTCP> {
|
||||||
|
auto sess = std::make_shared<HproseSessionTCP>(std::move(sock));
|
||||||
|
DoSendRequests(sess.get(), *rpcs, id);
|
||||||
|
sess->Close();
|
||||||
|
return sess;
|
||||||
|
}).Connect(address.c_str(), port, 30, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoSendRequests(HproseSessionTCP* sess, const std::vector<HproseRPCInfo>& infos, uint32_t id) {
|
||||||
|
static const int32_t buflen = 4096;
|
||||||
|
auto buf = buff_mgr->AllocStaticBuffer(buflen);
|
||||||
|
auto begin = buf.Data();
|
||||||
|
HproseWriter writer(begin + (id ? 8 : 4), buflen - (id ? 8 : 4));
|
||||||
|
for(auto& inf : infos)
|
||||||
|
inf.WriteRequest(writer);
|
||||||
|
writer.WriteTag(Tags::End);
|
||||||
|
sess->SendPacket(id, buf, writer.Length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPacketHandler(const std::function<void(uint32_t, HproseReader&)>& cb) { handler = cb; }
|
||||||
|
void SetErrorHandler(const std::function<void(uint32_t, const std::string&)>& cb) { ehandler = cb; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Worker* worker;
|
||||||
|
BufferMgr* buff_mgr;
|
||||||
|
std::string address;
|
||||||
|
uint16_t port;
|
||||||
|
std::function<void(uint32_t, HproseReader&)> handler;
|
||||||
|
std::function<void(uint32_t, const std::string&)> ehandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename OBJECT>
|
||||||
|
class HproseServerConnectorUDP : public HproseDataHandler {
|
||||||
|
public:
|
||||||
|
HproseServerConnectorUDP(Worker* wk, BufferMgr* mgr, uint16_t pt) : worker(wk), buff_mgr(mgr), port(pt) {}
|
||||||
|
void Start() {
|
||||||
|
auto srv = std::make_shared<UDPPeer>(*worker, port);
|
||||||
|
srv->SetReceivedCallback([this](const udp_receiver& r, const udp_sender& s, size_t available) {
|
||||||
|
auto rbuf = buff_mgr->AllocStaticBuffer(available);
|
||||||
|
auto recv_len = r(rbuf, available);
|
||||||
|
SetDataHandler([this, &s, obj = obj_alloc()](uint32_t dup_id, const unsigned char* data, size_t datalen) {
|
||||||
|
auto reader = HproseReader(data, datalen);
|
||||||
|
auto buf = buff_mgr->AllocStaticBuffer(4096);
|
||||||
|
auto begin = buf.Data();
|
||||||
|
HproseWriter writer(begin + (dup_id ? 8 : 4), 4096 - (dup_id ? 8 : 4));
|
||||||
|
handler(obj.get(), reader, writer);
|
||||||
|
writer.WriteTag(Tags::End);
|
||||||
|
WriteHeader(dup_id, buf.Data(), writer.Length());
|
||||||
|
s(buf, writer.Length() + (dup_id ? 8 : 4));
|
||||||
|
});
|
||||||
|
HandleData(rbuf.Data(), recv_len);
|
||||||
|
}).StartReceive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetObjectAllocator(const std::function<std::shared_ptr<OBJECT>()>& alloc) { obj_alloc = alloc; }
|
||||||
|
void SetPacketHandler(const std::function<void(OBJECT*, HproseReader&, HproseWriter&)>& cb) { handler = cb; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Worker* worker;
|
||||||
|
BufferMgr* buff_mgr;
|
||||||
|
uint16_t port;
|
||||||
|
std::function<std::shared_ptr<OBJECT>()> obj_alloc;
|
||||||
|
std::function<void(OBJECT*, HproseReader&, HproseWriter&)> handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HproseClientConnectorUDP : public HproseDataHandler {
|
||||||
|
public:
|
||||||
|
HproseClientConnectorUDP(Worker* wk, BufferMgr* mgr, const std::string& addr, uint16_t pt) : worker(wk), buff_mgr(mgr), address(addr), port(pt) {}
|
||||||
|
void SendRequest(uint32_t id, std::vector<HproseRPCInfo>& rpcs) {
|
||||||
|
auto peer = std::make_shared<UDPPeer>(*worker);
|
||||||
|
DoSendRequests(peer.get(), rpcs, id);
|
||||||
|
peer->SetReceivedCallback([this, p = peer.get()](const udp_receiver& r, const udp_sender& s, size_t available) {
|
||||||
|
auto rbuf = buff_mgr->AllocStaticBuffer(available);
|
||||||
|
auto recv_len = r(rbuf, available);
|
||||||
|
SetDataHandler([this](uint32_t dup_id, const unsigned char* data, size_t datalen) {
|
||||||
|
auto reader = HproseReader(data, datalen);
|
||||||
|
if(handler)
|
||||||
|
handler(dup_id, reader);
|
||||||
|
});
|
||||||
|
HandleData(rbuf.Data(), recv_len);
|
||||||
|
p->StopReceive();
|
||||||
|
});
|
||||||
|
peer->StartReceive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendRequestNoReturn(uint32_t id, std::shared_ptr<std::vector<HproseRPCInfo>> rpcs) {
|
||||||
|
auto peer = std::make_shared<UDPPeer>(*worker);
|
||||||
|
DoSendRequests(peer.get(), *rpcs, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoSendRequests(UDPPeer* peer, const std::vector<HproseRPCInfo>& infos, uint32_t id) {
|
||||||
|
static const int32_t buflen = 4096;
|
||||||
|
auto buf = buff_mgr->AllocStaticBuffer(buflen);
|
||||||
|
auto begin = buf.Data();
|
||||||
|
HproseWriter writer(begin + (id ? 8 : 4), buflen - (id ? 8 : 4));
|
||||||
|
for(auto& inf : infos)
|
||||||
|
inf.WriteRequest(writer);
|
||||||
|
writer.WriteTag(Tags::End);
|
||||||
|
WriteHeader(id, buf.Data(), writer.Length());
|
||||||
|
peer->SendTo(address.c_str(), port, buf, writer.Length() + (id ? 8 : 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPacketHandler(const std::function<void(uint32_t, HproseReader&)>& cb) { handler = cb; }
|
||||||
|
void SetErrorHandler(const std::function<void(uint32_t, const std::string&)>& cb) { ehandler = cb; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Worker* worker;
|
||||||
|
BufferMgr* buff_mgr;
|
||||||
|
std::string address;
|
||||||
|
uint16_t port;
|
||||||
|
std::function<void(uint32_t, HproseReader&)> handler;
|
||||||
|
std::function<void(uint32_t, const std::string&)> ehandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,271 @@
|
||||||
|
#ifndef _HPROSE_ED_HPP_
|
||||||
|
#define _HPROSE_ED_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "hprose_tags.hpp"
|
||||||
|
|
||||||
|
class HproseReader {
|
||||||
|
public:
|
||||||
|
HproseReader(const unsigned char* ptr, size_t len) : begin(ptr), end(ptr + len), current(ptr) {}
|
||||||
|
HproseReader(const unsigned char* ptr, const unsigned char* end) : begin(ptr), end(end), current(ptr) {}
|
||||||
|
|
||||||
|
unsigned char ReadByte() {
|
||||||
|
if(current >= end)
|
||||||
|
return 0;
|
||||||
|
return *current++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tags ReadTag() {
|
||||||
|
return static_cast<Tags>(ReadByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckTag(Tags tag, bool skip) {
|
||||||
|
if(current >= end)
|
||||||
|
return false;
|
||||||
|
if(static_cast<Tags>(*current) != tag)
|
||||||
|
return false;
|
||||||
|
if(skip)
|
||||||
|
current++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skip(uint32_t len) {
|
||||||
|
current += len;
|
||||||
|
if(current >= end)
|
||||||
|
current = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadUntilAsString(Tags tag) {
|
||||||
|
auto start = current;
|
||||||
|
while(current != end && *current != static_cast<unsigned char>(tag))
|
||||||
|
current++;
|
||||||
|
if(current == end) {
|
||||||
|
return std::string(start, current);
|
||||||
|
} else {
|
||||||
|
return std::string(start, current++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadBytesAsString(size_t length) {
|
||||||
|
auto start = current;
|
||||||
|
current += length;
|
||||||
|
if(current > end)
|
||||||
|
current = end;
|
||||||
|
return std::string(start, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadUTF8String(size_t length) {
|
||||||
|
auto start = current;
|
||||||
|
for(size_t i = 0; i < length && current < end; ++i) {
|
||||||
|
if(*current < 0x80) { // 0xxxxxxx
|
||||||
|
current++;
|
||||||
|
} else if(*current < 0xd0) { // 110xxxxx 10xxxxxx
|
||||||
|
if(current + 2 > end)
|
||||||
|
break;
|
||||||
|
current += 2;
|
||||||
|
} else if(*current < 0xf0) { // 1110xxxx 10xxxxxx 10xxxxxx
|
||||||
|
if(current + 3 > end)
|
||||||
|
break;
|
||||||
|
current += 3;
|
||||||
|
} else { // 11110xxx 10xxxxx 10xxxxxx 10xxxxxx
|
||||||
|
if(current + 4 > end)
|
||||||
|
break;
|
||||||
|
current += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto ptr = current;
|
||||||
|
CheckTag(Tags::Quote, true);
|
||||||
|
return std::string(start, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ReadInt64Raw(Tags end) {
|
||||||
|
int64_t value = 0;
|
||||||
|
auto b = ReadByte();
|
||||||
|
if(static_cast<Tags>(b) == end)
|
||||||
|
return value;
|
||||||
|
bool neg = false;
|
||||||
|
if(static_cast<Tags>(b) == Tags::Neg) {
|
||||||
|
neg = true;
|
||||||
|
b = ReadByte();
|
||||||
|
} else if(static_cast<Tags>(b) == Tags::Pos) {
|
||||||
|
b = ReadByte();
|
||||||
|
}
|
||||||
|
while(static_cast<Tags>(b) != end) {
|
||||||
|
value = value * 10 + int64_t(b - '0');
|
||||||
|
b = ReadByte();
|
||||||
|
}
|
||||||
|
return neg ? -value : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkipValue() {
|
||||||
|
SkipValue(ReadTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkipValue(Tags tag) {
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::Call:
|
||||||
|
case Tags::Result:
|
||||||
|
case Tags::Error:
|
||||||
|
case Tags::End:
|
||||||
|
case Tags::Argument: {
|
||||||
|
current--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Null:
|
||||||
|
case Tags::Empty:
|
||||||
|
case Tags::Num0:
|
||||||
|
case Tags::Num1:
|
||||||
|
case Tags::Num2:
|
||||||
|
case Tags::Num3:
|
||||||
|
case Tags::Num4:
|
||||||
|
case Tags::Num5:
|
||||||
|
case Tags::Num6:
|
||||||
|
case Tags::Num7:
|
||||||
|
case Tags::Num8:
|
||||||
|
case Tags::Num9:
|
||||||
|
case Tags::False:
|
||||||
|
case Tags::True:
|
||||||
|
case Tags::NaN: return;
|
||||||
|
case Tags::Infinity: {
|
||||||
|
ReadTag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Integer:
|
||||||
|
case Tags::Long: {
|
||||||
|
ReadInt64Raw(Tags::Semicolon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Double: {
|
||||||
|
ReadUntilAsString(Tags::Semicolon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::UTF8Char: {
|
||||||
|
ReadUTF8String(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::String: {
|
||||||
|
ReadUTF8String(ReadInt64Raw(Tags::Quote));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Bytes: {
|
||||||
|
ReadBytesAsString(ReadInt64Raw(Tags::Quote));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::List: {
|
||||||
|
int64_t count = ReadInt64Raw(Tags::Openbrace);
|
||||||
|
for(int64_t i = 0; i < count; ++i)
|
||||||
|
SkipValue();
|
||||||
|
ReadTag(); // Tags::Closebrace
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Map: {
|
||||||
|
int64_t count = ReadInt64Raw(Tags::Openbrace) * 2;
|
||||||
|
for(int64_t i = 0; i < count; ++i)
|
||||||
|
SkipValue();
|
||||||
|
ReadTag(); // Tags::Closebrace
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Date: {
|
||||||
|
Skip(8); // year-4 m-2 d-2
|
||||||
|
if(!CheckTag(Tags::Time, true))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Time: {
|
||||||
|
Skip(6); // h-2 m-2 s-2
|
||||||
|
if(CheckTag(Tags::Point, true)) {
|
||||||
|
// nano seconds 1-9digits
|
||||||
|
while(current < end && *current >= '0' && *current <= '9')
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
CheckTag(Tags::UTC, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::GUID: {
|
||||||
|
Skip(38); // '"' + 36 bytes string + '"'
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Class: {
|
||||||
|
SkipValue(Tags::String); // name
|
||||||
|
SkipValue(Tags::List); // list
|
||||||
|
if(!CheckTag(Tags::Object, true))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Tags::Object: {
|
||||||
|
SkipValue(); // index
|
||||||
|
if(CheckTag(Tags::Openbrace, true)) {
|
||||||
|
do {
|
||||||
|
SkipValue();
|
||||||
|
} while(!CheckTag(Tags::Closebrace, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushFieldRefs(std::vector<std::string> fields) { fieldRefs.emplace_back(std::move(fields)); }
|
||||||
|
const std::vector<std::string>& GetFieldRefs(int32_t idx) { return fieldRefs[idx]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const unsigned char* begin;
|
||||||
|
const unsigned char* end;
|
||||||
|
const unsigned char* current;
|
||||||
|
std::vector<std::vector<std::string>> fieldRefs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HproseWriter {
|
||||||
|
public:
|
||||||
|
HproseWriter(unsigned char* ptr, size_t len) : begin(ptr), end(ptr + len), current(ptr) {}
|
||||||
|
void WriteByte(unsigned char byte) {
|
||||||
|
*current++ = byte;
|
||||||
|
}
|
||||||
|
void WriteBytes(unsigned char* bytes, size_t len) {
|
||||||
|
memcpy(current, bytes, len);
|
||||||
|
current += len;
|
||||||
|
}
|
||||||
|
void WriteTag(Tags tag) {
|
||||||
|
*current++ = static_cast<unsigned char>(tag);
|
||||||
|
}
|
||||||
|
void WriteLength(uint32_t length) {
|
||||||
|
auto len = sprintf((char*)current, "%u", length);
|
||||||
|
current += len;
|
||||||
|
}
|
||||||
|
void WriteInteger(int64_t val) {
|
||||||
|
auto len = sprintf((char*)current, "%ld", val);
|
||||||
|
current += len;
|
||||||
|
}
|
||||||
|
void WriteDouble(double val) {
|
||||||
|
auto len = sprintf((char*)current, "%lf", val);
|
||||||
|
current += len;
|
||||||
|
}
|
||||||
|
void WriteString(const std::string& val) {
|
||||||
|
memcpy(current, val.data(), val.length());
|
||||||
|
current += val.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Length() { return current - begin; }
|
||||||
|
|
||||||
|
int32_t GetStructFieldsIndex(const std::string& name) {
|
||||||
|
auto iter = fieldIds.find(name);
|
||||||
|
if(iter == fieldIds.end())
|
||||||
|
return -1;
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t PushFieldRefs(const std::string& name) {
|
||||||
|
int32_t index = fieldIds.size();
|
||||||
|
fieldIds[name] = index;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const unsigned char* begin;
|
||||||
|
const unsigned char* end;
|
||||||
|
unsigned char* current;
|
||||||
|
std::map<std::string, int32_t> fieldIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,168 @@
|
||||||
|
#ifndef _HPROSE_SRV_HPP_
|
||||||
|
#define _HPROSE_SRV_HPP_
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "hprose_ed.hpp"
|
||||||
|
#include "hprose_types.hpp"
|
||||||
|
|
||||||
|
template<typename RET>
|
||||||
|
struct Invoker {
|
||||||
|
template <typename OBJECT, typename HANDLER, typename... ARGS>
|
||||||
|
bool operator() (OBJECT* objs, HANDLER&& handler, HproseWriter& w, ARGS&... args) const {
|
||||||
|
return HproseValueHelper<RET>().ToWriterWithResultTag(handler(std::forward<ARGS&>(args)...), w);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Invoker<void> {
|
||||||
|
template <typename OBJECT, typename HANDLER, typename... ARGS>
|
||||||
|
bool operator() (OBJECT* objs, HANDLER&& handler, HproseWriter& w, ARGS&... args) const {
|
||||||
|
handler(std::forward<ARGS&>(args)...);
|
||||||
|
return HproseValueHelperNull().ToWriterWithResultTag(w);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename RET>
|
||||||
|
struct InvokerObj {
|
||||||
|
template <typename OBJECT, typename HANDLER, typename... ARGS>
|
||||||
|
bool operator() (OBJECT* objs, HANDLER&& handler, HproseWriter& w, ARGS&... args) const {
|
||||||
|
return HproseValueHelper<RET>().ToWriterWithResultTag((*objs.*handler)(std::forward<ARGS&>(args)...), w);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct InvokerObj<void> {
|
||||||
|
template <typename OBJECT, typename HANDLER, typename... ARGS>
|
||||||
|
bool operator() (OBJECT* objs, HANDLER&& handler, HproseWriter& w, ARGS&... args) const {
|
||||||
|
(*objs.*handler)(std::forward<ARGS&>(args)...);
|
||||||
|
return HproseValueHelperNull().ToWriterWithResultTag(w);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct HandlerType {
|
||||||
|
using class_type = void;
|
||||||
|
using arg_helper = std::tuple<>;
|
||||||
|
using arg_type = std::tuple<>;
|
||||||
|
using invoke_type = void;
|
||||||
|
static constexpr int32_t arg_count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename RET, typename... ARGS>
|
||||||
|
struct HandlerType<std::function<RET(ARGS...)>> {
|
||||||
|
using class_type = void;
|
||||||
|
using arg_type = std::tuple<std::remove_const_t<std::remove_reference_t<ARGS>>...>;
|
||||||
|
using invoke_type = Invoker<RET>;
|
||||||
|
using call_type = std::function<RET(ARGS...)>;
|
||||||
|
static constexpr int32_t arg_count = sizeof...(ARGS);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename RET, typename CLASS, typename... ARGS>
|
||||||
|
struct HandlerType<RET(CLASS::*)(ARGS...)> {
|
||||||
|
using class_type = CLASS;
|
||||||
|
using arg_type = std::tuple<std::remove_const_t<std::remove_reference_t<ARGS>>...>;
|
||||||
|
using invoke_type = InvokerObj<RET>;
|
||||||
|
using call_type = RET(CLASS::*)(ARGS...);
|
||||||
|
static constexpr int32_t arg_count = sizeof...(ARGS);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename RET, typename CLASS, typename... ARGS>
|
||||||
|
struct HandlerType<RET(CLASS::*)(ARGS...) const> {
|
||||||
|
using class_type = CLASS;
|
||||||
|
using arg_type = std::tuple<std::remove_const_t<std::remove_reference_t<ARGS>>...>;
|
||||||
|
using invoke_type = InvokerObj<RET>;
|
||||||
|
using call_type = RET(CLASS::*)(ARGS...) const;
|
||||||
|
static constexpr int32_t arg_count = sizeof...(ARGS);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename OBJECT>
|
||||||
|
struct HproseInvoker {
|
||||||
|
virtual void Call(OBJECT* obj, HproseReader& r, HproseWriter& w) = 0;
|
||||||
|
virtual ~HproseInvoker() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename HANDLER>
|
||||||
|
class HProseCallType : public HproseInvoker<typename HandlerType<typename FunctionType<HANDLER>::type>::class_type> {
|
||||||
|
public:
|
||||||
|
using handler_type = HandlerType<typename FunctionType<HANDLER>::type>;
|
||||||
|
using args_type = typename handler_type::arg_type;
|
||||||
|
|
||||||
|
HProseCallType(HANDLER h, const std::string& name) : handler(h), method(name) {}
|
||||||
|
|
||||||
|
virtual void Call(typename handler_type::class_type* obj, HproseReader& r, HproseWriter& w) {
|
||||||
|
args_type args;
|
||||||
|
if(r.CheckTag(Tags::List, false))
|
||||||
|
args = HproseValueHelper<args_type>().FromReader(r);
|
||||||
|
bool ref = r.CheckTag(Tags::True, true);
|
||||||
|
bool has_error = CallImpl(obj, args, w, std::make_index_sequence<handler_type::arg_count>{});
|
||||||
|
if(ref && handler_type::arg_count > 0 && !has_error) {
|
||||||
|
w.WriteTag(Tags::Argument);
|
||||||
|
HproseValueHelper<args_type>().ToWriter(args, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t... I>
|
||||||
|
bool CallImpl(typename handler_type::class_type* obj, args_type& args, HproseWriter& w, std::index_sequence<I...>) {
|
||||||
|
return typename handler_type::invoke_type()(obj, handler, w, std::get<I>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HANDLER handler;
|
||||||
|
std::string method;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
std::unique_ptr<HProseCallType<F>> MakeHproseCallType(F f, const std::string& name) {
|
||||||
|
return std::unique_ptr<HProseCallType<F>>(new HProseCallType<F>(f, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename OBJECT>
|
||||||
|
class HproseServer {
|
||||||
|
public:
|
||||||
|
template<typename HANDLER>
|
||||||
|
void Register(const char* name, HANDLER func) {
|
||||||
|
auto invoker = std::unique_ptr<HproseInvoker<OBJECT>>(MakeHproseCallType(func, name));
|
||||||
|
rpc_calls[name] = std::move(invoker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleRequests(OBJECT* sess, HproseReader& reader, HproseWriter& writer) {
|
||||||
|
auto tag = reader.ReadTag();
|
||||||
|
while(tag == Tags::Call) {
|
||||||
|
auto method = HproseValueHelperString().FromReader(reader);
|
||||||
|
auto iter = rpc_calls.find(method);
|
||||||
|
if(iter == rpc_calls.end()) {
|
||||||
|
HproseError err(std::move(std::string("Error: \"").append(method).append("\" not found.")));
|
||||||
|
HproseValueHelperError().ToWriterWithResultTag(err, writer);
|
||||||
|
reader.SkipValue();
|
||||||
|
reader.SkipValue();
|
||||||
|
tag = reader.ReadTag();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
iter->second->Call(sess, reader, writer);
|
||||||
|
tag = reader.ReadTag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename SVC>
|
||||||
|
void UseConnector(SVC& svc) {
|
||||||
|
svc.SetObjectAllocator([this]() -> std::shared_ptr<OBJECT> {
|
||||||
|
if(obj_allocator)
|
||||||
|
return obj_allocator();
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
svc.SetPacketHandler([this](OBJECT* obj, HproseReader& reader, HproseWriter& writer) {
|
||||||
|
this->HandleRequests(obj, reader, writer);
|
||||||
|
});
|
||||||
|
svc.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetObjectAllocator(const std::function<std::shared_ptr<OBJECT>()>& cb) { obj_allocator = cb; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::function<std::shared_ptr<OBJECT>()> obj_allocator;
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<HproseInvoker<OBJECT>>> rpc_calls;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef _HPROSE_TAGS_HPP_
|
||||||
|
#define _HPROSE_TAGS_HPP_
|
||||||
|
|
||||||
|
enum class Tags : unsigned char {
|
||||||
|
TagError = 0,
|
||||||
|
// Serialize Type
|
||||||
|
Integer = 'i',
|
||||||
|
Long = 'l',
|
||||||
|
Double = 'd',
|
||||||
|
Null = 'n',
|
||||||
|
Empty = 'e',
|
||||||
|
True = 't',
|
||||||
|
False = 'f',
|
||||||
|
NaN = 'N',
|
||||||
|
Infinity = 'I',
|
||||||
|
Date = 'D',
|
||||||
|
Time = 'T',
|
||||||
|
UTC = 'Z',
|
||||||
|
Bytes = 'b',
|
||||||
|
UTF8Char = 'u',
|
||||||
|
String = 's',
|
||||||
|
GUID = 'g',
|
||||||
|
List = 'a',
|
||||||
|
Map = 'm',
|
||||||
|
Class = 'c',
|
||||||
|
Object = 'o',
|
||||||
|
Ref = 'r',
|
||||||
|
// Serialize Marks
|
||||||
|
Pos = '+',
|
||||||
|
Neg = '-',
|
||||||
|
Semicolon = ';',
|
||||||
|
Openbrace = '{',
|
||||||
|
Closebrace = '}',
|
||||||
|
Quote = '"',
|
||||||
|
Point = '.',
|
||||||
|
// Protocol s
|
||||||
|
Functions = 'F',
|
||||||
|
Call = 'C',
|
||||||
|
Result = 'R',
|
||||||
|
Argument = 'A',
|
||||||
|
Error = 'E',
|
||||||
|
End = 'z',
|
||||||
|
// Numbers
|
||||||
|
Num0 = '0',
|
||||||
|
Num1 = '1',
|
||||||
|
Num2 = '2',
|
||||||
|
Num3 = '3',
|
||||||
|
Num4 = '4',
|
||||||
|
Num5 = '5',
|
||||||
|
Num6 = '6',
|
||||||
|
Num7 = '7',
|
||||||
|
Num8 = '8',
|
||||||
|
Num9 = '9',
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,696 @@
|
||||||
|
#ifndef _HPROSE_TYPES_HPP_
|
||||||
|
#define _HPROSE_TYPES_HPP_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
#include <cmath>
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include "hprose_ed.hpp"
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct StdFuncType { using type = void; };
|
||||||
|
template<typename RET, typename CLASS, typename... ARGS>
|
||||||
|
struct StdFuncType<RET(CLASS::*)(ARGS...) const> { using type = std::function<RET(ARGS...)>; };
|
||||||
|
template<typename RET, typename CLASS, typename... ARGS>
|
||||||
|
struct StdFuncType<RET(CLASS::*)(ARGS...)> { using type = std::function<RET(ARGS...)>; };
|
||||||
|
|
||||||
|
template<typename HANDLER>
|
||||||
|
struct FunctionType { using type = typename StdFuncType<decltype(&HANDLER::operator())>::type; };
|
||||||
|
template<typename RET, typename... ARGS>
|
||||||
|
struct FunctionType<RET(*)(ARGS...)> { using type = std::function<RET(ARGS...)>; };
|
||||||
|
template<typename RET, typename CLASS, typename... ARGS>
|
||||||
|
struct FunctionType<RET(CLASS::*)(ARGS...)> { using type = RET(CLASS::*)(ARGS...); };
|
||||||
|
template<typename RET, typename CLASS, typename... ARGS>
|
||||||
|
struct FunctionType<RET(CLASS::*)(ARGS...) const> { using type = RET(CLASS::*)(ARGS...) const; };
|
||||||
|
|
||||||
|
template<int32_t INDEX, typename TYPE, typename FIRST, typename... REST>
|
||||||
|
struct IndexOfTypeHelper {
|
||||||
|
static constexpr int32_t value = std::conditional_t<std::is_same<TYPE, FIRST>::value, std::integral_constant<int32_t, INDEX>, IndexOfTypeHelper<INDEX + 1, TYPE, REST...>>::value;
|
||||||
|
};
|
||||||
|
template<int32_t INDEX, typename TYPE, typename ARG>
|
||||||
|
struct IndexOfTypeHelper<INDEX, TYPE, ARG> {
|
||||||
|
static constexpr int32_t value = std::conditional_t<std::is_same<TYPE, ARG>::value, std::integral_constant<int32_t, INDEX>, std::integral_constant<int32_t, -1>>::value;
|
||||||
|
};
|
||||||
|
template<typename T, typename... ARGS>
|
||||||
|
struct IndexOfType { static constexpr int32_t value = IndexOfTypeHelper<0, T, ARGS...>::value; };
|
||||||
|
template<typename T>
|
||||||
|
struct IndexOfType<T> { static constexpr int32_t value = -1; };
|
||||||
|
|
||||||
|
class HproseError {
|
||||||
|
public:
|
||||||
|
HproseError() : is_null(true) {}
|
||||||
|
HproseError(const std::string& msg) : is_null(false), errmsg(msg) {}
|
||||||
|
HproseError(std::string&& msg) : is_null(false), errmsg(std::move(msg)) {}
|
||||||
|
bool IsNull() const { return is_null; }
|
||||||
|
const std::string& Message() const { return errmsg; }
|
||||||
|
operator bool () { return !is_null; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool is_null;
|
||||||
|
std::string errmsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HproseValueHelperError {
|
||||||
|
HproseError FromReader(HproseReader& reader) {
|
||||||
|
reader.CheckTag(Tags::Null, true);
|
||||||
|
return HproseError();
|
||||||
|
}
|
||||||
|
bool ToWriterWithResultTag(const HproseError& err, HproseWriter& writer) {
|
||||||
|
if(err.IsNull())
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
else
|
||||||
|
writer.WriteTag(Tags::Error);
|
||||||
|
ToWriter(err, writer);
|
||||||
|
return !err.IsNull();
|
||||||
|
}
|
||||||
|
void ToWriter(const HproseError& err, HproseWriter& writer) {
|
||||||
|
if(err.IsNull())
|
||||||
|
writer.WriteTag(Tags::Null);
|
||||||
|
else {
|
||||||
|
const std::string& errmsg = err.Message();
|
||||||
|
writer.WriteTag(Tags::String);
|
||||||
|
writer.WriteLength(errmsg.length());
|
||||||
|
writer.WriteTag(Tags::Quote);
|
||||||
|
writer.WriteString(errmsg);
|
||||||
|
writer.WriteTag(Tags::Quote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HproseValueHelperNull {
|
||||||
|
bool ToWriterWithResultTag(HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
writer.WriteTag(Tags::Null);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ToWriter(HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HproseValueHelperInteger {
|
||||||
|
int64_t FromReader(HproseReader& r) {
|
||||||
|
auto tag = r.ReadTag();
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::Null: return 0;
|
||||||
|
case Tags::Empty: return 0;
|
||||||
|
case Tags::Num0: return 0;
|
||||||
|
case Tags::Num1: return 1;
|
||||||
|
case Tags::Num2: return 2;
|
||||||
|
case Tags::Num3: return 3;
|
||||||
|
case Tags::Num4: return 4;
|
||||||
|
case Tags::Num5: return 5;
|
||||||
|
case Tags::Num6: return 6;
|
||||||
|
case Tags::Num7: return 7;
|
||||||
|
case Tags::Num8: return 8;
|
||||||
|
case Tags::Num9: return 9;
|
||||||
|
case Tags::False: return 0;
|
||||||
|
case Tags::True: return 1;
|
||||||
|
case Tags::NaN: return 0;
|
||||||
|
case Tags::Infinity: {
|
||||||
|
r.ReadTag();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case Tags::Integer:
|
||||||
|
case Tags::Long: { // golang use big.int to decode Long
|
||||||
|
return r.ReadInt64Raw(Tags::Semicolon);
|
||||||
|
}
|
||||||
|
case Tags::Double: {
|
||||||
|
return int64_t(strtod(r.ReadUntilAsString(Tags::Semicolon).data(), nullptr));
|
||||||
|
}
|
||||||
|
case Tags::UTF8Char: {
|
||||||
|
return strtoll(r.ReadUTF8String(1).data(), nullptr, 10);
|
||||||
|
}
|
||||||
|
case Tags::String: {
|
||||||
|
return strtoll(r.ReadUTF8String(r.ReadInt64Raw(Tags::Quote)).data(), nullptr, 10);
|
||||||
|
}
|
||||||
|
case Tags::Bytes: {
|
||||||
|
return strtoll(r.ReadBytesAsString(r.ReadInt64Raw(Tags::Quote)).data(), nullptr, 10);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
r.SkipValue(tag);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ToWriterWithResultTag(int64_t value, HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
ToWriter(value, writer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ToWriter(int64_t value, HproseWriter& writer) {
|
||||||
|
if(value >= 0 && value <= 9) {
|
||||||
|
writer.WriteByte(value + '0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(value >= -0xffffffff && value <= 0xffffffff)
|
||||||
|
writer.WriteTag(Tags::Integer);
|
||||||
|
else
|
||||||
|
writer.WriteTag(Tags::Long);
|
||||||
|
writer.WriteInteger(value);
|
||||||
|
writer.WriteTag(Tags::Semicolon);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HproseValueHelperBool {
|
||||||
|
bool FromReader(HproseReader& r) {
|
||||||
|
auto tag = r.ReadTag();
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::Null:
|
||||||
|
case Tags::Empty:
|
||||||
|
case Tags::Num0: return false;
|
||||||
|
case Tags::Num1:
|
||||||
|
case Tags::Num2:
|
||||||
|
case Tags::Num3:
|
||||||
|
case Tags::Num4:
|
||||||
|
case Tags::Num5:
|
||||||
|
case Tags::Num6:
|
||||||
|
case Tags::Num7:
|
||||||
|
case Tags::Num8:
|
||||||
|
case Tags::Num9: return true;
|
||||||
|
case Tags::False: return false;
|
||||||
|
case Tags::True: return true;
|
||||||
|
case Tags::NaN: return false;
|
||||||
|
case Tags::Infinity: {
|
||||||
|
r.ReadTag();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Tags::Integer:
|
||||||
|
case Tags::Long: {
|
||||||
|
return r.ReadInt64Raw(Tags::Semicolon) != 0;
|
||||||
|
}
|
||||||
|
case Tags::Double: {
|
||||||
|
return strtod(r.ReadUntilAsString(Tags::Semicolon).data(), nullptr) != 0.0;
|
||||||
|
}
|
||||||
|
case Tags::UTF8Char: {
|
||||||
|
return r.ReadUTF8String(1) != "0";
|
||||||
|
}
|
||||||
|
case Tags::String: {
|
||||||
|
return CheckString(r.ReadUTF8String(r.ReadInt64Raw(Tags::Quote)));
|
||||||
|
}
|
||||||
|
case Tags::Bytes: {
|
||||||
|
return CheckString(r.ReadBytesAsString(r.ReadInt64Raw(Tags::Quote)));
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
r.SkipValue(tag);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool CheckString(const std::string& str) {
|
||||||
|
return str != "";
|
||||||
|
}
|
||||||
|
bool ToWriterWithResultTag(bool value, HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
ToWriter(value, writer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ToWriter(bool value, HproseWriter& writer) {
|
||||||
|
writer.WriteTag(value ? Tags::True : Tags::False);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HproseValueHelperDouble {
|
||||||
|
double FromReader(HproseReader& r) {
|
||||||
|
auto tag = r.ReadTag();
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::Null: return 0.0;
|
||||||
|
case Tags::Empty: return 0.0;
|
||||||
|
case Tags::Num0: return 0.0;
|
||||||
|
case Tags::Num1: return 1.0;
|
||||||
|
case Tags::Num2: return 2.0;
|
||||||
|
case Tags::Num3: return 3.0;
|
||||||
|
case Tags::Num4: return 4.0;
|
||||||
|
case Tags::Num5: return 5.0;
|
||||||
|
case Tags::Num6: return 6.0;
|
||||||
|
case Tags::Num7: return 7.0;
|
||||||
|
case Tags::Num8: return 8.0;
|
||||||
|
case Tags::Num9: return 9.0;
|
||||||
|
case Tags::False: return 0.0;
|
||||||
|
case Tags::True: return 1.0;
|
||||||
|
case Tags::NaN: return std::numeric_limits<double>::quiet_NaN();
|
||||||
|
case Tags::Infinity: {
|
||||||
|
auto t = r.ReadTag();
|
||||||
|
return (t == Tags::Pos) ? std::numeric_limits<double>::infinity() : -std::numeric_limits<double>::infinity();
|
||||||
|
}
|
||||||
|
case Tags::Integer:
|
||||||
|
case Tags::Long: {
|
||||||
|
return double(r.ReadInt64Raw(Tags::Semicolon));
|
||||||
|
}
|
||||||
|
case Tags::Double: {
|
||||||
|
return strtod(r.ReadUntilAsString(Tags::Semicolon).data(), nullptr);
|
||||||
|
}
|
||||||
|
case Tags::UTF8Char: {
|
||||||
|
return strtod(r.ReadUTF8String(1).data(), nullptr);
|
||||||
|
}
|
||||||
|
case Tags::String: {
|
||||||
|
return strtod(r.ReadUTF8String(r.ReadInt64Raw(Tags::Quote)).data(), nullptr);
|
||||||
|
}
|
||||||
|
case Tags::Bytes: {
|
||||||
|
return strtod(r.ReadBytesAsString(r.ReadInt64Raw(Tags::Quote)).data(), nullptr);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
r.SkipValue(tag);
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ToWriterWithResultTag(double value, HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
ToWriter(value, writer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ToWriter(double value, HproseWriter& writer) {
|
||||||
|
if(std::isnan(value)) {
|
||||||
|
writer.WriteTag(Tags::NaN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(std::isinf(value)) {
|
||||||
|
writer.WriteTag(Tags::Infinity);
|
||||||
|
writer.WriteTag(value > 0 ? Tags::Pos : Tags::Neg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writer.WriteTag(Tags::Double);
|
||||||
|
writer.WriteDouble(value);
|
||||||
|
writer.WriteTag(Tags::Semicolon);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HproseValueHelperString {
|
||||||
|
std::string FromReader(HproseReader& r) {
|
||||||
|
auto tag = r.ReadTag();
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::Null: return "";
|
||||||
|
case Tags::Empty: return "";
|
||||||
|
case Tags::Num0: return "0";
|
||||||
|
case Tags::Num1: return "1";
|
||||||
|
case Tags::Num2: return "2";
|
||||||
|
case Tags::Num3: return "3";
|
||||||
|
case Tags::Num4: return "4";
|
||||||
|
case Tags::Num5: return "5";
|
||||||
|
case Tags::Num6: return "6";
|
||||||
|
case Tags::Num7: return "7";
|
||||||
|
case Tags::Num8: return "8";
|
||||||
|
case Tags::Num9: return "9";
|
||||||
|
case Tags::False: return "false";
|
||||||
|
case Tags::True: return "true";
|
||||||
|
case Tags::NaN: return "nan";
|
||||||
|
case Tags::Infinity: {
|
||||||
|
auto t = r.ReadTag();
|
||||||
|
return (t == Tags::Pos) ? "+inf" : "-inf";
|
||||||
|
}
|
||||||
|
case Tags::Integer:
|
||||||
|
case Tags::Long:
|
||||||
|
case Tags::Double: {
|
||||||
|
return r.ReadUntilAsString(Tags::Semicolon);
|
||||||
|
}
|
||||||
|
case Tags::UTF8Char: {
|
||||||
|
return r.ReadUTF8String(1);
|
||||||
|
}
|
||||||
|
case Tags::String: {
|
||||||
|
return r.ReadUTF8String(r.ReadInt64Raw(Tags::Quote));
|
||||||
|
}
|
||||||
|
case Tags::Bytes: {
|
||||||
|
return r.ReadBytesAsString(r.ReadInt64Raw(Tags::Quote));
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
r.SkipValue(tag);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ToWriterWithResultTag(const std::string& value, HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
ToWriter(value, writer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ToWriter(const std::string& value, HproseWriter& writer) {
|
||||||
|
if(value.empty()) {
|
||||||
|
writer.WriteTag(Tags::Empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(value.length() == 1) {
|
||||||
|
writer.WriteTag(Tags::UTF8Char);
|
||||||
|
writer.WriteByte(value[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writer.WriteTag(Tags::String);
|
||||||
|
writer.WriteLength(value.length());
|
||||||
|
writer.WriteTag(Tags::Quote);
|
||||||
|
writer.WriteString(value);
|
||||||
|
writer.WriteTag(Tags::Quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FromReaderRaw(HproseReader& r) {
|
||||||
|
return r.ReadUTF8String(r.ReadInt64Raw(Tags::Quote));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToWriterRaw(const std::string& value, HproseWriter& writer, bool with_tag) {
|
||||||
|
if(with_tag)
|
||||||
|
writer.WriteTag(Tags::String);
|
||||||
|
writer.WriteLength(value.length());
|
||||||
|
writer.WriteTag(Tags::Quote);
|
||||||
|
writer.WriteString(value);
|
||||||
|
writer.WriteTag(Tags::Quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct HproseValueHelperStruct {
|
||||||
|
T FromReader(HproseReader& reader) {
|
||||||
|
T val;
|
||||||
|
auto tag = reader.ReadTag();
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::Class: {
|
||||||
|
HproseValueHelperString().FromReaderRaw(reader);
|
||||||
|
int32_t count = reader.ReadInt64Raw(Tags::Openbrace);
|
||||||
|
std::vector<std::string> fields;
|
||||||
|
for(int32_t i = 0; i < count; ++i)
|
||||||
|
fields.emplace_back(HproseValueHelperString().FromReader(reader));
|
||||||
|
reader.PushFieldRefs(std::move(fields));
|
||||||
|
reader.CheckTag(Tags::Closebrace, true);
|
||||||
|
reader.CheckTag(Tags::Object, true);
|
||||||
|
}
|
||||||
|
case Tags::Object: {
|
||||||
|
int32_t index = reader.ReadInt64Raw(Tags::Openbrace);
|
||||||
|
auto& fields = reader.GetFieldRefs(index);
|
||||||
|
auto& refmap = val.GetFieldMaps();
|
||||||
|
for(size_t i = 0; i < fields.size(); ++i)
|
||||||
|
refmap.ReadField((uintptr_t)&val, fields[i], reader);
|
||||||
|
reader.CheckTag(Tags::Closebrace, true);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
reader.SkipValue(tag);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ToWriterWithResultTag(const T& val, HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
val.GetFieldMaps().ToWriter((uintptr_t)&val, writer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ToWriter(const T& val, HproseWriter& writer) {
|
||||||
|
val.GetFieldMaps().ToWriter((uintptr_t)&val, writer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> struct HproseValueHelperImpl { using type = HproseValueHelperStruct<T>; };
|
||||||
|
template<> struct HproseValueHelperImpl<int> { using type = HproseValueHelperInteger; };
|
||||||
|
template<> struct HproseValueHelperImpl<HproseError> { using type = HproseValueHelperError; };
|
||||||
|
template<> struct HproseValueHelperImpl<bool> { using type = HproseValueHelperBool; };
|
||||||
|
template<> struct HproseValueHelperImpl<float> { using type = HproseValueHelperDouble; };
|
||||||
|
template<> struct HproseValueHelperImpl<double> { using type = HproseValueHelperDouble; };
|
||||||
|
template<> struct HproseValueHelperImpl<char*> { using type = HproseValueHelperString; };
|
||||||
|
template<> struct HproseValueHelperImpl<const char*> { using type = HproseValueHelperString; };
|
||||||
|
template<> struct HproseValueHelperImpl<std::string> { using type = HproseValueHelperString; };
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using HproseValueHelper = typename HproseValueHelperImpl<std::remove_cv_t<std::remove_reference_t<T>>>::type;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct HproseValueHelperSlice {
|
||||||
|
std::vector<T> FromReader(HproseReader& reader) {
|
||||||
|
std::vector<T> ret;
|
||||||
|
auto tag = reader.ReadTag();
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::List: {
|
||||||
|
HproseValueHelper<T> vh;
|
||||||
|
int64_t count = reader.ReadInt64Raw(Tags::Openbrace);
|
||||||
|
for(int64_t i = 0; i < count; ++i)
|
||||||
|
ret.push_back(vh.FromReader(reader));
|
||||||
|
reader.CheckTag(Tags::Closebrace, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
reader.SkipValue(tag);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ToWriterWithResultTag(const std::vector<T>& value, HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
ToWriter(value, writer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ToWriter(const std::vector<T>& values, HproseWriter& writer) {
|
||||||
|
HproseValueHelper<T> vh;
|
||||||
|
writer.WriteTag(Tags::List);
|
||||||
|
writer.WriteLength(values.size());
|
||||||
|
writer.WriteTag(Tags::Openbrace);
|
||||||
|
for(auto& v : values)
|
||||||
|
vh.ToWriter(v, writer);
|
||||||
|
writer.WriteTag(Tags::Closebrace);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename K, typename V, typename MAP>
|
||||||
|
struct HproseValueHelperMap {
|
||||||
|
MAP FromReader(HproseReader& reader) {
|
||||||
|
MAP ret;
|
||||||
|
auto tag = reader.ReadTag();
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::Map: {
|
||||||
|
HproseValueHelper<K> kvh;
|
||||||
|
HproseValueHelper<V> vvh;
|
||||||
|
int64_t count = reader.ReadInt64Raw(Tags::Openbrace);
|
||||||
|
for(int64_t i = 0; i < count; ++i) {
|
||||||
|
auto k = kvh.FromReader(reader);
|
||||||
|
auto v = vvh.FromReader(reader);
|
||||||
|
ret[k] = v;
|
||||||
|
}
|
||||||
|
reader.CheckTag(Tags::Closebrace, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
reader.SkipValue(tag);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ToWriterWithResultTag(const MAP& value, HproseWriter& writer) {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
ToWriter(value, writer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void ToWriter(const MAP& value, HproseWriter& writer) {
|
||||||
|
HproseValueHelper<K> kvh;
|
||||||
|
HproseValueHelper<V> vvh;
|
||||||
|
writer.WriteTag(Tags::Map);
|
||||||
|
writer.WriteLength(value.size());
|
||||||
|
writer.WriteTag(Tags::Openbrace);
|
||||||
|
for(auto& iter : value) {
|
||||||
|
kvh.ToWriter(iter.first, writer);
|
||||||
|
vvh.ToWriter(iter.second, writer);
|
||||||
|
}
|
||||||
|
writer.WriteTag(Tags::Closebrace);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<int32_t INDEX>
|
||||||
|
struct ErrorChecker {
|
||||||
|
template<typename T>
|
||||||
|
bool IsError(T& val) {
|
||||||
|
return !std::get<INDEX>(val).IsNull();
|
||||||
|
}
|
||||||
|
template<typename T>
|
||||||
|
void WriteError(T& val, HproseWriter& writer) {
|
||||||
|
HproseValueHelperError().ToWriterWithResultTag(std::get<INDEX>(val), writer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct ErrorChecker<-1> {
|
||||||
|
template<typename T>
|
||||||
|
bool IsError(T& val) { return false; }
|
||||||
|
template<typename T>
|
||||||
|
void WriteError(T& val, HproseWriter& writer) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ... ARGS>
|
||||||
|
struct HproseValueHelperTuple {
|
||||||
|
using helper_type = std::tuple<HproseValueHelper<ARGS>...>;
|
||||||
|
|
||||||
|
std::tuple<ARGS...> FromReader(HproseReader& reader) {
|
||||||
|
std::tuple<ARGS...> ret;
|
||||||
|
auto tag = reader.ReadTag();
|
||||||
|
switch(tag) {
|
||||||
|
case Tags::List: {
|
||||||
|
helper_type vhs;
|
||||||
|
int64_t count = reader.ReadInt64Raw(Tags::Openbrace);
|
||||||
|
FromReaderImpl(ret, vhs, reader, count, std::make_index_sequence<sizeof...(ARGS)>{});
|
||||||
|
for(int32_t i = sizeof...(ARGS); i < count; ++i)
|
||||||
|
reader.SkipValue();
|
||||||
|
reader.CheckTag(Tags::Closebrace, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
reader.SkipValue(tag);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void FromReaderImpl(std::tuple<ARGS...>& value, helper_type& vhs, HproseReader& reader, int64_t count, std::index_sequence<>) {}
|
||||||
|
|
||||||
|
template<size_t I, size_t... R>
|
||||||
|
void FromReaderImpl(std::tuple<ARGS...>& value, helper_type& vhs, HproseReader& reader, int64_t count, std::index_sequence<I, R...>) {
|
||||||
|
if(I < (size_t)count) {
|
||||||
|
std::get<I>(value) = std::get<I>(vhs).FromReader(reader);
|
||||||
|
FromReaderImpl(value, vhs, reader, count, std::index_sequence<R...>{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToWriterWithResultTag(const std::tuple<ARGS...>& value, HproseWriter& writer) {
|
||||||
|
ErrorChecker<IndexOfType<HproseError, ARGS...>::value> checker;
|
||||||
|
bool has_error = checker.IsError(value);
|
||||||
|
if(has_error) {
|
||||||
|
checker.WriteError(value, writer);
|
||||||
|
} else {
|
||||||
|
writer.WriteTag(Tags::Result);
|
||||||
|
ToWriter(value, writer);
|
||||||
|
}
|
||||||
|
return has_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToWriter(const std::tuple<ARGS...>& value, HproseWriter& writer) {
|
||||||
|
helper_type vhs;
|
||||||
|
writer.WriteTag(Tags::List);
|
||||||
|
writer.WriteLength(sizeof...(ARGS));
|
||||||
|
writer.WriteTag(Tags::Openbrace);
|
||||||
|
ToWriterImpl(value, vhs, writer, std::make_index_sequence<sizeof...(ARGS)>{});
|
||||||
|
writer.WriteTag(Tags::Closebrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToWriterImpl(const std::tuple<ARGS...>& value, helper_type& vhs, HproseWriter& writer, std::index_sequence<>) {}
|
||||||
|
|
||||||
|
template<size_t I, size_t... R>
|
||||||
|
void ToWriterImpl(const std::tuple<ARGS...>& value, helper_type& vhs, HproseWriter& writer, std::index_sequence<I, R...>) {
|
||||||
|
std::get<I>(vhs).ToWriter(std::get<I>(value), writer);
|
||||||
|
ToWriterImpl(value, vhs, writer, std::index_sequence<R...>{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct HproseValueHelperImpl<std::vector<T>> { using type = HproseValueHelperSlice<T>; };
|
||||||
|
template<typename K, typename V>
|
||||||
|
struct HproseValueHelperImpl<std::map<K, V>> { using type = HproseValueHelperMap<K, V, std::map<K, V>>; };
|
||||||
|
template<typename K, typename V>
|
||||||
|
struct HproseValueHelperImpl<std::unordered_map<K, V>> { using type = HproseValueHelperMap<K, V, std::unordered_map<K, V>>; };
|
||||||
|
template<typename ... ARGS>
|
||||||
|
struct HproseValueHelperImpl<std::tuple<ARGS...>> { using type = HproseValueHelperTuple<ARGS...>; };
|
||||||
|
|
||||||
|
struct HproseReflector {
|
||||||
|
virtual void FromReader(uintptr_t addr, HproseReader& r) = 0;
|
||||||
|
virtual void ToWriter(uintptr_t addr, HproseWriter& w) = 0;
|
||||||
|
virtual const std::string& GetName() = 0;
|
||||||
|
virtual ~HproseReflector() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct HproseReflectType : public HproseReflector {
|
||||||
|
HproseReflectType(uintptr_t off, const std::string& n) : offset(off), name(n) {}
|
||||||
|
virtual void FromReader(uintptr_t addr, HproseReader& r) {
|
||||||
|
T* ptr = (T*)(addr + offset);
|
||||||
|
*ptr = HproseValueHelper<T>().FromReader(r);
|
||||||
|
}
|
||||||
|
virtual void ToWriter(uintptr_t addr, HproseWriter& w) {
|
||||||
|
T* ptr = (T*)(addr + offset);
|
||||||
|
HproseValueHelper<T>().ToWriter(*ptr, w);
|
||||||
|
}
|
||||||
|
virtual const std::string& GetName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
uintptr_t offset;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HproseReflectMap {
|
||||||
|
bool IsInited() { return inited; }
|
||||||
|
|
||||||
|
void Init(const std::string& name) {
|
||||||
|
struct_name = name;
|
||||||
|
inited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void RegisterField(T* t, const std::string& fname) {
|
||||||
|
auto reflector = new HproseReflectType<T>((uintptr_t)t, fname);
|
||||||
|
fields[fname] = std::unique_ptr<HproseReflector>(reflector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadField(uintptr_t struct_ptr, const std::string& name, HproseReader& reader) {
|
||||||
|
auto iter = fields.find(name);
|
||||||
|
if(iter == fields.end()) {
|
||||||
|
reader.SkipValue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
iter->second->FromReader(struct_ptr, reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToWriter(uintptr_t struct_ptr, HproseWriter& writer) const {
|
||||||
|
auto idx = writer.GetStructFieldsIndex(struct_name);
|
||||||
|
if(idx == -1) {
|
||||||
|
idx = writer.PushFieldRefs(struct_name);
|
||||||
|
WriteFields(writer);
|
||||||
|
}
|
||||||
|
writer.WriteTag(Tags::Object);
|
||||||
|
HproseValueHelperInteger().ToWriter(idx, writer);
|
||||||
|
writer.WriteTag(Tags::Openbrace);
|
||||||
|
for(auto& iter : fields)
|
||||||
|
iter.second->ToWriter(struct_ptr, writer);
|
||||||
|
writer.WriteTag(Tags::Closebrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteFields(HproseWriter& writer) const {
|
||||||
|
writer.WriteTag(Tags::Class);
|
||||||
|
HproseValueHelperString().ToWriterRaw(struct_name, writer, false);
|
||||||
|
HproseValueHelperInteger().ToWriter(fields.size(), writer);
|
||||||
|
writer.WriteTag(Tags::Openbrace);
|
||||||
|
for(auto& iter : fields)
|
||||||
|
HproseValueHelperString().ToWriterRaw(iter.first, writer, true);
|
||||||
|
writer.WriteTag(Tags::Closebrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inited = false;
|
||||||
|
std::string struct_name;
|
||||||
|
std::map<std::string, std::unique_ptr<HproseReflector>> fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FE_1(FUN, FIELD) FUN(FIELD)
|
||||||
|
#define FE_2(FUN, FIELD, ...) FUN(FIELD)FE_1(FUN, __VA_ARGS__)
|
||||||
|
#define FE_3(FUN, FIELD, ...) FUN(FIELD)FE_2(FUN, __VA_ARGS__)
|
||||||
|
#define FE_4(FUN, FIELD, ...) FUN(FIELD)FE_3(FUN, __VA_ARGS__)
|
||||||
|
#define FE_5(FUN, FIELD, ...) FUN(FIELD)FE_4(FUN, __VA_ARGS__)
|
||||||
|
#define FE_6(FUN, FIELD, ...) FUN(FIELD)FE_5(FUN, __VA_ARGS__)
|
||||||
|
#define FE_7(FUN, FIELD, ...) FUN(FIELD)FE_6(FUN, __VA_ARGS__)
|
||||||
|
#define FE_8(FUN, FIELD, ...) FUN(FIELD)FE_7(FUN, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME
|
||||||
|
#define FOR_EACH(action,...) GET_MACRO(__VA_ARGS__,FE_8,FE_7,FE_6,FE_5,FE_4,FE_3,FE_2,FE_1)(action,__VA_ARGS__)
|
||||||
|
|
||||||
|
#define PUSH_FIELD(FIELD) ref_map.RegisterField(&ptr->FIELD, #FIELD);
|
||||||
|
|
||||||
|
#define HPROSE_REFLECT(struct_type, ...) \
|
||||||
|
public:\
|
||||||
|
static HproseReflectMap& GetFieldMaps() {\
|
||||||
|
static HproseReflectMap ref_map;\
|
||||||
|
if(ref_map.IsInited())\
|
||||||
|
return ref_map;\
|
||||||
|
static std::atomic<bool> inited(false);\
|
||||||
|
bool expected = false;\
|
||||||
|
if(inited.compare_exchange_strong(expected, true)) {\
|
||||||
|
struct_type* ptr = nullptr;\
|
||||||
|
FOR_EACH(PUSH_FIELD, __VA_ARGS__)\
|
||||||
|
ref_map.Init(#struct_type);\
|
||||||
|
} else {\
|
||||||
|
while(!ref_map.IsInited());\
|
||||||
|
}\
|
||||||
|
return ref_map;\
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include "timer.hpp"
|
||||||
|
#include "tcp.hpp"
|
||||||
|
|
||||||
|
#include "hprose_types.hpp"
|
||||||
|
#include "hprose_srv.hpp"
|
||||||
|
#include "hprose_cli.hpp"
|
||||||
|
#include "hprose_conn.hpp"
|
||||||
|
#include "worker.hpp"
|
||||||
|
|
||||||
|
struct PushData {
|
||||||
|
int result;
|
||||||
|
int uid;
|
||||||
|
std::vector<int> stars;
|
||||||
|
HPROSE_REFLECT(PushData, result, uid, stars)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HandlerObject {
|
||||||
|
std::string Hello(std::string& str) {
|
||||||
|
return std::string("Hello ").append(str).append("!");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<int, int, HproseError> Test(const std::tuple<int, int>& a, int b) {
|
||||||
|
std::cout << "Test called." << std::endl;
|
||||||
|
return std::make_tuple(std::get<0>(a) * b, std::get<1>(a) * b, HproseError("Error!!!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<int, int, HproseError> Swap(int& a, int& b) {
|
||||||
|
a += 100;
|
||||||
|
b += 123;
|
||||||
|
return std::make_tuple(a, b, HproseError());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, int> Maptest(const std::unordered_map<int, int>& m) {
|
||||||
|
std::map<int, int> ret;
|
||||||
|
for(auto& iter : m) {
|
||||||
|
ret[iter.first] = iter.second + 100;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushData Pushs(PushData& data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::thread t1([]() {
|
||||||
|
TimerdWorker worker;
|
||||||
|
HproseServerConnectorTCP<HandlerObject> connector(&worker, 12345);
|
||||||
|
HproseServer<HandlerObject> srv;
|
||||||
|
auto obj = std::make_shared<HandlerObject>();
|
||||||
|
srv.SetObjectAllocator([&obj]() -> auto {
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
srv.UseConnector(connector);
|
||||||
|
srv.Register("hello", &HandlerObject::Hello);
|
||||||
|
srv.Register("test", &HandlerObject::Test);
|
||||||
|
srv.Register("swap", &HandlerObject::Swap);
|
||||||
|
srv.Register("maptest", &HandlerObject::Maptest);
|
||||||
|
srv.Register("Pushs", &HandlerObject::Pushs);
|
||||||
|
asio::signal_set signals(worker, SIGINT, SIGTERM);
|
||||||
|
signals.async_wait([&worker](const asio::error_code& error, int signal_number) {
|
||||||
|
std::cout << "Interrupted." << std::endl;
|
||||||
|
worker.stop();
|
||||||
|
});
|
||||||
|
worker.run();
|
||||||
|
});
|
||||||
|
// std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
// std::thread t2([]() {
|
||||||
|
// TimerdWorker worker;
|
||||||
|
// BufferMgr bufmgr;
|
||||||
|
// HproseClientConnectorTCP connector(&worker, &bufmgr, "127.0.0.1", 12345);
|
||||||
|
// HproseClient client;
|
||||||
|
// client.UseConnector(connector);
|
||||||
|
// client.MultiCall("hello").PushArgs("world").Expect([](std::string& ret) {
|
||||||
|
// std::cout << ret << std::endl;
|
||||||
|
// }).Catch([](const std::string& msg) {
|
||||||
|
// std::cout << msg << std::endl;
|
||||||
|
// });
|
||||||
|
// client.MultiCall("test").PushArgs(std::make_tuple(123, 678), 10).Expect([](int a, int b, HproseError err) {
|
||||||
|
// std::cout << a << " " << b << " " << err.IsNull() << std::endl;
|
||||||
|
// }).Catch([](const std::string& msg) {
|
||||||
|
// std::cout << msg << std::endl;
|
||||||
|
// });
|
||||||
|
// int a = 100;
|
||||||
|
// int b = 200;
|
||||||
|
// client.MultiCall("swap").PushRefArgs(a, b).Expect([&a, &b]() {
|
||||||
|
// std::cout << a << b << std::endl;
|
||||||
|
// }).Catch([](const std::string& msg) {
|
||||||
|
// std::cout << msg << std::endl;
|
||||||
|
// });
|
||||||
|
// std::map<int, int> mp{{1, 100}, {2, 400}, {3, 900}};
|
||||||
|
// client.MultiCall("maptest").PushArgs(mp).Expect([](const std::map<int, int>& ret) {
|
||||||
|
// for(auto& iter : ret) {
|
||||||
|
// std::cout << "===" << iter.first << iter.second << std::endl;
|
||||||
|
// }
|
||||||
|
// }).Catch([](const std::string& msg) {
|
||||||
|
// std::cout << msg << std::endl;
|
||||||
|
// });
|
||||||
|
// client.MultiApply();
|
||||||
|
// client.Call("sttest").PushArgs(testst{1, "hello", 3.0, innst{1, 2}}).Expect([](testst ret) {
|
||||||
|
// std::cout << ret.a << ret.b << ret.c << std::endl;
|
||||||
|
// }).Catch([](const std::string& msg) {
|
||||||
|
// std::cout << msg << std::endl;
|
||||||
|
// });
|
||||||
|
// worker.run();
|
||||||
|
// });
|
||||||
|
t1.join();
|
||||||
|
// t2.join();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
INCLUDE_PATH=./include
|
||||||
|
LIBRARY_PATH=./lib
|
||||||
|
LIBS=pthread
|
||||||
|
CFLAGS=-Wall
|
||||||
|
CXXFLAGS=--std=c++14 -Wall -O2 -g
|
||||||
|
|
||||||
|
cobjs = temp/bit.o
|
||||||
|
cppobjs = temp/main.o
|
||||||
|
|
||||||
|
.PHONY: all prep clean
|
||||||
|
all: prep main
|
||||||
|
|
||||||
|
prep:
|
||||||
|
@mkdir -p temp
|
||||||
|
|
||||||
|
main: $(cppobjs)
|
||||||
|
$(CXX) $(cppobjs) -L$(LIBRARY_PATH) -l$(LIBS) -o main
|
||||||
|
|
||||||
|
temp/bit.o: bit.c
|
||||||
|
$(CC) $(CFLAGS) $< -I$(INCLUDE_PATH) -c -o $@
|
||||||
|
|
||||||
|
temp/main.o: main.cpp hprose_tags.hpp hprose_srv.hpp hprose_cli.hpp hprose_conn.hpp hprose_ed.hpp hprose_types.hpp tcp.hpp udp.hpp worker.hpp timer.hpp buffers.hpp objpool.hpp
|
||||||
|
$(CXX) $(CXXFLAGS) $< -I$(INCLUDE_PATH) -c -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -f main
|
||||||
|
@rm -rf temp
|
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef _OBJECT_POOL_HPP_
|
||||||
|
#define _OBJECT_POOL_HPP_
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
template<typename OBJECT, typename MUTEX_TYPE = std::mutex, uint32_t INIT_ALLOC_SIZE = 1024>
|
||||||
|
class ObjectPool {
|
||||||
|
protected:
|
||||||
|
struct ObjectWrapper {
|
||||||
|
OBJECT real_obj;
|
||||||
|
ObjectWrapper* next;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
~ObjectPool() {
|
||||||
|
for(auto& block : mem_blocks) {
|
||||||
|
delete[] block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ... ARGS>
|
||||||
|
OBJECT* Alloc(ARGS... args) {
|
||||||
|
std::lock_guard<MUTEX_TYPE> locker(mutex);
|
||||||
|
if(free_blocks == nullptr) {
|
||||||
|
auto mem_block = new uint8_t[sizeof(ObjectWrapper) * alloc_size];
|
||||||
|
mem_blocks.push_back(mem_block);
|
||||||
|
ObjectWrapper* first = reinterpret_cast<ObjectWrapper*>(mem_block);
|
||||||
|
free_blocks = first + 1;
|
||||||
|
free_blocks->next = reinterpret_cast<ObjectWrapper*>(alloc_size - 2);
|
||||||
|
alloc_size *= 2;
|
||||||
|
if(alloc_size > 0x10000)
|
||||||
|
alloc_size = 0x10000;
|
||||||
|
return new(&first->real_obj) OBJECT(std::forward<ARGS>(args)...);
|
||||||
|
} else {
|
||||||
|
OBJECT* object = nullptr;
|
||||||
|
uintptr_t left_count = reinterpret_cast<uintptr_t>(free_blocks->next);
|
||||||
|
if(left_count <= 0x10000 && left_count > 0) {
|
||||||
|
object = &free_blocks->real_obj;
|
||||||
|
free_blocks++;
|
||||||
|
free_blocks->next = reinterpret_cast<ObjectWrapper*>(left_count - 1);
|
||||||
|
} else {
|
||||||
|
object = &free_blocks->real_obj;
|
||||||
|
if(left_count < 1)
|
||||||
|
free_blocks = nullptr;
|
||||||
|
else
|
||||||
|
free_blocks = free_blocks->next;
|
||||||
|
}
|
||||||
|
return new(object) OBJECT(std::forward<ARGS>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Recycle(OBJECT* object) {
|
||||||
|
std::lock_guard<MUTEX_TYPE> locker(mutex);
|
||||||
|
object->~OBJECT();
|
||||||
|
ObjectWrapper* wrapper = reinterpret_cast<ObjectWrapper*>(object);
|
||||||
|
wrapper->next = free_blocks;
|
||||||
|
free_blocks = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t alloc_size = INIT_ALLOC_SIZE;
|
||||||
|
ObjectWrapper* free_blocks = nullptr;
|
||||||
|
std::list<uint8_t*> mem_blocks;
|
||||||
|
MUTEX_TYPE mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LockLess {
|
||||||
|
void lock() {}
|
||||||
|
void unlock() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename OBJECT, uint32_t INIT_ALLOC_SIZE = 1024>
|
||||||
|
using ObjectPoolNoLock = ObjectPool<OBJECT, LockLess, INIT_ALLOC_SIZE>;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,143 @@
|
||||||
|
#ifndef _TCP_SERVER_HPP_
|
||||||
|
#define _TCP_SERVER_HPP_
|
||||||
|
|
||||||
|
#ifndef ASIO_STANDALONE
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#endif
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "buffers.hpp"
|
||||||
|
|
||||||
|
using asio::ip::tcp;
|
||||||
|
|
||||||
|
template<typename SessionType>
|
||||||
|
using tcp_cb_connected = std::function<std::shared_ptr<SessionType>(tcp::socket&)>;
|
||||||
|
template<typename SessionType>
|
||||||
|
using tcp_cb_received = std::function<void(SessionType*, const unsigned char* data, size_t length)>;
|
||||||
|
template<typename SessionType>
|
||||||
|
using tcp_cb_disconnected = std::function<void(SessionType*)>;
|
||||||
|
|
||||||
|
template<typename SessionType>
|
||||||
|
class TCPSession : public std::enable_shared_from_this<TCPSession<SessionType>> {
|
||||||
|
public:
|
||||||
|
TCPSession(tcp::socket sock) : socket(std::move(sock)) {
|
||||||
|
static_assert(std::is_base_of<TCPSession<SessionType>, SessionType>::value , "SessionType should derive from TCPSession<SessionType>");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadData() {
|
||||||
|
socket.async_read_some(asio::buffer(read_buffer, 1536), [this, self = this->shared_from_this()](std::error_code ec, std::size_t length) {
|
||||||
|
if (!ec) {
|
||||||
|
if(received)
|
||||||
|
received(static_cast<SessionType*>(this), read_buffer, length);
|
||||||
|
ReadData();
|
||||||
|
} else {
|
||||||
|
if(disconnected)
|
||||||
|
disconnected(static_cast<SessionType*>(this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendData(StaticBuffer&& buffer, size_t length) {
|
||||||
|
auto abuf = asio::buffer(buffer.Data(), length);
|
||||||
|
socket.async_write_some(abuf, [buf = std::move(buffer)](std::error_code ec, size_t length) {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() {
|
||||||
|
socket.shutdown(tcp::socket::shutdown_both);
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp::socket& UnderlyingSocket() { return socket; }
|
||||||
|
|
||||||
|
SessionType& SetReceivedCallback(const tcp_cb_received<SessionType>& cb) { received = cb; return static_cast<SessionType&>(*this);}
|
||||||
|
SessionType& SetDisconnectedCallback(const tcp_cb_disconnected<SessionType>& cb) { disconnected = cb; return static_cast<SessionType&>(*this); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
tcp::socket socket;
|
||||||
|
tcp_cb_received<SessionType> received;
|
||||||
|
tcp_cb_disconnected<SessionType> disconnected;
|
||||||
|
unsigned char read_buffer[1536];
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename SessionType>
|
||||||
|
class TCPServer : public std::enable_shared_from_this<TCPServer<SessionType>> {
|
||||||
|
public:
|
||||||
|
TCPServer(asio::io_context& ctx, short port) : acceptor(ctx, tcp::endpoint(tcp::v4(), port)) {}
|
||||||
|
|
||||||
|
void Start() {
|
||||||
|
if(started)
|
||||||
|
return;
|
||||||
|
started = true;
|
||||||
|
_Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop() {
|
||||||
|
if(!started)
|
||||||
|
return;
|
||||||
|
started = false;
|
||||||
|
acceptor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
TCPServer& SetConnectedCallback(const tcp_cb_connected<SessionType>& cb) { connected = cb; return *this; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void _Accept() {
|
||||||
|
acceptor.async_accept([this, self = this->shared_from_this()](std::error_code ec, tcp::socket socket) {
|
||||||
|
if (!ec) {
|
||||||
|
if(connected) {
|
||||||
|
auto session = connected(socket);
|
||||||
|
if(session != nullptr)
|
||||||
|
session->ReadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(started)
|
||||||
|
_Accept();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool started = false;
|
||||||
|
tcp_cb_connected<SessionType> connected;
|
||||||
|
tcp::acceptor acceptor;
|
||||||
|
};
|
||||||
|
|
||||||
|
using connect_fail_cb = std::function<void(const std::string&)>;
|
||||||
|
|
||||||
|
template<typename SessionType>
|
||||||
|
class TCPClient : public std::enable_shared_from_this<TCPClient<SessionType>> {
|
||||||
|
public:
|
||||||
|
TCPClient(asio::io_context& ctx) : socket(ctx) {}
|
||||||
|
void Connect(const char* ipaddr, short port, uint32_t timeout_time, connect_fail_cb cb = nullptr) {
|
||||||
|
tcp::endpoint endpoint(asio::ip::make_address_v4(ipaddr), port);
|
||||||
|
auto timeout = std::make_unique<asio::steady_timer>(socket.get_io_context(), asio::chrono::milliseconds(timeout_time));
|
||||||
|
timeout->async_wait([this, cb](std::error_code ec) {
|
||||||
|
if(!ec) {
|
||||||
|
if(cb)
|
||||||
|
cb(ec.message());
|
||||||
|
socket.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.async_connect(endpoint, [this, cb, timeout = std::move(timeout), self = this->shared_from_this()](std::error_code ec) {
|
||||||
|
if (!ec) {
|
||||||
|
if(connected) {
|
||||||
|
auto session = connected(socket);
|
||||||
|
if(session != nullptr)
|
||||||
|
session->ReadData();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(cb)
|
||||||
|
cb(ec.message());
|
||||||
|
}
|
||||||
|
timeout->cancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TCPClient& SetConnectedCallback(tcp_cb_connected<SessionType> cb) { connected = cb; return *this; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
tcp::socket socket;
|
||||||
|
tcp_cb_connected<SessionType> connected;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef _TIMER_HPP_
|
||||||
|
#define _TIMER_HPP_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#ifndef ASIO_STANDALONE
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#endif
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
using timer_callback = std::function<void()>;
|
||||||
|
|
||||||
|
class Timer : public std::enable_shared_from_this<Timer> {
|
||||||
|
public:
|
||||||
|
Timer(asio::io_context& ctx, const timer_callback& cb, uint32_t wait, uint32_t interval) :
|
||||||
|
callback(cb),
|
||||||
|
interval(interval),
|
||||||
|
timer(ctx, asio::chrono::milliseconds(wait)) {}
|
||||||
|
|
||||||
|
void Begin() {
|
||||||
|
timer.async_wait([this, self = shared_from_this()](const asio::error_code& ec) {
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
if(callback)
|
||||||
|
callback();
|
||||||
|
if(interval > 0)
|
||||||
|
std::make_shared<Timer>(timer.get_io_context(), callback, interval, interval)->Begin();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
void Cancel() {
|
||||||
|
timer.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
timer_callback callback;
|
||||||
|
uint32_t interval;
|
||||||
|
asio::steady_timer timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TimerService {
|
||||||
|
public:
|
||||||
|
virtual std::weak_ptr<Timer> AddTimer(const timer_callback& cb, uint32_t wait, uint32_t interval) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef _UDP_HPP_
|
||||||
|
#define _UDP_HPP_
|
||||||
|
|
||||||
|
#ifndef ASIO_STANDALONE
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#endif
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "buffers.hpp"
|
||||||
|
|
||||||
|
using asio::ip::udp;
|
||||||
|
|
||||||
|
using udp_receiver = std::function<size_t(StaticBuffer&, size_t)>;
|
||||||
|
using udp_sender = std::function<void(StaticBuffer&, size_t)>;
|
||||||
|
using udp_cb_received = std::function<void(const udp_receiver&, const udp_sender&, size_t)>;
|
||||||
|
|
||||||
|
class UDPPeer : public std::enable_shared_from_this<UDPPeer> {
|
||||||
|
public:
|
||||||
|
UDPPeer(asio::io_context& ctx, short port) : socket(ctx, udp::endpoint(udp::v4(), port)) {}
|
||||||
|
UDPPeer(asio::io_context& ctx) : socket(ctx, udp::v4()) {}
|
||||||
|
|
||||||
|
void StartReceive() {
|
||||||
|
if(started)
|
||||||
|
return;
|
||||||
|
started = true;
|
||||||
|
_ReceiveFrom();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopReceive() {
|
||||||
|
if(!started)
|
||||||
|
return;
|
||||||
|
started = false;
|
||||||
|
socket.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendTo(const char* addr, uint16_t port, StaticBuffer& buf, size_t length) {
|
||||||
|
udp::endpoint remote_peer(asio::ip::make_address_v4(addr), port);
|
||||||
|
auto abuf = asio::buffer(buf.Data(), length);
|
||||||
|
socket.async_send_to(abuf, remote_peer, [obuf = std::move(buf)](std::error_code ec, size_t){});
|
||||||
|
}
|
||||||
|
|
||||||
|
UDPPeer& SetReceivedCallback(udp_cb_received cb) { callback = cb; return *this; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void _ReceiveFrom() {
|
||||||
|
socket.async_wait(udp::socket::wait_read, [this, self = this->shared_from_this()](std::error_code ec){
|
||||||
|
if(!ec) {
|
||||||
|
size_t reserve_size = socket.available();
|
||||||
|
if(callback) {
|
||||||
|
auto receiver = [this](StaticBuffer& buf, size_t length) -> size_t {
|
||||||
|
return socket.receive_from(asio::buffer(buf.Data(), length), remote);
|
||||||
|
};
|
||||||
|
auto sender = [this](StaticBuffer& buf, size_t length) {
|
||||||
|
auto abuf = asio::buffer(buf.Data(), length);
|
||||||
|
socket.async_send_to(abuf, remote, [obuf = std::move(buf)](std::error_code ec, size_t){});
|
||||||
|
};
|
||||||
|
callback(receiver, sender, reserve_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(started)
|
||||||
|
_ReceiveFrom();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool started = false;
|
||||||
|
udp_cb_received callback;
|
||||||
|
udp::socket socket;
|
||||||
|
udp::endpoint remote;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef _WORKER_HPP_
|
||||||
|
#define _WORKER_HPP_
|
||||||
|
|
||||||
|
#ifndef ASIO_STANDALONE
|
||||||
|
#define ASIO_STANDALONE
|
||||||
|
#endif
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
#include "timer.hpp"
|
||||||
|
|
||||||
|
class Worker : public asio::io_context {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class TimerdWorker : public Worker , public TimerService {
|
||||||
|
public:
|
||||||
|
std::weak_ptr<Timer> AddTimer(const timer_callback& cb, uint32_t wait, uint32_t interval) {
|
||||||
|
auto timer = std::make_shared<Timer>(*this, cb, wait, interval);
|
||||||
|
timer->Begin();
|
||||||
|
return std::weak_ptr<Timer>(timer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue