From c3531ead11851523b8154d7d3b3573acb23afb28 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Sun, 29 Dec 2024 04:13:39 -0800 Subject: [PATCH] fix: support arrays in C++ join/leave --- src/draft.ts | 23 ++++++++--------- src/outgoing_msg.cc | 14 ++--------- src/socket.cc | 49 ++++++++++++++----------------------- src/socket.h | 3 +++ src/util/arguments.h | 3 ++- src/util/string_or_buffer.h | 22 +++++++++++++++++ 6 files changed, 59 insertions(+), 55 deletions(-) create mode 100644 src/util/string_or_buffer.h diff --git a/src/draft.ts b/src/draft.ts index cd159b20..d67cd808 100644 --- a/src/draft.ts +++ b/src/draft.ts @@ -41,27 +41,28 @@ interface RadioGroupOptions { export interface Radio extends Writable {} allowMethods(Radio.prototype, ["send"]) -const join = (Socket.prototype as any).join -const leave = (Socket.prototype as any).leave +const join = ( + Socket.prototype as Socket & { + join: (value: Array) => void + } +).join +const leave = ( + Socket.prototype as Socket & { + leave: (value: Array) => void + } +).leave export class Dish extends Socket { constructor(options?: SocketOptions) { super(SocketType.Dish, options) } - /* TODO: These methods might accept arrays in their C++ implementation for - the sake of simplicity. */ - join(...values: Array): void { - for (const value of values) { - join(value) - } + join(values) } leave(...values: Array): void { - for (const value of values) { - leave(value) - } + leave(values) } } diff --git a/src/outgoing_msg.cc b/src/outgoing_msg.cc index e8161986..e2e48e73 100644 --- a/src/outgoing_msg.cc +++ b/src/outgoing_msg.cc @@ -5,6 +5,7 @@ #include "./module.h" #include "util/error.h" +#include "util/string_or_buffer.h" namespace zmq { OutgoingMsg::OutgoingMsg(Napi::Value value, std::reference_wrapper module) { @@ -118,18 +119,7 @@ bool OutgoingMsg::Parts::SetGroup(Napi::Value value) { return false; } - auto group = [&]() { - if (value.IsString()) { - return std::string(value.As()); - } - if (value.IsBuffer()) { - auto buf = value.As(); - auto length = buf.As>().Length(); - auto* value = buf.As>().Data(); - return std::string(value, length); - } - return std::string(); - }(); + const auto group = convert_string_or_buffer(value); for (auto& part : parts) { if (zmq_msg_set_group(part.get(), group.c_str()) < 0) { diff --git a/src/socket.cc b/src/socket.cc index 53e91142..c3288866 100644 --- a/src/socket.cc +++ b/src/socket.cc @@ -14,6 +14,7 @@ #include "util/async_scope.h" #include "util/error.h" #include "util/object.h" +#include "util/string_or_buffer.h" #include "util/take.h" #include "util/uvdelayed.h" #include "util/uvwork.h" @@ -102,7 +103,7 @@ Socket::Socket(const Napi::CallbackInfo& info) return; } - uv_os_sock_t file_descriptor = 0; + auto file_descriptor = uv_os_sock_t{}; const auto error = [this]() { [[maybe_unused]] auto err = zmq_close(socket); @@ -675,27 +676,20 @@ Napi::Value Socket::Receive(const Napi::CallbackInfo& info) { void Socket::Join([[maybe_unused]] const Napi::CallbackInfo& info) { #ifdef ZMQ_HAS_POLLABLE_THREAD_SAFE - Arg::Validator args{ - Arg::Required("Group must be a string or buffer"), - }; - - if (args.ThrowIfInvalid(info)) { - return; + for (size_t i_value = 0; i_value < info.Length(); ++i_value) { + const auto& value = info[i_value]; + this->JoinElement(value); } +#endif +} +void Socket::JoinElement([[maybe_unused]] const Napi::Value& value) { +#ifdef ZMQ_HAS_POLLABLE_THREAD_SAFE if (!ValidateOpen()) { return; } - auto str = [&]() { - if (info[0].IsString()) { - return std::string(info[0].As()); - } - auto buf = info[0].As(); - auto length = buf.As>().Length(); - auto* value = buf.As>().Data(); - return std::string(value, length); - }(); + const auto str = convert_string_or_buffer(value); if (zmq_join(socket, str.c_str()) < 0) { ErrnoException(Env(), zmq_errno()).ThrowAsJavaScriptException(); @@ -706,27 +700,20 @@ void Socket::Join([[maybe_unused]] const Napi::CallbackInfo& info) { void Socket::Leave([[maybe_unused]] const Napi::CallbackInfo& info) { #ifdef ZMQ_HAS_POLLABLE_THREAD_SAFE - Arg::Validator args{ - Arg::Required("Group must be a string or buffer"), - }; - - if (args.ThrowIfInvalid(info)) { - return; + for (size_t i_value = 0; i_value < info.Length(); ++i_value) { + const auto& value = info[i_value]; + this->LeaveElement(value); } +#endif +} +void Socket::LeaveElement([[maybe_unused]] const Napi::Value& value) { +#ifdef ZMQ_HAS_POLLABLE_THREAD_SAFE if (!ValidateOpen()) { return; } - auto str = [&]() { - if (info[0].IsString()) { - return std::string(info[0].As()); - } - auto buf = info[0].As(); - auto length = buf.As>().Length(); - auto* value = buf.As>().Data(); - return std::string(value, length); - }(); + const auto str = convert_string_or_buffer(value); if (zmq_leave(socket, str.c_str()) < 0) { ErrnoException(Env(), zmq_errno()).ThrowAsJavaScriptException(); diff --git a/src/socket.h b/src/socket.h index 601cb3f2..b6f560a4 100644 --- a/src/socket.h +++ b/src/socket.h @@ -70,6 +70,9 @@ class Socket : public Napi::ObjectWrap, public Closable { force_inline void Send(const Napi::Promise::Deferred& res, OutgoingMsg::Parts& parts); force_inline void Receive(const Napi::Promise::Deferred& res); + inline void JoinElement(const Napi::Value& value); + inline void LeaveElement(const Napi::Value& value); + class Poller : public zmq::Poller { std::reference_wrapper socket; std::optional read_deferred; diff --git a/src/util/arguments.h b/src/util/arguments.h index bbda1cca..3dba23b7 100644 --- a/src/util/arguments.h +++ b/src/util/arguments.h @@ -88,7 +88,8 @@ class Validator { if constexpr (I == NumArgs) { if (info.Length() > NumArgs) { auto msg = "Expected " + std::to_string(NumArgs) + " argument" - + (NumArgs != 1 ? "s" : ""); + + (NumArgs != 1 ? "s" : "") + " but received " + + std::to_string(info.Length()); return Napi::TypeError::New(info.Env(), msg); } diff --git a/src/util/string_or_buffer.h b/src/util/string_or_buffer.h new file mode 100644 index 00000000..6c43b113 --- /dev/null +++ b/src/util/string_or_buffer.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +namespace zmq { + +inline std::string convert_string_or_buffer(const Napi::Value& value) { + if (value.IsString()) { + return std::string(value.As()); + } + if (value.IsBuffer()) { + auto buf = value.As(); + auto length = buf.As>().Length(); + auto* value = buf.As>().Data(); + return {value, length}; + } + throw Napi::TypeError::New(value.Env(), "Value must be a string or buffer"); +} + +} // namespace zmq