mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-13 19:01:24 +00:00
Wire: Move the logic of [de]serialization in WireCmd.
This will help with follow-up changes that add support for a more complete grammer of types, including structures containing pointers to objects or other structures. Instead of having the wire::Client and wire::Server directly act on buffer memory, a couple interfaces are introduced so that WireCmd can do things like get the object<->id mapping and temporary allocations. While the serialization and deserialization of most commands was moved into WireCmd, the commands that don't directly correspond to NXT methods have their logic moved inside Client and Server and will be made to expose the new interface in a follow-up commit.
This commit is contained in:
parent
419e9841a8
commit
88fb8fa353
@ -24,8 +24,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace nxt {
|
namespace nxt { namespace wire {
|
||||||
namespace wire {
|
|
||||||
|
|
||||||
//* Client side implementation of the API, will serialize everything to memory to send to the server side.
|
//* Client side implementation of the API, will serialize everything to memory to send to the server side.
|
||||||
namespace client {
|
namespace client {
|
||||||
@ -187,7 +186,7 @@ namespace wire {
|
|||||||
|
|
||||||
//* The client wire uses the global NXT device to store its global data such as the serializer
|
//* The client wire uses the global NXT device to store its global data such as the serializer
|
||||||
//* and the object id allocators.
|
//* and the object id allocators.
|
||||||
class Device : public ObjectBase {
|
class Device : public ObjectBase, public wire::ObjectIdProvider {
|
||||||
public:
|
public:
|
||||||
Device(CommandSerializer* serializer)
|
Device(CommandSerializer* serializer)
|
||||||
: ObjectBase(this, 1, 1),
|
: ObjectBase(this, 1, 1),
|
||||||
@ -205,6 +204,13 @@ namespace wire {
|
|||||||
ObjectAllocator<{{type.name.CamelCase()}}> {{type.name.camelCase()}};
|
ObjectAllocator<{{type.name.CamelCase()}}> {{type.name.camelCase()}};
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
// Implementation of the ObjectIdProvider interface
|
||||||
|
{% for type in by_category["object"] %}
|
||||||
|
ObjectId GetId({{as_cType(type.name)}} object) const override {
|
||||||
|
return reinterpret_cast<{{as_backendType(type)}}>(object)->id;
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
void HandleError(const char* message) {
|
void HandleError(const char* message) {
|
||||||
if (errorCallback) {
|
if (errorCallback) {
|
||||||
errorCallback(message, errorUserdata);
|
errorCallback(message, errorUserdata);
|
||||||
@ -226,55 +232,18 @@ namespace wire {
|
|||||||
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
||||||
|
|
||||||
{{as_backendType(method.return_type)}} Client{{Suffix}}(
|
{{as_backendType(method.return_type)}} Client{{Suffix}}(
|
||||||
{{-as_backendType(type)}} self
|
{{-as_cType(type.name)}} cSelf
|
||||||
{%- for arg in method.arguments -%}
|
{%- for arg in method.arguments -%}
|
||||||
, {{as_annotated_backendType(arg)}}
|
, {{as_annotated_cType(arg)}}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
) {
|
) {
|
||||||
|
{{as_backendType(type)}} self = reinterpret_cast<{{as_backendType(type)}}>(cSelf);
|
||||||
Device* device = self->device;
|
Device* device = self->device;
|
||||||
wire::{{Suffix}}Cmd cmd;
|
wire::{{Suffix}}Cmd cmd;
|
||||||
|
|
||||||
//* Create the structure going on the wire on the stack and fill it with the value
|
//* Create the structure going on the wire on the stack and fill it with the value
|
||||||
//* arguments so it can compute its size.
|
//* arguments so it can compute its size.
|
||||||
{
|
cmd.self = cSelf;
|
||||||
//* Value objects are stored as IDs
|
|
||||||
{% for arg in method.arguments if arg.annotation == "value" %}
|
|
||||||
{% if arg.type.category == "object" %}
|
|
||||||
cmd.{{as_varName(arg.name)}} = {{as_varName(arg.name)}}->id;
|
|
||||||
{% else %}
|
|
||||||
cmd.{{as_varName(arg.name)}} = {{as_varName(arg.name)}};
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
cmd.self = self->id;
|
|
||||||
|
|
||||||
//* The length of const char* is considered a value argument.
|
|
||||||
{% for arg in method.arguments if arg.length == "strlen" %}
|
|
||||||
cmd.{{as_varName(arg.name)}}Strlen = strlen({{as_varName(arg.name)}});
|
|
||||||
{% endfor %}
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Allocate space to send the command and copy the value args over.
|
|
||||||
size_t requiredSize = cmd.GetRequiredSize();
|
|
||||||
auto allocCmd = reinterpret_cast<decltype(cmd)*>(device->GetCmdSpace(requiredSize));
|
|
||||||
*allocCmd = cmd;
|
|
||||||
|
|
||||||
//* In the allocated space, write the non-value arguments.
|
|
||||||
{% for arg in method.arguments if arg.annotation != "value" %}
|
|
||||||
{% set argName = as_varName(arg.name) %}
|
|
||||||
{% if arg.length == "strlen" %}
|
|
||||||
memcpy(allocCmd->GetPtr_{{argName}}(), {{argName}}, allocCmd->{{argName}}Strlen + 1);
|
|
||||||
{% elif arg.length == "constant_one" %}
|
|
||||||
memcpy(allocCmd->GetPtr_{{argName}}(), {{argName}}, sizeof(*{{argName}}));
|
|
||||||
{% elif arg.type.category == "object" %}
|
|
||||||
auto {{argName}}Storage = reinterpret_cast<uint32_t*>(allocCmd->GetPtr_{{argName}}());
|
|
||||||
for (size_t i = 0; i < {{as_varName(arg.length.name)}}; i++) {
|
|
||||||
{{argName}}Storage[i] = {{argName}}[i]->id;
|
|
||||||
}
|
|
||||||
{% else %}
|
|
||||||
memcpy(allocCmd->GetPtr_{{argName}}(), {{argName}}, {{as_varName(arg.length.name)}} * sizeof(*{{argName}}));
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
//* For object creation, store the object ID the client will use for the result.
|
//* For object creation, store the object ID the client will use for the result.
|
||||||
{% if method.return_type.category == "object" %}
|
{% if method.return_type.category == "object" %}
|
||||||
@ -288,8 +257,20 @@ namespace wire {
|
|||||||
self->builderCallback.canCall = false;
|
self->builderCallback.canCall = false;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
allocCmd->resultId = allocation->object->id;
|
cmd.resultId = allocation->object->id;
|
||||||
allocCmd->resultSerial = allocation->serial;
|
cmd.resultSerial = allocation->serial;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for arg in method.arguments %}
|
||||||
|
cmd.{{as_varName(arg.name)}} = {{as_varName(arg.name)}};
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
//* Allocate space to send the command and copy the value args over.
|
||||||
|
size_t requiredSize = cmd.GetRequiredSize();
|
||||||
|
char* allocatedBuffer = static_cast<char*>(device->GetCmdSpace(requiredSize));
|
||||||
|
cmd.Serialize(allocatedBuffer, *device);
|
||||||
|
|
||||||
|
{% if method.return_type.category == "object" %}
|
||||||
return allocation->object.get();
|
return allocation->object.get();
|
||||||
{% endif %}
|
{% endif %}
|
||||||
}
|
}
|
||||||
@ -320,8 +301,7 @@ namespace wire {
|
|||||||
wire::{{as_MethodSuffix(type.name, Name("destroy"))}}Cmd cmd;
|
wire::{{as_MethodSuffix(type.name, Name("destroy"))}}Cmd cmd;
|
||||||
cmd.objectId = obj->id;
|
cmd.objectId = obj->id;
|
||||||
|
|
||||||
size_t requiredSize = cmd.GetRequiredSize();
|
auto allocCmd = static_cast<decltype(cmd)*>(obj->device->GetCmdSpace(sizeof(cmd)));
|
||||||
auto allocCmd = reinterpret_cast<decltype(cmd)*>(obj->device->GetCmdSpace(requiredSize));
|
|
||||||
*allocCmd = cmd;
|
*allocCmd = cmd;
|
||||||
|
|
||||||
obj->device->{{type.name.camelCase()}}.Free(obj);
|
obj->device->{{type.name.camelCase()}}.Free(obj);
|
||||||
@ -349,8 +329,7 @@ namespace wire {
|
|||||||
cmd.start = start;
|
cmd.start = start;
|
||||||
cmd.size = size;
|
cmd.size = size;
|
||||||
|
|
||||||
size_t requiredSize = cmd.GetRequiredSize();
|
auto allocCmd = static_cast<decltype(cmd)*>(buffer->device->GetCmdSpace(sizeof(cmd)));
|
||||||
auto allocCmd = reinterpret_cast<decltype(cmd)*>(buffer->device->GetCmdSpace(requiredSize));
|
|
||||||
*allocCmd = cmd;
|
*allocCmd = cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +338,9 @@ namespace wire {
|
|||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyClientBufferUnmap(Buffer* buffer) {
|
void ProxyClientBufferUnmap(nxtBuffer cBuffer) {
|
||||||
|
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
||||||
|
|
||||||
//* Invalidate the local pointer, and cancel all other in-flight requests that would turn into
|
//* Invalidate the local pointer, and cancel all other in-flight requests that would turn into
|
||||||
//* errors anyway (you can't double map). This prevents race when the following happens, where
|
//* errors anyway (you can't double map). This prevents race when the following happens, where
|
||||||
//* the application code would have unmapped a buffer but still receive a callback:
|
//* the application code would have unmapped a buffer but still receive a callback:
|
||||||
@ -373,7 +354,7 @@ namespace wire {
|
|||||||
}
|
}
|
||||||
buffer->ClearMapRequests(NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN);
|
buffer->ClearMapRequests(NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN);
|
||||||
|
|
||||||
ClientBufferUnmap(buffer);
|
ClientBufferUnmap(cBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientDeviceReference(Device*) {
|
void ClientDeviceReference(Device*) {
|
||||||
@ -414,7 +395,7 @@ namespace wire {
|
|||||||
Client(Device* device) : mDevice(device) {
|
Client(Device* device) : mDevice(device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t* HandleCommands(const uint8_t* commands, size_t size) override {
|
const char* HandleCommands(const char* commands, size_t size) override {
|
||||||
while (size > sizeof(ReturnWireCmd)) {
|
while (size > sizeof(ReturnWireCmd)) {
|
||||||
ReturnWireCmd cmdId = *reinterpret_cast<const ReturnWireCmd*>(commands);
|
ReturnWireCmd cmdId = *reinterpret_cast<const ReturnWireCmd*>(commands);
|
||||||
|
|
||||||
@ -454,48 +435,51 @@ namespace wire {
|
|||||||
//* Checks there is enough data left, updates the buffer / size and returns
|
//* Checks there is enough data left, updates the buffer / size and returns
|
||||||
//* the command (or nullptr for an error).
|
//* the command (or nullptr for an error).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static const T* GetCommand(const uint8_t** commands, size_t* size) {
|
static const T* GetData(const char** buffer, size_t* size, size_t count) {
|
||||||
if (*size < sizeof(T)) {
|
// TODO(cwallez@chromium.org): Check for overflow
|
||||||
|
size_t totalSize = count * sizeof(T);
|
||||||
|
if (*size < totalSize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const T* cmd = reinterpret_cast<const T*>(*commands);
|
const T* data = reinterpret_cast<const T*>(*buffer);
|
||||||
|
|
||||||
size_t cmdSize = cmd->GetRequiredSize();
|
*buffer += totalSize;
|
||||||
if (*size < cmdSize) {
|
*size -= totalSize;
|
||||||
return nullptr;
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
static const T* GetCommand(const char** commands, size_t* size) {
|
||||||
|
return GetData<T>(commands, size, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
*commands += cmdSize;
|
bool HandleDeviceErrorCallbackCmd(const char** commands, size_t* size) {
|
||||||
*size -= cmdSize;
|
|
||||||
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandleDeviceErrorCallbackCmd(const uint8_t** commands, size_t* size) {
|
|
||||||
const auto* cmd = GetCommand<ReturnDeviceErrorCallbackCmd>(commands, size);
|
const auto* cmd = GetCommand<ReturnDeviceErrorCallbackCmd>(commands, size);
|
||||||
if (cmd == nullptr) {
|
if (cmd == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->GetMessage()[cmd->messageStrlen] != '\0') {
|
const char* message = GetData<char>(commands, size, cmd->messageStrlen + 1);
|
||||||
|
if (message == nullptr || message[cmd->messageStrlen] != '\0') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mDevice->HandleError(cmd->GetMessage());
|
mDevice->HandleError(message);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
{% for type in by_category["object"] if type.is_builder %}
|
{% for type in by_category["object"] if type.is_builder %}
|
||||||
{% set Type = type.name.CamelCase() %}
|
{% set Type = type.name.CamelCase() %}
|
||||||
bool Handle{{Type}}ErrorCallbackCmd(const uint8_t** commands, size_t* size) {
|
bool Handle{{Type}}ErrorCallbackCmd(const char** commands, size_t* size) {
|
||||||
const auto* cmd = GetCommand<Return{{Type}}ErrorCallbackCmd>(commands, size);
|
const auto* cmd = GetCommand<Return{{Type}}ErrorCallbackCmd>(commands, size);
|
||||||
if (cmd == nullptr) {
|
if (cmd == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->GetMessage()[cmd->messageStrlen] != '\0') {
|
const char* message = GetData<char>(commands, size, cmd->messageStrlen + 1);
|
||||||
|
if (message == nullptr || message[cmd->messageStrlen] != '\0') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,18 +491,18 @@ namespace wire {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool called = builtObject->builderCallback.Call(static_cast<nxtBuilderErrorStatus>(cmd->status), cmd->GetMessage());
|
bool called = builtObject->builderCallback.Call(static_cast<nxtBuilderErrorStatus>(cmd->status), message);
|
||||||
|
|
||||||
// Unhandled builder errors are forwarded to the device
|
// Unhandled builder errors are forwarded to the device
|
||||||
if (!called && cmd->status != NXT_BUILDER_ERROR_STATUS_SUCCESS && cmd->status != NXT_BUILDER_ERROR_STATUS_UNKNOWN) {
|
if (!called && cmd->status != NXT_BUILDER_ERROR_STATUS_SUCCESS && cmd->status != NXT_BUILDER_ERROR_STATUS_UNKNOWN) {
|
||||||
mDevice->HandleError(("Unhandled builder error: " + std::string(cmd->GetMessage())).c_str());
|
mDevice->HandleError(("Unhandled builder error: " + std::string(message)).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
bool HandleBufferMapReadAsyncCallback(const uint8_t** commands, size_t* size) {
|
bool HandleBufferMapReadAsyncCallback(const char** commands, size_t* size) {
|
||||||
const auto* cmd = GetCommand<ReturnBufferMapReadAsyncCallbackCmd>(commands, size);
|
const auto* cmd = GetCommand<ReturnBufferMapReadAsyncCallbackCmd>(commands, size);
|
||||||
if (cmd == nullptr) {
|
if (cmd == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@ -554,8 +538,14 @@ namespace wire {
|
|||||||
if (buffer->mappedData != nullptr) {
|
if (buffer->mappedData != nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* requestData = GetData<char>(commands, size, request.size);
|
||||||
|
if (requestData == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
buffer->mappedData = malloc(request.size);
|
buffer->mappedData = malloc(request.size);
|
||||||
memcpy(buffer->mappedData, cmd->GetData(), request.size);
|
memcpy(buffer->mappedData, requestData, request.size);
|
||||||
|
|
||||||
request.callback(static_cast<nxtBufferMapAsyncStatus>(cmd->status), buffer->mappedData, request.userdata);
|
request.callback(static_cast<nxtBufferMapAsyncStatus>(cmd->status), buffer->mappedData, request.userdata);
|
||||||
} else {
|
} else {
|
||||||
@ -577,5 +567,4 @@ namespace wire {
|
|||||||
return new client::Client(clientDevice);
|
return new client::Client(clientDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}} // namespace nxt::wire
|
||||||
}
|
|
||||||
|
@ -12,83 +12,246 @@
|
|||||||
//* See the License for the specific language governing permissions and
|
//* See the License for the specific language governing permissions and
|
||||||
//* limitations under the License.
|
//* limitations under the License.
|
||||||
|
|
||||||
#include "wire/WireCmd_autogen.h"
|
#include "wire/WireCmd.h"
|
||||||
|
|
||||||
namespace nxt {
|
#include <cstring>
|
||||||
namespace wire {
|
|
||||||
|
namespace nxt { namespace wire {
|
||||||
|
|
||||||
|
// Macro to simplify error handling, similar to NXT_TRY but for DeserializeResult.
|
||||||
|
#define DESERIALIZE_TRY(EXPR) \
|
||||||
|
{ \
|
||||||
|
DeserializeResult exprResult = EXPR; \
|
||||||
|
if (exprResult != DeserializeResult::Success) { \
|
||||||
|
return exprResult; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consumes from (buffer, size) enough memory to contain T[count] and return it in data.
|
||||||
|
// Returns FatalError if not enough memory was available
|
||||||
|
template <typename T>
|
||||||
|
DeserializeResult GetPtrFromBuffer(const char** buffer, size_t* size, size_t count, const T** data) {
|
||||||
|
// TODO(cwallez@chromium.org): For robustness we would need to handle overflows here.
|
||||||
|
size_t totalSize = sizeof(T) * count;
|
||||||
|
if (totalSize > *size) {
|
||||||
|
return DeserializeResult::FatalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = reinterpret_cast<const T*>(*buffer);
|
||||||
|
*buffer += totalSize;
|
||||||
|
*size -= totalSize;
|
||||||
|
|
||||||
|
return DeserializeResult::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocates enough space from allocator to countain T[count] and return it in out.
|
||||||
|
// Return FatalError if the allocator couldn't allocate the memory.
|
||||||
|
template <typename T>
|
||||||
|
DeserializeResult GetSpace(DeserializeAllocator* allocator, size_t count, T** out) {
|
||||||
|
// TODO(cwallez@chromium.org): For robustness we would need to handle overflows here.
|
||||||
|
size_t totalSize = sizeof(T) * count;
|
||||||
|
*out = static_cast<T*>(allocator->GetSpace(totalSize));
|
||||||
|
if (*out == nullptr) {
|
||||||
|
return DeserializeResult::FatalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeserializeResult::Success;
|
||||||
|
}
|
||||||
|
|
||||||
{% for type in by_category["object"] %}
|
{% for type in by_category["object"] %}
|
||||||
{% for method in type.methods %}
|
{% for method in type.methods %}
|
||||||
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
||||||
|
{% set Cmd = Suffix + "Cmd" %}
|
||||||
|
|
||||||
size_t {{Suffix}}Cmd::GetRequiredSize() const {
|
//* Structure for the wire format of each of the commands. Parameters passed by value
|
||||||
size_t result = sizeof(*this);
|
//* are embedded directly in the structure. Other parameters are assumed to be in the
|
||||||
|
//* memory directly following the structure in the buffer. With value parameters the
|
||||||
|
//* structure can compute how much buffer size it needs and where the start of non-value
|
||||||
|
//* parameters is in the buffer.
|
||||||
|
struct {{Cmd}}Transfer {
|
||||||
|
//* Start the structure with the command ID, so that casting to WireCmd gives the ID.
|
||||||
|
wire::WireCmd commandId;
|
||||||
|
|
||||||
|
ObjectId self;
|
||||||
|
|
||||||
|
{% if method.return_type.category == "object" %}
|
||||||
|
ObjectId resultId;
|
||||||
|
ObjectSerial resultSerial;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
//* Value types are directly in the command, objects being replaced with their IDs.
|
||||||
|
{% for arg in method.arguments if arg.annotation == "value" %}
|
||||||
|
{% if arg.type.category == "object" %}
|
||||||
|
ObjectId {{as_varName(arg.name)}};
|
||||||
|
{% else %}
|
||||||
|
{{as_cType(arg.type.name)}} {{as_varName(arg.name)}};
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
//* const char* have their length embedded directly in the command.
|
||||||
|
{% for arg in method.arguments if arg.length == "strlen" %}
|
||||||
|
size_t {{as_varName(arg.name)}}Strlen;
|
||||||
|
{% endfor %}
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t {{Cmd}}::GetRequiredSize() const {
|
||||||
|
size_t result = sizeof({{Cmd}}Transfer);
|
||||||
|
|
||||||
{% for arg in method.arguments if arg.annotation != "value" %}
|
{% for arg in method.arguments if arg.annotation != "value" %}
|
||||||
|
{% set argName = as_varName(arg.name) %}
|
||||||
|
|
||||||
{% if arg.length == "strlen" %}
|
{% if arg.length == "strlen" %}
|
||||||
result += {{as_varName(arg.name)}}Strlen + 1;
|
result += std::strlen({{as_varName(arg.name)}});
|
||||||
|
|
||||||
{% elif arg.length == "constant_one" %}
|
{% elif arg.length == "constant_one" %}
|
||||||
result += sizeof({{as_cType(arg.type.name)}});
|
result += sizeof({{as_cType(arg.type.name)}});
|
||||||
|
|
||||||
{% elif arg.type.category == "object" %}
|
{% elif arg.type.category == "object" %}
|
||||||
result += {{as_varName(arg.length.name)}} * sizeof(uint32_t);
|
result += {{as_varName(arg.length.name)}} * sizeof(ObjectId);
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
result += {{as_varName(arg.length.name)}} * sizeof({{as_cType(arg.type.name)}});
|
result += {{as_varName(arg.length.name)}} * sizeof({{as_cType(arg.type.name)}});
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
{% for const in ["", "const"] %}
|
void {{Cmd}}::Serialize(char* buffer, const ObjectIdProvider& objectIdProvider) const {
|
||||||
{% for get_arg in method.arguments if get_arg.annotation != "value" %}
|
auto transfer = reinterpret_cast<{{Cmd}}Transfer*>(buffer);
|
||||||
|
buffer += sizeof({{Cmd}}Transfer);
|
||||||
|
|
||||||
{{const}} uint8_t* {{Suffix}}Cmd::GetPtr_{{as_varName(get_arg.name)}}() {{const}} {
|
transfer->commandId = wire::WireCmd::{{Suffix}};
|
||||||
//* Start counting after the current structure
|
transfer->self = objectIdProvider.GetId(self);
|
||||||
{{const}} uint8_t* ptr = reinterpret_cast<{{const}} uint8_t*>(this + 1);
|
|
||||||
|
|
||||||
//* Increment the pointer until we find the 'arg' then return early.
|
{% if method.return_type.category == "object" %}
|
||||||
//* This will mean some of the code will be unreachable but there is no
|
transfer->resultId = resultId;
|
||||||
//* "break" in Jinja2.
|
transfer->resultSerial = resultSerial;
|
||||||
{% for arg in method.arguments if arg.annotation != "value" %}
|
|
||||||
{% if get_arg == arg %}
|
|
||||||
return ptr;
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if arg.length == "strlen" %}
|
|
||||||
ptr += {{as_varName(arg.name)}}Strlen + 1;
|
//* Value types are directly in the command, objects being replaced with their IDs.
|
||||||
{% elif arg.length == "constant_one" %}
|
{% for arg in method.arguments if arg.annotation == "value" %}
|
||||||
ptr += sizeof({{as_cType(arg.type.name)}});
|
{% set argName = as_varName(arg.name) %}
|
||||||
{% elif arg.type.category == "object" %}
|
{% if arg.type.category == "object" %}
|
||||||
ptr += {{as_varName(arg.length.name)}} * sizeof(uint32_t);
|
transfer->{{argName}} = objectIdProvider.GetId(this->{{argName}});
|
||||||
{% else %}
|
{% else %}
|
||||||
ptr += {{as_varName(arg.length.name)}} * sizeof({{as_cType(arg.type.name)}});
|
transfer->{{argName}} = this->{{argName}};
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
//* const char* have their length embedded directly in the command.
|
||||||
|
{% for arg in method.arguments if arg.length == "strlen" %}
|
||||||
|
{% set argName = as_varName(arg.name) %}
|
||||||
|
transfer->{{argName}}Strlen = std::strlen(this->{{argName}});
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
//* In the allocated space, write the non-value arguments.
|
||||||
|
{% for arg in method.arguments if arg.annotation != "value" %}
|
||||||
|
{% set argName = as_varName(arg.name) %}
|
||||||
|
|
||||||
|
{% if arg.length == "strlen" %}
|
||||||
|
memcpy(buffer, this->{{argName}}, transfer->{{argName}}Strlen);
|
||||||
|
buffer += transfer->{{argName}}Strlen;
|
||||||
|
|
||||||
|
{% elif arg.length == "constant_one" %}
|
||||||
|
memcpy(buffer, this->{{argName}}, sizeof(*(this->{{argName}})));
|
||||||
|
buffer += sizeof(*(this->{{argName}}));
|
||||||
|
|
||||||
|
{% elif arg.type.category == "object" %}
|
||||||
|
{% set argLength = as_varName(arg.length.name) %}
|
||||||
|
auto {{argName}}Storage = reinterpret_cast<ObjectId*>(buffer);
|
||||||
|
for (size_t i = 0; i < {{argLength}}; i++) {
|
||||||
|
{{argName}}Storage[i] = objectIdProvider.GetId(this->{{argName}}[i]);
|
||||||
|
}
|
||||||
|
buffer += sizeof(ObjectId) * {{argLength}};
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{% set argLength = as_varName(arg.length.name) %}
|
||||||
|
memcpy(buffer, this->{{argName}}, {{argLength}} * sizeof(*(this->{{argName}})));
|
||||||
|
buffer += {{argLength}} * sizeof(*(this->{{argName}}));
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeserializeResult {{Cmd}}::Deserialize(const char** buffer, size_t* size, DeserializeAllocator* allocator, const ObjectIdResolver& resolver) {
|
||||||
|
(void) allocator;
|
||||||
|
|
||||||
|
const {{Cmd}}Transfer* transfer = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, 1, &transfer));
|
||||||
|
|
||||||
|
selfId = transfer->self;
|
||||||
|
{% if method.return_type.category == "object" %}
|
||||||
|
resultId = transfer->resultId;
|
||||||
|
resultSerial = transfer->resultSerial;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
DESERIALIZE_TRY(resolver.GetFromId(selfId, &self));
|
||||||
|
|
||||||
|
{% for arg in method.arguments if arg.annotation == "value" %}
|
||||||
|
{% set argName = as_varName(arg.name) %}
|
||||||
|
{% if arg.type.category == "object" %}
|
||||||
|
DESERIALIZE_TRY(resolver.GetFromId(transfer->{{argName}}, &(this->{{argName}})));
|
||||||
|
{% else %}
|
||||||
|
this->{{argName}} = transfer->{{argName}};
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for arg in method.arguments if arg.annotation != "value" %}
|
||||||
|
{% set argName = as_varName(arg.name) %}
|
||||||
|
{% if arg.length == "strlen" %}
|
||||||
|
{
|
||||||
|
size_t stringLength = transfer->{{argName}}Strlen;
|
||||||
|
const char* stringInBuffer = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, stringLength, &stringInBuffer));
|
||||||
|
|
||||||
|
char* copiedString = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetSpace(allocator, stringLength + 1, &copiedString));
|
||||||
|
memcpy(copiedString, stringInBuffer, stringLength);
|
||||||
|
copiedString[stringLength] = '\0';
|
||||||
|
this->{{argName}} = copiedString;
|
||||||
|
}
|
||||||
|
{% elif arg.length == "constant_one" %}
|
||||||
|
{
|
||||||
|
const {{as_cType(arg.type.name)}}* argInBuffer = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, 1, &argInBuffer));
|
||||||
|
|
||||||
|
{{as_cType(arg.type.name)}}* copiedArg = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetSpace(allocator, 1, &copiedArg));
|
||||||
|
memcpy(copiedArg, argInBuffer, sizeof(*{{argName}}));
|
||||||
|
this->{{argName}} = copiedArg;
|
||||||
|
}
|
||||||
|
{% elif arg.type.category == "object" %}
|
||||||
|
{% set argLength = as_varName(arg.length.name) %}
|
||||||
|
{
|
||||||
|
const ObjectId* idsInBuffer = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, {{argLength}}, &idsInBuffer));
|
||||||
|
|
||||||
|
{{as_cType(arg.type.name)}}* copiedObjects = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetSpace(allocator, {{argLength}}, &copiedObjects));
|
||||||
|
for (size_t i = 0; i < {{argLength}}; i++) {
|
||||||
|
DESERIALIZE_TRY(resolver.GetFromId(idsInBuffer[i], &copiedObjects[i]));
|
||||||
|
}
|
||||||
|
this->{{argName}} = copiedObjects;
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
{% set argLength = as_varName(arg.length.name) %}
|
||||||
|
{
|
||||||
|
const {{as_cType(arg.type.name)}}* argInBuffer = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, {{argLength}}, &argInBuffer));
|
||||||
|
|
||||||
|
{{as_cType(arg.type.name)}}* copiedArg = nullptr;
|
||||||
|
DESERIALIZE_TRY(GetSpace(allocator, {{argLength}}, &copiedArg))
|
||||||
|
memcpy(copiedArg, argInBuffer, {{argLength}} * sizeof(*{{argName}}));
|
||||||
|
this->{{argName}} = copiedArg;
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
return DeserializeResult::Success;
|
||||||
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% set Suffix = as_MethodSuffix(type.name, Name("destroy")) %}
|
}} // namespace nxt::wire
|
||||||
size_t {{Suffix}}Cmd::GetRequiredSize() const {
|
|
||||||
return sizeof(*this);
|
|
||||||
}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% for type in by_category["object"] if type.is_builder %}
|
|
||||||
{% set Type = type.name.CamelCase() %}
|
|
||||||
size_t Return{{Type}}ErrorCallbackCmd::GetRequiredSize() const {
|
|
||||||
return sizeof(*this) + messageStrlen + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* Return{{Type}}ErrorCallbackCmd::GetMessage() {
|
|
||||||
return reinterpret_cast<char*>(this + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Return{{Type}}ErrorCallbackCmd::GetMessage() const {
|
|
||||||
return reinterpret_cast<const char*>(this + 1);
|
|
||||||
}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -15,10 +15,41 @@
|
|||||||
#ifndef WIRE_WIRECMD_AUTOGEN_H_
|
#ifndef WIRE_WIRECMD_AUTOGEN_H_
|
||||||
#define WIRE_WIRECMD_AUTOGEN_H_
|
#define WIRE_WIRECMD_AUTOGEN_H_
|
||||||
|
|
||||||
#include <nxt/nxt.h>
|
namespace nxt { namespace wire {
|
||||||
|
|
||||||
namespace nxt {
|
using ObjectId = uint32_t;
|
||||||
namespace wire {
|
using ObjectSerial = uint32_t;
|
||||||
|
|
||||||
|
enum class DeserializeResult {
|
||||||
|
Success,
|
||||||
|
FatalError,
|
||||||
|
ErrorObject,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface to allocate more space to deserialize pointed-to data.
|
||||||
|
// nullptr is treated as an error.
|
||||||
|
class DeserializeAllocator {
|
||||||
|
public:
|
||||||
|
virtual void* GetSpace(size_t size) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface to convert an ID to a server object, if possible.
|
||||||
|
// Methods return FatalError if the ID is for a non-existent object, ErrorObject if the
|
||||||
|
// object is an error value and Success otherwise.
|
||||||
|
class ObjectIdResolver {
|
||||||
|
public:
|
||||||
|
{% for type in by_category["object"] %}
|
||||||
|
virtual DeserializeResult GetFromId(ObjectId id, {{as_cType(type.name)}}* out) const = 0;
|
||||||
|
{% endfor %}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface to convert a client object to its ID for the wiring.
|
||||||
|
class ObjectIdProvider {
|
||||||
|
public:
|
||||||
|
{% for type in by_category["object"] %}
|
||||||
|
virtual ObjectId GetId({{as_cType(type.name)}} object) const = 0;
|
||||||
|
{% endfor %}
|
||||||
|
};
|
||||||
|
|
||||||
//* Enum used as a prefix to each command on the wire format.
|
//* Enum used as a prefix to each command on the wire format.
|
||||||
enum class WireCmd : uint32_t {
|
enum class WireCmd : uint32_t {
|
||||||
@ -34,50 +65,45 @@ namespace wire {
|
|||||||
{% for type in by_category["object"] %}
|
{% for type in by_category["object"] %}
|
||||||
{% for method in type.methods %}
|
{% for method in type.methods %}
|
||||||
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
||||||
|
{% set Cmd = Suffix + "Cmd" %}
|
||||||
|
|
||||||
//* Structure for the wire format of each of the commands. Parameters passed by value
|
//* These are "structure" version of the list of arguments to the different NXT methods.
|
||||||
//* are embedded directly in the structure. Other parameters are assumed to be in the
|
//* They provide helpers to serialize/deserialize to/from a buffer.
|
||||||
//* memory directly following the structure in the buffer. With value parameters the
|
struct {{Cmd}} {
|
||||||
//* structure can compute how much buffer size it needs and where the start of non-value
|
//* From a filled structure, compute how much size will be used in the serialization buffer.
|
||||||
//* parameters is in the buffer.
|
size_t GetRequiredSize() const;
|
||||||
struct {{Suffix}}Cmd {
|
|
||||||
|
|
||||||
//* Start the structure with the command ID, so that casting to WireCmd gives the ID.
|
//* Serialize the structure and everything it points to into serializeBuffer which must be
|
||||||
wire::WireCmd commandId = wire::WireCmd::{{Suffix}};
|
//* big enough to contain all the data (as queried from GetRequiredSize).
|
||||||
|
void Serialize(char* serializeBuffer, const ObjectIdProvider& objectIdProvider) const;
|
||||||
|
|
||||||
uint32_t self;
|
//* Deserializes the structure from a buffer, consuming a maximum of *size bytes. When this
|
||||||
|
//* function returns, buffer and size will be updated by the number of bytes consumed to
|
||||||
|
//* deserialize the structure. Structures containing pointers will use allocator to get
|
||||||
|
//* scratch space to deserialize the pointed-to data.
|
||||||
|
//* Deserialize returns:
|
||||||
|
//* - Success if everything went well (yay!)
|
||||||
|
//* - FatalError is something bad happened (buffer too small for example)
|
||||||
|
//* - ErrorObject if one if the deserialized object is an error value, for the implementation
|
||||||
|
//* of the Maybe monad.
|
||||||
|
//* If the return value is not FatalError, selfId, resultId and resultSerial (if present) are
|
||||||
|
//* filled.
|
||||||
|
DeserializeResult Deserialize(const char** buffer, size_t* size, DeserializeAllocator* allocator, const ObjectIdResolver& resolver);
|
||||||
|
|
||||||
|
{{as_cType(type.name)}} self;
|
||||||
|
|
||||||
|
//* Command handlers want to know the object ID in addition to the backing object.
|
||||||
|
//* Doesn't need to be filled before Serialize, or GetRequiredSize.
|
||||||
|
ObjectId selfId;
|
||||||
|
|
||||||
//* Commands creating objects say which ID the created object will be referred as.
|
//* Commands creating objects say which ID the created object will be referred as.
|
||||||
{% if method.return_type.category == "object" %}
|
{% if method.return_type.category == "object" %}
|
||||||
uint32_t resultId;
|
ObjectId resultId;
|
||||||
uint32_t resultSerial;
|
ObjectSerial resultSerial;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
//* Value types are directly in the command, objects being replaced with their IDs.
|
{% for arg in method.arguments %}
|
||||||
{% for arg in method.arguments if arg.annotation == "value" %}
|
{{as_annotated_cType(arg)}};
|
||||||
{% if arg.type.category == "object" %}
|
|
||||||
uint32_t {{as_varName(arg.name)}};
|
|
||||||
{% else %}
|
|
||||||
{{as_cType(arg.type.name)}} {{as_varName(arg.name)}};
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
//* const char* have their length embedded directly in the command.
|
|
||||||
{% for arg in method.arguments if arg.length == "strlen" %}
|
|
||||||
size_t {{as_varName(arg.name)}}Strlen;
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
//* The following commands do computation, provided the members for value parameters
|
|
||||||
//* have been initialized.
|
|
||||||
|
|
||||||
//* Compute how much buffer memory is required to hold the structure and all its arguments.
|
|
||||||
size_t GetRequiredSize() const;
|
|
||||||
|
|
||||||
//* Gets the pointer to the start of the buffer containing a non-value parameter.
|
|
||||||
{% for get_arg in method.arguments if get_arg.annotation != "value" %}
|
|
||||||
{% set ArgName = as_varName(get_arg.name) %}
|
|
||||||
uint8_t* GetPtr_{{ArgName}}();
|
|
||||||
const uint8_t* GetPtr_{{ArgName}}() const;
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
};
|
};
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -86,9 +112,7 @@ namespace wire {
|
|||||||
{% set Suffix = as_MethodSuffix(type.name, Name("destroy")) %}
|
{% set Suffix = as_MethodSuffix(type.name, Name("destroy")) %}
|
||||||
struct {{Suffix}}Cmd {
|
struct {{Suffix}}Cmd {
|
||||||
WireCmd commandId = WireCmd::{{Suffix}};
|
WireCmd commandId = WireCmd::{{Suffix}};
|
||||||
uint32_t objectId;
|
ObjectId objectId;
|
||||||
|
|
||||||
size_t GetRequiredSize() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -102,22 +126,18 @@ namespace wire {
|
|||||||
BufferMapReadAsyncCallback,
|
BufferMapReadAsyncCallback,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//* Command for the server calling a builder status callback.
|
||||||
{% for type in by_category["object"] if type.is_builder %}
|
{% for type in by_category["object"] if type.is_builder %}
|
||||||
struct Return{{type.name.CamelCase()}}ErrorCallbackCmd {
|
struct Return{{type.name.CamelCase()}}ErrorCallbackCmd {
|
||||||
wire::ReturnWireCmd commandId = ReturnWireCmd::{{type.name.CamelCase()}}ErrorCallback;
|
wire::ReturnWireCmd commandId = ReturnWireCmd::{{type.name.CamelCase()}}ErrorCallback;
|
||||||
|
|
||||||
uint32_t builtObjectId;
|
ObjectId builtObjectId;
|
||||||
uint32_t builtObjectSerial;
|
ObjectSerial builtObjectSerial;
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
size_t messageStrlen;
|
size_t messageStrlen;
|
||||||
|
|
||||||
size_t GetRequiredSize() const;
|
|
||||||
char* GetMessage();
|
|
||||||
const char* GetMessage() const;
|
|
||||||
};
|
};
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
}
|
}} // namespace nxt::wire
|
||||||
}
|
|
||||||
|
|
||||||
#endif // WIRE_WIRECMD_AUTOGEN_H_
|
#endif // WIRE_WIRECMD_AUTOGEN_H_
|
||||||
|
@ -17,11 +17,12 @@
|
|||||||
|
|
||||||
#include "common/Assert.h"
|
#include "common/Assert.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace nxt {
|
namespace nxt { namespace wire {
|
||||||
namespace wire {
|
|
||||||
|
|
||||||
namespace server {
|
namespace server {
|
||||||
class Server;
|
class Server;
|
||||||
@ -72,6 +73,19 @@ namespace wire {
|
|||||||
|
|
||||||
//* Get a backend objects for a given client ID.
|
//* Get a backend objects for a given client ID.
|
||||||
//* Returns nullptr if the ID hasn't previously been allocated.
|
//* Returns nullptr if the ID hasn't previously been allocated.
|
||||||
|
const Data* Get(uint32_t id) const {
|
||||||
|
if (id >= mKnown.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Data* data = &mKnown[id];
|
||||||
|
|
||||||
|
if (!data->allocated) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
Data* Get(uint32_t id) {
|
Data* Get(uint32_t id) {
|
||||||
if (id >= mKnown.size()) {
|
if (id >= mKnown.size()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -130,7 +144,60 @@ namespace wire {
|
|||||||
|
|
||||||
void ForwardBufferMapReadAsync(nxtBufferMapAsyncStatus status, const void* ptr, nxtCallbackUserdata userdata);
|
void ForwardBufferMapReadAsync(nxtBufferMapAsyncStatus status, const void* ptr, nxtCallbackUserdata userdata);
|
||||||
|
|
||||||
class Server : public CommandHandler {
|
// A really really simple implementation of the DeserializeAllocator. It's main feature
|
||||||
|
// is that it has some inline storage so as to avoid allocations for the majority of
|
||||||
|
// commands.
|
||||||
|
class ServerAllocator : public DeserializeAllocator {
|
||||||
|
public:
|
||||||
|
ServerAllocator() {
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ServerAllocator() {
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GetSpace(size_t size) override {
|
||||||
|
// Return space in the current buffer if possible first.
|
||||||
|
if (mRemainingSize >= size) {
|
||||||
|
char* buffer = mCurrentBuffer;
|
||||||
|
mCurrentBuffer += size;
|
||||||
|
mRemainingSize -= size;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise allocate a new buffer and try again.
|
||||||
|
size_t allocationSize = std::max(size, size_t(2048));
|
||||||
|
char* allocation = static_cast<char*>(malloc(allocationSize));
|
||||||
|
if (allocation == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAllocations.push_back(allocation);
|
||||||
|
mCurrentBuffer = allocation;
|
||||||
|
mRemainingSize = allocationSize;
|
||||||
|
return GetSpace(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
|
for (auto allocation : mAllocations) {
|
||||||
|
free(allocation);
|
||||||
|
}
|
||||||
|
mAllocations.clear();
|
||||||
|
|
||||||
|
// The initial buffer is the inline buffer so that some allocations can be skipped
|
||||||
|
mCurrentBuffer = mStaticBuffer;
|
||||||
|
mRemainingSize = sizeof(mStaticBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t mRemainingSize = 0;
|
||||||
|
char* mCurrentBuffer = nullptr;
|
||||||
|
char mStaticBuffer[2048];
|
||||||
|
std::vector<char*> mAllocations;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Server : public CommandHandler, public ObjectIdResolver {
|
||||||
public:
|
public:
|
||||||
Server(nxtDevice device, const nxtProcTable& procs, CommandSerializer* serializer)
|
Server(nxtDevice device, const nxtProcTable& procs, CommandSerializer* serializer)
|
||||||
: mProcs(procs), mSerializer(serializer) {
|
: mProcs(procs), mSerializer(serializer) {
|
||||||
@ -147,9 +214,11 @@ namespace wire {
|
|||||||
ReturnDeviceErrorCallbackCmd cmd;
|
ReturnDeviceErrorCallbackCmd cmd;
|
||||||
cmd.messageStrlen = std::strlen(message);
|
cmd.messageStrlen = std::strlen(message);
|
||||||
|
|
||||||
auto allocCmd = reinterpret_cast<ReturnDeviceErrorCallbackCmd*>(GetCmdSpace(cmd.GetRequiredSize()));
|
auto allocCmd = static_cast<ReturnDeviceErrorCallbackCmd*>(GetCmdSpace(sizeof(cmd)));
|
||||||
*allocCmd = cmd;
|
*allocCmd = cmd;
|
||||||
strcpy(allocCmd->GetMessage(), message);
|
|
||||||
|
char* messageAlloc = static_cast<char*>(GetCmdSpace(cmd.messageStrlen + 1));
|
||||||
|
strcpy(messageAlloc, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
{% for type in by_category["object"] if type.is_builder%}
|
{% for type in by_category["object"] if type.is_builder%}
|
||||||
@ -176,9 +245,10 @@ namespace wire {
|
|||||||
cmd.status = status;
|
cmd.status = status;
|
||||||
cmd.messageStrlen = std::strlen(message);
|
cmd.messageStrlen = std::strlen(message);
|
||||||
|
|
||||||
auto allocCmd = reinterpret_cast<Return{{Type}}ErrorCallbackCmd*>(GetCmdSpace(cmd.GetRequiredSize()));
|
auto allocCmd = static_cast<Return{{Type}}ErrorCallbackCmd*>(GetCmdSpace(sizeof(cmd)));
|
||||||
*allocCmd = cmd;
|
*allocCmd = cmd;
|
||||||
strcpy(allocCmd->GetMessage(), message);
|
char* messageAlloc = static_cast<char*>(GetCmdSpace(strlen(message) + 1));
|
||||||
|
strcpy(messageAlloc, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -189,23 +259,22 @@ namespace wire {
|
|||||||
cmd.bufferSerial = data->bufferSerial;
|
cmd.bufferSerial = data->bufferSerial;
|
||||||
cmd.requestSerial = data->requestSerial;
|
cmd.requestSerial = data->requestSerial;
|
||||||
cmd.status = status;
|
cmd.status = status;
|
||||||
|
|
||||||
cmd.dataLength = 0;
|
cmd.dataLength = 0;
|
||||||
if (status == NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
|
|
||||||
cmd.dataLength = data->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto allocCmd = reinterpret_cast<ReturnBufferMapReadAsyncCallbackCmd*>(GetCmdSpace(cmd.GetRequiredSize()));
|
auto allocCmd = static_cast<ReturnBufferMapReadAsyncCallbackCmd*>(GetCmdSpace(sizeof(cmd)));
|
||||||
*allocCmd = cmd;
|
*allocCmd = cmd;
|
||||||
|
|
||||||
if (status == NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
|
if (status == NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS) {
|
||||||
memcpy(allocCmd->GetData(), ptr, data->size);
|
allocCmd->dataLength = data->size;
|
||||||
|
|
||||||
|
void* dataAlloc = GetCmdSpace(data->size);
|
||||||
|
memcpy(dataAlloc, ptr, data->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete data;
|
delete data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t* HandleCommands(const uint8_t* commands, size_t size) override {
|
const char* HandleCommands(const char* commands, size_t size) override {
|
||||||
mProcs.deviceTick(mKnownDevice.Get(1)->handle);
|
mProcs.deviceTick(mKnownDevice.Get(1)->handle);
|
||||||
|
|
||||||
while (size > sizeof(WireCmd)) {
|
while (size > sizeof(WireCmd)) {
|
||||||
@ -236,6 +305,7 @@ namespace wire {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
mAllocator.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size != 0) {
|
if (size != 0) {
|
||||||
@ -249,10 +319,29 @@ namespace wire {
|
|||||||
nxtProcTable mProcs;
|
nxtProcTable mProcs;
|
||||||
CommandSerializer* mSerializer = nullptr;
|
CommandSerializer* mSerializer = nullptr;
|
||||||
|
|
||||||
|
ServerAllocator mAllocator;
|
||||||
|
|
||||||
void* GetCmdSpace(size_t size) {
|
void* GetCmdSpace(size_t size) {
|
||||||
return mSerializer->GetCmdSpace(size);
|
return mSerializer->GetCmdSpace(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implementation of the ObjectIdResolver interface
|
||||||
|
{% for type in by_category["object"] %}
|
||||||
|
DeserializeResult GetFromId(ObjectId id, {{as_cType(type.name)}}* out) const override {
|
||||||
|
auto data = mKnown{{type.name.CamelCase()}}.Get(id);
|
||||||
|
if (data == nullptr) {
|
||||||
|
return DeserializeResult::FatalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = data->handle;
|
||||||
|
if (data->valid) {
|
||||||
|
return DeserializeResult::Success;
|
||||||
|
} else {
|
||||||
|
return DeserializeResult::ErrorObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
//* The list of known IDs for each object type.
|
//* The list of known IDs for each object type.
|
||||||
{% for type in by_category["object"] %}
|
{% for type in by_category["object"] %}
|
||||||
KnownObjects<{{as_cType(type.name)}}> mKnown{{type.name.CamelCase()}};
|
KnownObjects<{{as_cType(type.name)}}> mKnown{{type.name.CamelCase()}};
|
||||||
@ -262,20 +351,15 @@ namespace wire {
|
|||||||
//* Checks there is enough data left, updates the buffer / size and returns
|
//* Checks there is enough data left, updates the buffer / size and returns
|
||||||
//* the command (or nullptr for an error).
|
//* the command (or nullptr for an error).
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static const T* GetCommand(const uint8_t** commands, size_t* size) {
|
static const T* GetCommand(const char** commands, size_t* size) {
|
||||||
if (*size < sizeof(T)) {
|
if (*size < sizeof(T)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const T* cmd = reinterpret_cast<const T*>(*commands);
|
const T* cmd = reinterpret_cast<const T*>(*commands);
|
||||||
|
|
||||||
size_t cmdSize = cmd->GetRequiredSize();
|
*commands += sizeof(T);
|
||||||
if (*size < cmdSize) {
|
*size -= sizeof(T);
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
*commands += cmdSize;
|
|
||||||
*size -= cmdSize;
|
|
||||||
|
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
@ -287,99 +371,42 @@ namespace wire {
|
|||||||
|
|
||||||
//* The generic command handlers
|
//* The generic command handlers
|
||||||
|
|
||||||
bool Handle{{Suffix}}(const uint8_t** commands, size_t* size) {
|
bool Handle{{Suffix}}(const char** commands, size_t* size) {
|
||||||
//* Get command ptr, and check it fits in the buffer.
|
{{Suffix}}Cmd cmd;
|
||||||
const auto* cmd = GetCommand<{{Suffix}}Cmd>(commands, size);
|
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator, *this);
|
||||||
if (cmd == nullptr) {
|
|
||||||
|
if (deserializeResult == DeserializeResult::FatalError) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* While unpacking arguments, if any of them is an error, valid will be set to false.
|
|
||||||
bool valid = true;
|
|
||||||
|
|
||||||
//* Unpack 'self'
|
//* Unpack 'self'
|
||||||
{% set Type = type.name.CamelCase() %}
|
auto* selfData = mKnown{{type.name.CamelCase()}}.Get(cmd.selfId);
|
||||||
{{as_cType(type.name)}} self;
|
ASSERT(selfData != nullptr);
|
||||||
auto* selfData = mKnown{{Type}}.Get(cmd->self);
|
|
||||||
{
|
|
||||||
if (selfData == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
valid = valid && selfData->valid;
|
|
||||||
self = selfData->handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
//* Unpack value objects from IDs.
|
|
||||||
{% for arg in method.arguments if arg.annotation == "value" and arg.type.category == "object" %}
|
|
||||||
{% set Type = arg.type.name.CamelCase() %}
|
|
||||||
{{as_cType(arg.type.name)}} arg_{{as_varName(arg.name)}};
|
|
||||||
{
|
|
||||||
auto* data = mKnown{{Type}}.Get(cmd->{{as_varName(arg.name)}});
|
|
||||||
if (data == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
valid = valid && data->valid;
|
|
||||||
arg_{{as_varName(arg.name)}} = data->handle;
|
|
||||||
}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
//* Unpack pointer arguments
|
|
||||||
{% for arg in method.arguments if arg.annotation != "value" %}
|
|
||||||
{% set argName = as_varName(arg.name) %}
|
|
||||||
const {{as_cType(arg.type.name)}}* arg_{{argName}};
|
|
||||||
{% if arg.length == "strlen" %}
|
|
||||||
//* Unpack strings, checking they are null-terminated.
|
|
||||||
arg_{{argName}} = reinterpret_cast<const {{as_cType(arg.type.name)}}*>(cmd->GetPtr_{{argName}}());
|
|
||||||
if (arg_{{argName}}[cmd->{{argName}}Strlen] != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
{% elif arg.type.category == "object" %}
|
|
||||||
//* Unpack arrays of objects.
|
|
||||||
//* TODO(cwallez@chromium.org) do not allocate when there are few objects.
|
|
||||||
std::vector<{{as_cType(arg.type.name)}}> {{argName}}Storage(cmd->{{as_varName(arg.length.name)}});
|
|
||||||
auto {{argName}}Ids = reinterpret_cast<const uint32_t*>(cmd->GetPtr_{{argName}}());
|
|
||||||
for (size_t i = 0; i < cmd->{{as_varName(arg.length.name)}}; i++) {
|
|
||||||
{% set Type = arg.type.name.CamelCase() %}
|
|
||||||
auto* data = mKnown{{Type}}.Get({{argName}}Ids[i]);
|
|
||||||
if (data == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
{{argName}}Storage[i] = data->handle;
|
|
||||||
valid = valid && data->valid;
|
|
||||||
}
|
|
||||||
arg_{{argName}} = {{argName}}Storage.data();
|
|
||||||
{% else %}
|
|
||||||
//* For anything else, just get the pointer.
|
|
||||||
arg_{{argName}} = reinterpret_cast<const {{as_cType(arg.type.name)}}*>(cmd->GetPtr_{{argName}}());
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
//* At that point all the data has been upacked in cmd->* or arg_*
|
|
||||||
|
|
||||||
//* In all cases allocate the object data as it will be refered-to by the client.
|
//* In all cases allocate the object data as it will be refered-to by the client.
|
||||||
{% set return_type = method.return_type %}
|
{% set return_type = method.return_type %}
|
||||||
{% set returns = return_type.name.canonical_case() != "void" %}
|
{% set returns = return_type.name.canonical_case() != "void" %}
|
||||||
{% if returns %}
|
{% if returns %}
|
||||||
{% set Type = method.return_type.name.CamelCase() %}
|
{% set Type = method.return_type.name.CamelCase() %}
|
||||||
auto* resultData = mKnown{{Type}}.Allocate(cmd->resultId);
|
auto* resultData = mKnown{{Type}}.Allocate(cmd.resultId);
|
||||||
if (resultData == nullptr) {
|
if (resultData == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
resultData->serial = cmd->resultSerial;
|
resultData->serial = cmd.resultSerial;
|
||||||
|
|
||||||
{% if type.is_builder %}
|
{% if type.is_builder %}
|
||||||
selfData->builtObjectId = cmd->resultId;
|
selfData->builtObjectId = cmd.resultId;
|
||||||
selfData->builtObjectSerial = cmd->resultSerial;
|
selfData->builtObjectSerial = cmd.resultSerial;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
//* After the data is allocated, apply the argument error propagation mechanism
|
//* After the data is allocated, apply the argument error propagation mechanism
|
||||||
if (!valid) {
|
if (deserializeResult == DeserializeResult::ErrorObject) {
|
||||||
{% if type.is_builder %}
|
{% if type.is_builder %}
|
||||||
selfData->valid = false;
|
selfData->valid = false;
|
||||||
//* If we are in GetResult, fake an error callback
|
//* If we are in GetResult, fake an error callback
|
||||||
{% if returns %}
|
{% if returns %}
|
||||||
On{{type.name.CamelCase()}}Error(NXT_BUILDER_ERROR_STATUS_ERROR, "Maybe monad", cmd->self, selfData->serial);
|
On{{type.name.CamelCase()}}Error(NXT_BUILDER_ERROR_STATUS_ERROR, "Maybe monad", cmd.selfId, selfData->serial);
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
return true;
|
return true;
|
||||||
@ -388,13 +415,9 @@ namespace wire {
|
|||||||
{% if returns %}
|
{% if returns %}
|
||||||
auto result ={{" "}}
|
auto result ={{" "}}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
mProcs.{{as_varName(type.name, method.name)}}(self
|
mProcs.{{as_varName(type.name, method.name)}}(cmd.self
|
||||||
{%- for arg in method.arguments -%}
|
{%- for arg in method.arguments -%}
|
||||||
{%- if arg.annotation == "value" and arg.type.category != "object" -%}
|
, cmd.{{as_varName(arg.name)}}
|
||||||
, cmd->{{as_varName(arg.name)}}
|
|
||||||
{%- else -%}
|
|
||||||
, arg_{{as_varName(arg.name)}}
|
|
||||||
{%- endif -%}
|
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -407,7 +430,7 @@ namespace wire {
|
|||||||
{% if return_type.is_builder %}
|
{% if return_type.is_builder %}
|
||||||
if (result != nullptr) {
|
if (result != nullptr) {
|
||||||
uint64_t userdata1 = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
|
uint64_t userdata1 = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
|
||||||
uint64_t userdata2 = (uint64_t(resultData->serial) << uint64_t(32)) + cmd->resultId;
|
uint64_t userdata2 = (uint64_t(resultData->serial) << uint64_t(32)) + cmd.resultId;
|
||||||
mProcs.{{as_varName(return_type.name, Name("set error callback"))}}(result, Forward{{return_type.name.CamelCase()}}ToClient, userdata1, userdata2);
|
mProcs.{{as_varName(return_type.name, Name("set error callback"))}}(result, Forward{{return_type.name.CamelCase()}}ToClient, userdata1, userdata2);
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -420,18 +443,20 @@ namespace wire {
|
|||||||
//* Handlers for the destruction of objects: clients do the tracking of the
|
//* Handlers for the destruction of objects: clients do the tracking of the
|
||||||
//* reference / release and only send destroy on refcount = 0.
|
//* reference / release and only send destroy on refcount = 0.
|
||||||
{% set Suffix = as_MethodSuffix(type.name, Name("destroy")) %}
|
{% set Suffix = as_MethodSuffix(type.name, Name("destroy")) %}
|
||||||
bool Handle{{Suffix}}(const uint8_t** commands, size_t* size) {
|
bool Handle{{Suffix}}(const char** commands, size_t* size) {
|
||||||
const auto* cmd = GetCommand<{{Suffix}}Cmd>(commands, size);
|
const auto* cmd = GetCommand<{{Suffix}}Cmd>(commands, size);
|
||||||
if (cmd == nullptr) {
|
if (cmd == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectId objectId = cmd->objectId;
|
||||||
|
|
||||||
//* ID 0 are reserved for nullptr and cannot be destroyed.
|
//* ID 0 are reserved for nullptr and cannot be destroyed.
|
||||||
if (cmd->objectId == 0) {
|
if (objectId == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* data = mKnown{{type.name.CamelCase()}}.Get(cmd->objectId);
|
auto* data = mKnown{{type.name.CamelCase()}}.Get(objectId);
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -440,12 +465,12 @@ namespace wire {
|
|||||||
mProcs.{{as_varName(type.name, Name("release"))}}(data->handle);
|
mProcs.{{as_varName(type.name, Name("release"))}}(data->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
mKnown{{type.name.CamelCase()}}.Free(cmd->objectId);
|
mKnown{{type.name.CamelCase()}}.Free(objectId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
bool HandleBufferMapReadAsync(const uint8_t** commands, size_t* size) {
|
bool HandleBufferMapReadAsync(const char** commands, size_t* size) {
|
||||||
//* These requests are just forwarded to the buffer, with userdata containing what the client
|
//* These requests are just forwarded to the buffer, with userdata containing what the client
|
||||||
//* will require in the return command.
|
//* will require in the return command.
|
||||||
const auto* cmd = GetCommand<BufferMapReadAsyncCmd>(commands, size);
|
const auto* cmd = GetCommand<BufferMapReadAsyncCmd>(commands, size);
|
||||||
@ -453,17 +478,22 @@ namespace wire {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* buffer = mKnownBuffer.Get(cmd->bufferId);
|
ObjectId bufferId = cmd->bufferId;
|
||||||
|
uint32_t requestSerial = cmd->requestSerial;
|
||||||
|
uint32_t requestSize = cmd->size;
|
||||||
|
uint32_t requestStart = cmd->start;
|
||||||
|
|
||||||
|
auto* buffer = mKnownBuffer.Get(bufferId);
|
||||||
if (buffer == nullptr) {
|
if (buffer == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* data = new MapReadUserdata;
|
auto* data = new MapReadUserdata;
|
||||||
data->server = this;
|
data->server = this;
|
||||||
data->bufferId = cmd->bufferId;
|
data->bufferId = bufferId;
|
||||||
data->bufferSerial = buffer->serial;
|
data->bufferSerial = buffer->serial;
|
||||||
data->requestSerial = cmd->requestSerial;
|
data->requestSerial = requestSerial;
|
||||||
data->size = cmd->size;
|
data->size = requestSize;
|
||||||
|
|
||||||
auto userdata = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
|
auto userdata = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
|
||||||
|
|
||||||
@ -473,7 +503,7 @@ namespace wire {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mProcs.bufferMapReadAsync(buffer->handle, cmd->start, cmd->size, ForwardBufferMapReadAsync, userdata);
|
mProcs.bufferMapReadAsync(buffer->handle, requestStart, requestSize, ForwardBufferMapReadAsync, userdata);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -503,5 +533,4 @@ namespace wire {
|
|||||||
return new server::Server(device, procs, serializer);
|
return new server::Server(device, procs, serializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}} // namespace nxt::wire
|
||||||
}
|
|
||||||
|
@ -25,7 +25,6 @@ Generate(
|
|||||||
${GENERATOR_COMMON_ARGS}
|
${GENERATOR_COMMON_ARGS}
|
||||||
-T wire
|
-T wire
|
||||||
EXTRA_SOURCES
|
EXTRA_SOURCES
|
||||||
${WIRE_DIR}/WireCmd.cpp
|
|
||||||
${WIRE_DIR}/WireCmd.h
|
${WIRE_DIR}/WireCmd.h
|
||||||
)
|
)
|
||||||
target_include_directories(wire_autogen PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(wire_autogen PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
@ -31,7 +31,7 @@ namespace nxt { namespace wire {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* result = &mBuffer[mOffset];
|
char* result = &mBuffer[mOffset];
|
||||||
mOffset += size;
|
mOffset += size;
|
||||||
|
|
||||||
if (mOffset > sizeof(mBuffer)) {
|
if (mOffset > sizeof(mBuffer)) {
|
||||||
|
@ -34,7 +34,7 @@ namespace nxt { namespace wire {
|
|||||||
private:
|
private:
|
||||||
CommandHandler* mHandler = nullptr;
|
CommandHandler* mHandler = nullptr;
|
||||||
size_t mOffset = 0;
|
size_t mOffset = 0;
|
||||||
uint8_t mBuffer[10000000];
|
char mBuffer[10000000];
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace nxt::wire
|
}} // namespace nxt::wire
|
||||||
|
@ -31,7 +31,7 @@ namespace nxt { namespace wire {
|
|||||||
class CommandHandler {
|
class CommandHandler {
|
||||||
public:
|
public:
|
||||||
virtual ~CommandHandler() = default;
|
virtual ~CommandHandler() = default;
|
||||||
virtual const uint8_t* HandleCommands(const uint8_t* commands, size_t size) = 0;
|
virtual const char* HandleCommands(const char* commands, size_t size) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
CommandHandler* NewClientDevice(nxtProcTable* procs,
|
CommandHandler* NewClientDevice(nxtProcTable* procs,
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
// Copyright 2017 The NXT Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#include "wire/WireCmd.h"
|
|
||||||
|
|
||||||
namespace nxt { namespace wire {
|
|
||||||
|
|
||||||
size_t ReturnDeviceErrorCallbackCmd::GetRequiredSize() const {
|
|
||||||
return sizeof(*this) + messageStrlen + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* ReturnDeviceErrorCallbackCmd::GetMessage() {
|
|
||||||
return reinterpret_cast<char*>(this + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* ReturnDeviceErrorCallbackCmd::GetMessage() const {
|
|
||||||
return reinterpret_cast<const char*>(this + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t BufferMapReadAsyncCmd::GetRequiredSize() const {
|
|
||||||
return sizeof(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ReturnBufferMapReadAsyncCallbackCmd::GetRequiredSize() const {
|
|
||||||
return sizeof(*this) + dataLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ReturnBufferMapReadAsyncCallbackCmd::GetData() {
|
|
||||||
return this + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const void* ReturnBufferMapReadAsyncCallbackCmd::GetData() const {
|
|
||||||
return this + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}} // namespace nxt::wire
|
|
@ -15,6 +15,8 @@
|
|||||||
#ifndef WIRE_WIRECMD_H_
|
#ifndef WIRE_WIRECMD_H_
|
||||||
#define WIRE_WIRECMD_H_
|
#define WIRE_WIRECMD_H_
|
||||||
|
|
||||||
|
#include <nxt/nxt.h>
|
||||||
|
|
||||||
#include "wire/WireCmd_autogen.h"
|
#include "wire/WireCmd_autogen.h"
|
||||||
|
|
||||||
namespace nxt { namespace wire {
|
namespace nxt { namespace wire {
|
||||||
@ -23,10 +25,6 @@ namespace nxt { namespace wire {
|
|||||||
wire::ReturnWireCmd commandId = ReturnWireCmd::DeviceErrorCallback;
|
wire::ReturnWireCmd commandId = ReturnWireCmd::DeviceErrorCallback;
|
||||||
|
|
||||||
size_t messageStrlen;
|
size_t messageStrlen;
|
||||||
|
|
||||||
size_t GetRequiredSize() const;
|
|
||||||
char* GetMessage();
|
|
||||||
const char* GetMessage() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BufferMapReadAsyncCmd {
|
struct BufferMapReadAsyncCmd {
|
||||||
@ -36,8 +34,6 @@ namespace nxt { namespace wire {
|
|||||||
uint32_t requestSerial;
|
uint32_t requestSerial;
|
||||||
uint32_t start;
|
uint32_t start;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
|
||||||
size_t GetRequiredSize() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReturnBufferMapReadAsyncCallbackCmd {
|
struct ReturnBufferMapReadAsyncCallbackCmd {
|
||||||
@ -48,10 +44,6 @@ namespace nxt { namespace wire {
|
|||||||
uint32_t requestSerial;
|
uint32_t requestSerial;
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
uint32_t dataLength;
|
uint32_t dataLength;
|
||||||
|
|
||||||
size_t GetRequiredSize() const;
|
|
||||||
void* GetData();
|
|
||||||
const void* GetData() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace nxt::wire
|
}} // namespace nxt::wire
|
||||||
|
Loading…
x
Reference in New Issue
Block a user