Index: native_client_sdk/src/libraries/nacl_io/devfs/jspipe_event_emitter.cc |
diff --git a/native_client_sdk/src/libraries/nacl_io/devfs/jspipe_event_emitter.cc b/native_client_sdk/src/libraries/nacl_io/devfs/jspipe_event_emitter.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cc39414e115d0f5ae0dc6b6399d680c2f2806d6f |
--- /dev/null |
+++ b/native_client_sdk/src/libraries/nacl_io/devfs/jspipe_event_emitter.cc |
@@ -0,0 +1,308 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "nacl_io/devfs/jspipe_event_emitter.h" |
+ |
+#include <assert.h> |
+#include <errno.h> |
+#include <string.h> |
+ |
+#include <algorithm> |
+ |
+#define TRACE(format, ...) \ |
+ LOG_TRACE("jspipe[%s]: " format, name_.c_str(), ##__VA_ARGS__) |
+#define ERROR(format, ...) \ |
+ LOG_ERROR("jspipe[%s]: " format, name_.c_str(), ##__VA_ARGS__) |
+ |
+#include "nacl_io/log.h" |
+#include "nacl_io/osinttypes.h" |
+#include "nacl_io/pepper_interface.h" |
+ |
+namespace { |
+const size_t kMaxPostMessageSize = 64*1024; |
+const char* kDictKeyPipe = "pipe"; |
+const char* kDictKeyOperation = "operation"; |
+const char* kDictKeyPayload = "payload"; |
+const char* kOperationNameAck = "ack"; |
+const char* kOperationNameWrite = "write"; |
+} |
+ |
+namespace nacl_io { |
+ |
+JSPipeEventEmitter::JSPipeEventEmitter(PepperInterface* ppapi, size_t size) |
+ : input_fifo_(size), |
+ post_message_buffer_size_(size), |
+ bytes_sent_(0), |
+ bytes_acked_(0), |
+ bytes_read_(0), |
+ ppapi_(ppapi), |
+ messaging_iface_(NULL), |
+ var_iface_(NULL), |
+ array_iface_(NULL), |
+ buffer_iface_(NULL), |
+ dict_iface_(NULL), |
+ pipe_name_var_(PP_MakeUndefined()), |
+ pipe_key_(PP_MakeUndefined()), |
+ operation_key_(PP_MakeUndefined()), |
+ payload_key_(PP_MakeUndefined()), |
+ write_var_(PP_MakeUndefined()), |
+ ack_var_(PP_MakeUndefined()) { |
+ UpdateStatus_Locked(); |
+ if (ppapi == NULL) { |
+ TRACE("missing PPAPI provider"); |
+ return; |
+ } |
+ messaging_iface_ = ppapi->GetMessagingInterface(); |
+ var_iface_ = ppapi->GetVarInterface(); |
+ array_iface_ = ppapi->GetVarArrayInterface(); |
+ buffer_iface_ = ppapi->GetVarArrayBufferInterface(); |
+ dict_iface_ = ppapi->GetVarDictionaryInterface(); |
+ |
+ if (var_iface_ == NULL) |
+ return; |
+ |
+ pipe_key_ = VarFromCStr(kDictKeyPipe); |
+ operation_key_ = VarFromCStr(kDictKeyOperation); |
+ payload_key_ = VarFromCStr(kDictKeyPayload); |
+ write_var_ = VarFromCStr(kOperationNameWrite); |
+ ack_var_ = VarFromCStr(kOperationNameAck); |
+} |
+ |
+void JSPipeEventEmitter::Destroy() { |
+ if (var_iface_ == NULL) |
+ return; |
+ var_iface_->Release(pipe_name_var_); |
+ var_iface_->Release(pipe_key_); |
+ var_iface_->Release(operation_key_); |
+ var_iface_->Release(payload_key_); |
+ var_iface_->Release(write_var_); |
+ var_iface_->Release(ack_var_); |
+} |
+ |
+PP_Var JSPipeEventEmitter::VarFromCStr(const char* string) { |
+ assert(var_iface_); |
+ return var_iface_->VarFromUtf8(string, strlen(string)); |
+} |
+ |
+void JSPipeEventEmitter::UpdateStatus_Locked() { |
+ uint32_t status = 0; |
+ if (!input_fifo_.IsEmpty()) |
+ status |= POLLIN; |
+ |
+ if (GetOSpace() > 0) |
+ status |= POLLOUT; |
+ |
+ ClearEvents_Locked(~status); |
+ RaiseEvents_Locked(status); |
+} |
+ |
+Error JSPipeEventEmitter::Read_Locked(char* data, size_t len, int* out_bytes) { |
+ *out_bytes = input_fifo_.Read(data, len); |
+ if (*out_bytes > 0) { |
+ bytes_read_ += *out_bytes; |
+ Error err = SendAckMessage(bytes_read_); |
+ if (err != 0) |
+ ERROR("Sending ACK failed: %d\n", err.error); |
+ } |
+ |
+ UpdateStatus_Locked(); |
+ return 0; |
+} |
+ |
+Error JSPipeEventEmitter::SendWriteMessage(const void* buf, size_t count) { |
+ TRACE("SendWriteMessage [%"PRIuS"] total=%"PRIuS, count, bytes_sent_); |
+ if (!var_iface_ || !buffer_iface_) |
+ return EIO; |
+ |
+ // Copy payload data in a new ArrayBuffer |
+ PP_Var buffer = buffer_iface_->Create(count); |
+ memcpy(buffer_iface_->Map(buffer), buf, count); |
+ buffer_iface_->Unmap(buffer); |
+ |
+ Error rtn = SendMessageToJS(write_var_, buffer); |
+ var_iface_->Release(buffer); |
+ return rtn; |
+} |
+ |
+Error JSPipeEventEmitter::SetName(const char* name) { |
+ if (var_iface_ == NULL) |
+ return EIO; |
+ |
+ // name can only be set once |
+ if (!name_.empty()) |
+ return EIO; |
+ |
+ // new name must not be empty |
+ if (!name || strlen(name) == 0) |
+ return EIO; |
+ |
+ TRACE("set name: %s", name); |
+ name_ = name; |
+ pipe_name_var_ = VarFromCStr(name); |
+ return 0; |
+} |
+ |
+Error JSPipeEventEmitter::SendMessageToJS(PP_Var operation, |
+ PP_Var payload) { |
+ if (!ppapi_ || !messaging_iface_ || !var_iface_ || !dict_iface_) |
+ return EIO; |
+ |
+ // Create dict object which will be sent to JavaScript. |
+ PP_Var dict = dict_iface_->Create(); |
+ |
+ // Set try keys in the dictionaty: 'pipe', 'operation', and 'payload' |
+ dict_iface_->Set(dict, pipe_key_, pipe_name_var_); |
binji
2014/05/08 18:18:33
nice
|
+ dict_iface_->Set(dict, operation_key_, operation); |
+ dict_iface_->Set(dict, payload_key_, payload); |
+ |
+ // Send the dict via PostMessage |
+ messaging_iface_->PostMessage(ppapi_->GetInstance(), dict); |
+ |
+ // Release the dict |
+ var_iface_->Release(dict); |
+ return 0; |
+} |
+ |
+Error JSPipeEventEmitter::SendAckMessage(size_t byte_count) { |
+ TRACE("SendAckMessage %"PRIuS, byte_count); |
+ PP_Var payload; |
+ payload.type = PP_VARTYPE_INT32; |
+ payload.value.as_int = (int32_t)byte_count; |
+ |
+ return SendMessageToJS(ack_var_, payload); |
+} |
+ |
+size_t JSPipeEventEmitter::HandleJSWrite(const char* data, size_t len) { |
+ size_t out_len = input_fifo_.Write(data, len); |
+ UpdateStatus_Locked(); |
+ return out_len; |
+} |
+ |
+void JSPipeEventEmitter::HandleJSAck(size_t byte_count) { |
+ if (byte_count > bytes_sent_) { |
+ ERROR("HandleAck unexpected byte count: %"PRIuS, byte_count); |
+ return; |
+ } |
+ |
+ bytes_acked_ = byte_count; |
+ TRACE("HandleAck: %" SCNuS "/%" PRIuS, bytes_acked_, bytes_sent_); |
binji
2014/05/08 18:18:33
PRIuS, SCNuS is for scanf
|
+ UpdateStatus_Locked(); |
+} |
+ |
+Error JSPipeEventEmitter::HandleJSWrite(struct PP_Var message) { |
+ TRACE("HandleJSWrite"); |
+ if (message.type != PP_VARTYPE_ARRAY_BUFFER) { |
+ TRACE("HandleJSWrite expected ArrayBuffer but got %d.", message.type); |
+ return EINVAL; |
+ } |
+ uint32_t length; |
+ if (buffer_iface_->ByteLength(message, &length) != PP_TRUE) |
+ return EINVAL; |
+ |
+ char* buffer = (char*)buffer_iface_->Map(message); |
+ |
+ // Write data to the input fifo |
+ size_t wrote = HandleJSWrite(buffer, length); |
+ buffer_iface_->Unmap(message); |
+ if (wrote != length) { |
+ LOG_ERROR("Only wrote %d of %d bytes to pipe", (int)wrote, (int)length); |
binji
2014/05/08 18:18:33
why cast?
|
+ return EIO; |
+ } |
+ TRACE("done HandleWrite: %d", length); |
+ return 0; |
+} |
+ |
+Error JSPipeEventEmitter::HandleJSAck(PP_Var message) { |
+ if (message.type != PP_VARTYPE_INT32) { |
+ TRACE("HandleAck integer object expected but got %d.", message.type); |
+ return EINVAL; |
+ } |
+ HandleJSAck(message.value.as_int); |
+ return 0; |
+} |
+ |
+int JSPipeEventEmitter::VarStrcmp(PP_Var a, PP_Var b) { |
+ uint32_t length_a = 0; |
+ uint32_t length_b = 0; |
+ const char* cstring_a = var_iface_->VarToUtf8(a, &length_a); |
+ const char* cstring_b = var_iface_->VarToUtf8(a, &length_b); |
+ std::string string_a(cstring_a, length_a); |
binji
2014/05/08 18:18:33
seems a bit cheesy to create a std::string just to
|
+ std::string string_b(cstring_b, length_a); |
+ return strcmp(string_a.c_str(), string_b.c_str()); |
+} |
+ |
+Error JSPipeEventEmitter::HandleJSMessage(struct PP_Var message) { |
+ Error err = 0; |
+ if (!messaging_iface_ || !var_iface_ || !dict_iface_ || !buffer_iface_) { |
+ TRACE("HandleJSMessage: missing PPAPI interfaces"); |
+ return ENOSYS; |
+ } |
+ |
+ // Verify that we have an array with size two. |
+ if (message.type != PP_VARTYPE_DICTIONARY) { |
+ TRACE("HandleJSMessage passed non-dictionary var"); |
+ return EINVAL; |
+ } |
+ |
+#ifndef NDEBUG |
+ PP_Var pipe_name_var = dict_iface_->Get(message, pipe_key_); |
+ if (VarStrcmp(pipe_name_var, pipe_name_var_)) { |
+ TRACE("HandleJSMessage wrong pipe name"); |
+ return EINVAL; |
+ } |
+ var_iface_->Release(pipe_name_var); |
+#endif |
+ |
+ PP_Var operation_var = dict_iface_->Get(message, operation_key_); |
+ if (operation_var.type != PP_VARTYPE_STRING) { |
+ TRACE("HandleJSMessage invalid operation"); |
+ err = EINVAL; |
+ } else { |
+ uint32_t length; |
+ const char* operation_string; |
+ operation_string = var_iface_->VarToUtf8(operation_var, &length); |
+ std::string message_type(operation_string, length); |
+ |
+ TRACE("HandleJSMessage %s", message_type.c_str()); |
+ PP_Var payload = dict_iface_->Get(message, payload_key_); |
+ if (message_type == kOperationNameWrite) { |
+ err = HandleJSWrite(payload); |
+ } else if (message_type == kOperationNameAck) { |
+ err = HandleJSAck(payload); |
+ } else { |
+ TRACE("Unknown message type: %s", message_type.c_str()); |
+ err = EINVAL; |
+ } |
+ var_iface_->Release(payload); |
+ } |
+ |
+ var_iface_->Release(operation_var); |
+ return err; |
+} |
+ |
+Error JSPipeEventEmitter::Write_Locked(const char* data, size_t len, |
+ int* out_bytes) { |
+ if (GetOSpace() == 0) { |
+ *out_bytes = 0; |
+ return 0; |
+ } |
+ |
+ if (len > GetOSpace()) |
+ len = GetOSpace(); |
+ |
+ // Limit the size of the data we send with PostMessage to kMaxPostMessageSize |
+ if (len > kMaxPostMessageSize) |
+ len = kMaxPostMessageSize; |
+ |
+ Error err = SendWriteMessage(data, len); |
+ if (err != 0) |
+ return err; |
+ *out_bytes = len; |
+ bytes_sent_ += len; |
+ |
+ UpdateStatus_Locked(); |
+ return 0; |
+} |
+ |
+} // namespace nacl_io |