Add new JSON interface.
GitOrigin-RevId: aaf756de59e72f949c1150d99e1277047f25fac9
This commit is contained in:
parent
119fc9563a
commit
7d6f14db10
@ -13,6 +13,7 @@
|
||||
#include "td/utils/JsonBuilder.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@ -52,23 +53,29 @@ static std::pair<td_api::object_ptr<td_api::Function>, string> to_request(Slice
|
||||
return std::make_pair(std::move(func), std::move(extra));
|
||||
}
|
||||
|
||||
static std::string from_response(const td_api::Object &object, const string &extra) {
|
||||
static string from_response(const td_api::Object &object, const string &extra, int client_id) {
|
||||
auto str = json_encode<string>(ToJson(object));
|
||||
CHECK(!str.empty() && str.back() == '}');
|
||||
str.reserve(str.size() + (extra.empty() ? 0 : 10 + extra.size()) + (client_id == 0 ? 0 : 14 + 10));
|
||||
if (!extra.empty()) {
|
||||
str.pop_back();
|
||||
str.reserve(str.size() + 11 + extra.size());
|
||||
str += ",\"@extra\":";
|
||||
str += extra;
|
||||
str += '}';
|
||||
}
|
||||
if (client_id != 0) {
|
||||
str.pop_back();
|
||||
str += ",\"@client_id\":";
|
||||
str += to_string(client_id);
|
||||
str += '}';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static TD_THREAD_LOCAL std::string *current_output;
|
||||
static TD_THREAD_LOCAL string *current_output;
|
||||
|
||||
static const char *store_string(std::string str) {
|
||||
init_thread_local<std::string>(current_output);
|
||||
static const char *store_string(string str) {
|
||||
init_thread_local<string>(current_output);
|
||||
*current_output = std::move(str);
|
||||
return current_output->c_str();
|
||||
}
|
||||
@ -85,11 +92,11 @@ void ClientJson::send(Slice request) {
|
||||
|
||||
const char *ClientJson::receive(double timeout) {
|
||||
auto response = client_.receive(timeout);
|
||||
if (!response.object) {
|
||||
if (response.object == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string extra;
|
||||
string extra;
|
||||
if (response.id != 0) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
auto it = extra_.find(response.id);
|
||||
@ -98,13 +105,60 @@ const char *ClientJson::receive(double timeout) {
|
||||
extra_.erase(it);
|
||||
}
|
||||
}
|
||||
return store_string(from_response(*response.object, extra));
|
||||
return store_string(from_response(*response.object, extra, 0));
|
||||
}
|
||||
|
||||
const char *ClientJson::execute(Slice request) {
|
||||
auto parsed_request = to_request(request);
|
||||
return store_string(from_response(*Client::execute(Client::Request{0, std::move(parsed_request.first)}).object,
|
||||
parsed_request.second));
|
||||
parsed_request.second, 0));
|
||||
}
|
||||
|
||||
static ClientManager *get_manager() {
|
||||
static ClientManager client_manager;
|
||||
return &client_manager;
|
||||
}
|
||||
|
||||
static std::mutex extra_mutex;
|
||||
static std::unordered_map<int64, string> extra;
|
||||
static std::atomic<uint64> extra_id{1};
|
||||
|
||||
int td_json_create_client() {
|
||||
return static_cast<int>(get_manager()->create_client());
|
||||
}
|
||||
|
||||
void td_json_send(int client_id, Slice request) {
|
||||
auto parsed_request = to_request(request);
|
||||
auto request_id = extra_id.fetch_add(1, std::memory_order_relaxed);
|
||||
if (!parsed_request.second.empty()) {
|
||||
std::lock_guard<std::mutex> guard(extra_mutex);
|
||||
extra[request_id] = std::move(parsed_request.second);
|
||||
}
|
||||
get_manager()->send(client_id, request_id, std::move(parsed_request.first));
|
||||
}
|
||||
|
||||
const char *td_json_receive(double timeout) {
|
||||
auto response = get_manager()->receive(timeout);
|
||||
if (!response.object) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
string extra_str;
|
||||
if (response.request_id != 0) {
|
||||
std::lock_guard<std::mutex> guard(extra_mutex);
|
||||
auto it = extra.find(response.request_id);
|
||||
if (it != extra.end()) {
|
||||
extra_str = std::move(it->second);
|
||||
extra.erase(it);
|
||||
}
|
||||
}
|
||||
return store_string(from_response(*response.object, extra_str, response.client_id));
|
||||
}
|
||||
|
||||
const char *td_json_execute(Slice request) {
|
||||
auto parsed_request = to_request(request);
|
||||
return store_string(
|
||||
from_response(*ClientManager::execute(std::move(parsed_request.first)), parsed_request.second, 0));
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
@ -33,4 +33,12 @@ class ClientJson final {
|
||||
std::atomic<std::uint64_t> extra_id_{1};
|
||||
};
|
||||
|
||||
int td_json_create_client();
|
||||
|
||||
void td_json_send(int client_id, Slice request);
|
||||
|
||||
const char *td_json_receive(double timeout);
|
||||
|
||||
const char *td_json_execute(Slice request);
|
||||
|
||||
} // namespace td
|
||||
|
@ -10,10 +10,6 @@
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
extern "C" int td_json_client_square(int x, const char *str) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
void *td_json_client_create() {
|
||||
return new td::ClientJson();
|
||||
}
|
||||
@ -33,3 +29,19 @@ const char *td_json_client_receive(void *client, double timeout) {
|
||||
const char *td_json_client_execute(void *client, const char *request) {
|
||||
return td::ClientJson::execute(td::Slice(request == nullptr ? "" : request));
|
||||
}
|
||||
|
||||
int td_create_client() {
|
||||
return td::td_json_create_client();
|
||||
}
|
||||
|
||||
void td_send(int client_id, const char *request) {
|
||||
td::td_json_send(client_id, td::Slice(request == nullptr ? "" : request));
|
||||
}
|
||||
|
||||
const char *td_receive(double timeout) {
|
||||
return td::td_json_receive(timeout);
|
||||
}
|
||||
|
||||
const char *td_execute(const char *request) {
|
||||
return td::td_json_execute(td::Slice(request == nullptr ? "" : request));
|
||||
}
|
||||
|
@ -21,11 +21,11 @@
|
||||
* The main TDLib interface is asynchronous. To match requests with a corresponding response a field "@extra" can
|
||||
* be added to the request object. The corresponding response will have an "@extra" field with exactly the same value.
|
||||
*
|
||||
* A TDLib client instance should be created through td_json_client_create.
|
||||
* A TDLib client instance can be created through td_json_client_create.
|
||||
* Requests then can be sent using td_json_client_send from any thread.
|
||||
* New updates and request responses can be received through td_json_client_receive from any thread. This function
|
||||
* shouldn't be called simultaneously from two different threads. Also note that all updates and request responses
|
||||
* should be applied in the order they were received to ensure consistency.
|
||||
* must not be called simultaneously from two different threads. Also note that all updates and request responses
|
||||
* must be applied in the order they were received to ensure consistency.
|
||||
* Given this information, it's advisable to call this function from a dedicated thread.
|
||||
* Some service TDLib requests can be executed synchronously from any thread by using td_json_client_execute.
|
||||
* The TDLib client instance can be destroyed via td_json_client_destroy.
|
||||
@ -68,7 +68,7 @@ TDJSON_EXPORT void td_json_client_send(void *client, const char *request);
|
||||
|
||||
/**
|
||||
* Receives incoming updates and request responses from the TDLib client. May be called from any thread, but
|
||||
* shouldn't be called simultaneously from two different threads.
|
||||
* must not be called simultaneously from two different threads.
|
||||
* Returned pointer will be deallocated by TDLib during next call to td_json_client_receive or td_json_client_execute
|
||||
* in the same thread, so it can't be used after that.
|
||||
* \param[in] client The client.
|
||||
@ -89,11 +89,74 @@ TDJSON_EXPORT const char *td_json_client_receive(void *client, double timeout);
|
||||
TDJSON_EXPORT const char *td_json_client_execute(void *client, const char *request);
|
||||
|
||||
/**
|
||||
* Destroys the TDLib client instance. After this is called the client instance shouldn't be used anymore.
|
||||
* Destroys the TDLib client instance. After this is called the client instance must not be used anymore.
|
||||
* \param[in] client The client.
|
||||
*/
|
||||
TDJSON_EXPORT void td_json_client_destroy(void *client);
|
||||
|
||||
/*
|
||||
* New TDLib JSON interface.
|
||||
*
|
||||
* The main TDLib interface is asynchronous. To match requests with a corresponding response a field "@extra" can
|
||||
* be added to the request object. The corresponding response will have an "@extra" field with exactly the same value.
|
||||
* Each returned object will have an "@client_id" field, containing and identifier of the client for which
|
||||
* a response or an update is received.
|
||||
*
|
||||
* A TDLib client instance can be created through td_create_client.
|
||||
* Requests then can be sent using td_send from any thread and the received client identifier.
|
||||
* New updates and request responses can be received through td_receive from any thread. This function
|
||||
* must not be called simultaneously from two different threads. Also note that all updates and request responses
|
||||
* must be applied in the order they were received to ensure consistency.
|
||||
* Some TDLib requests can be executed synchronously from any thread by using td_execute.
|
||||
* The TDLib client instances are destroyed automatically after they are closed.
|
||||
*
|
||||
* General pattern of usage:
|
||||
* \code
|
||||
* int client_id = td_create_client();
|
||||
* // share the client_id with other threads, which will be able to send requests via td_send
|
||||
*
|
||||
* const double WAIT_TIMEOUT = 10.0; // seconds
|
||||
* while (true) {
|
||||
* const char *result = td_receive(WAIT_TIMEOUT);
|
||||
* if (result) {
|
||||
* // parse the result as JSON object and process it as an incoming update or an answer to a previously sent request
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a new instance of TDLib.
|
||||
* \return Opaque indentifier of the created TDLib client.
|
||||
*/
|
||||
TDJSON_EXPORT int td_create_client();
|
||||
|
||||
/**
|
||||
* Sends request to the TDLib client. May be called from any thread.
|
||||
* \param[in] client_id The TDLib client identifier.
|
||||
* \param[in] request JSON-serialized null-terminated request to TDLib.
|
||||
*/
|
||||
TDJSON_EXPORT void td_send(int client_id, const char *request);
|
||||
|
||||
/**
|
||||
* Receives incoming updates and request responses. Must not be called simultaneously from two different threads.
|
||||
* Returned pointer will be deallocated by TDLib during next call to td_receive or td_execute
|
||||
* in the same thread, so it can't be used after that.
|
||||
* \param[in] timeout The maximum number of seconds allowed for this function to wait for new data.
|
||||
* \return JSON-serialized null-terminated incoming update or request response. May be NULL if the timeout expires.
|
||||
*/
|
||||
TDJSON_EXPORT const char *td_receive(double timeout);
|
||||
|
||||
/**
|
||||
* Synchronously executes TDLib request. May be called from any thread.
|
||||
* Only a few requests can be executed synchronously.
|
||||
* Returned pointer will be deallocated by TDLib during next call to td_receive or td_execute
|
||||
* in the same thread, so it can't be used after that.
|
||||
* \param[in] request JSON-serialized null-terminated request to TDLib.
|
||||
* \return JSON-serialized null-terminated request response.
|
||||
*/
|
||||
TDJSON_EXPORT const char *td_execute(const char *request);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user