diff --git a/src/child_process.cc b/src/child_process.cc index 30b1a6878d..367dc1eb8a 100644 --- a/src/child_process.cc +++ b/src/child_process.cc @@ -73,41 +73,18 @@ ChildProcess::Write (const Arguments& args) ChildProcess *child = ObjectWrap::Unwrap(args.Holder()); assert(child); - ssize_t len; + enum encoding enc = ParseEncoding(args[1]); + ssize_t len = DecodeBytes(args[0], enc); - Local string; - Local array; - - if (args[0]->IsArray()) { - array = Local::Cast(args[0]); - len = array->Length(); - } else { - string = args[0]->ToString(); - len = string->Utf8Length(); + if (len < 0) { + Local exception = Exception::TypeError(String::New("Bad argument")); + return ThrowException(exception); } char buf[len]; - - if (args[0]->IsArray()) { - for (ssize_t index = 0; index < len; index++) { - Local int_value = array->Get(Integer::New(index)); - buf[index] = int_value->IntegerValue(); - } - } else { - switch (ParseEncoding(args[1])) { - case RAW: - case ASCII: - string->WriteAscii(buf, 0, len); - break; - - case UTF8: - string->WriteUtf8(buf, len); - break; - - default: - return ThrowException(String::New("Unknown encoding.")); - } - } + ssize_t written = DecodeWrite(buf, len, args[0], enc); + + assert(written == len); return child->Write(buf, len) == 0 ? True() : False(); } @@ -169,28 +146,10 @@ ChildProcess::on_read (evcom_reader *r, const void *buf, size_t len) HandleScope scope; bool isSTDOUT = (r == &child->stdout_reader_); - Local argv[1]; - enum encoding encoding = isSTDOUT ? child->stdout_encoding_ : child->stderr_encoding_; - if (len == 0) { - argv[0] = Local::New(Null()); - - } else if (encoding == RAW) { - // raw encoding - Local array = Array::New(len); - for (size_t i = 0; i < len; i++) { - unsigned char val = static_cast(buf)[i]; - array->Set(Integer::New(i), Integer::New(val)); - } - argv[0] = array; - - } else { - // utf8 or ascii encoding - argv[0] = String::New((const char*)buf, len); - } - - child->Emit(isSTDOUT ? "output" : "error", 1, argv); + Local data = Encode(buf, len, encoding); + child->Emit(isSTDOUT ? "output" : "error", 1, &data); child->MaybeShutdown(); } diff --git a/src/file.cc b/src/file.cc index 03037f7494..d278099889 100644 --- a/src/file.cc +++ b/src/file.cc @@ -116,34 +116,8 @@ EIOPromise::After (eio_req *req) case EIO_READ: { argc = 2; - // FIXME the following is really ugly! - if (promise->encoding_ == RAW) { - if (req->result == 0) { - argv[0] = Local::New(Null()); - argv[1] = Integer::New(0); - } else { - char *buf = reinterpret_cast(req->ptr2); - size_t len = req->result; - Local array = Array::New(len); - for (unsigned int i = 0; i < len; i++) { - unsigned char val = reinterpret_cast(buf)[i]; - array->Set(Integer::New(i), Integer::New(val)); - } - argv[0] = array; - argv[1] = Integer::New(req->result); - } - } else { - // UTF8 - if (req->result == 0) { - // eof - argv[0] = Local::New(Null()); - argv[1] = Integer::New(0); - } else { - char *buf = reinterpret_cast(req->ptr2); - argv[0] = String::New(buf, req->result); - argv[1] = Integer::New(req->result); - } - } + argv[0] = Encode(req->ptr2, req->result, promise->encoding_); + argv[1] = Integer::New(req->result); break; } @@ -304,6 +278,7 @@ Open (const Arguments& args) * 1 data the data to write (string = utf8, array = raw) * 2 position if integer, position to write at in the file. * if null, write from the current position + * 3 encoding */ static Handle Write (const Arguments& args) @@ -317,29 +292,15 @@ Write (const Arguments& args) int fd = args[0]->Int32Value(); off_t offset = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; - char *buf = NULL; - size_t len = 0; - - if (args[1]->IsString()) { - // utf8 encoding - Local string = args[1]->ToString(); - len = string->Utf8Length(); - buf = reinterpret_cast(malloc(len)); - string->WriteUtf8(buf, len); - - } else if (args[1]->IsArray()) { - // raw encoding - Local array = Local::Cast(args[1]); - len = array->Length(); - buf = reinterpret_cast(malloc(len)); - for (unsigned int i = 0; i < len; i++) { - Local int_value = array->Get(Integer::New(i)); - buf[i] = int_value->Int32Value(); - } - - } else { - return ThrowException(BAD_ARGUMENTS); + enum encoding enc = ParseEncoding(args[3]); + ssize_t len = DecodeBytes(args[1], enc); + if (len < 0) { + Local exception = Exception::TypeError(String::New("Bad argument")); + return ThrowException(exception); } + char buf[len]; + ssize_t written = DecodeWrite(buf, len, args[1], enc); + assert(written == len); return scope.Close(EIOPromise::Write(fd, buf, len, offset)); } diff --git a/src/net.cc b/src/net.cc index 6955549e60..53c914bd85 100644 --- a/src/net.cc +++ b/src/net.cc @@ -374,80 +374,30 @@ Connection::Send (const Arguments& args) return ThrowException(exception); } - // XXX - // A lot of improvement can be made here. First of all we're allocating - // evcom_bufs for every send which is clearly inefficent - it should use a - // memory pool or ring buffer. Of course, expressing binary data as an - // array of integers is extremely inefficent. This can improved when v8 - // bug 270 (http://code.google.com/p/v8/issues/detail?id=270) has been - // addressed. - - if (args[0]->IsString()) { - enum encoding enc = ParseEncoding(args[1]); - Local s = args[0]->ToString(); - size_t len = s->Utf8Length(); - char buf[len]; - switch (enc) { - case RAW: - case ASCII: - s->WriteAscii(buf, 0, len); - break; - - case UTF8: - s->WriteUtf8(buf, len); - break; - - default: - assert(0 && "unhandled string encoding"); - } - connection->Send(buf, len); - - } else if (args[0]->IsArray()) { - Handle array = Handle::Cast(args[0]); - size_t len = array->Length(); - char buf[len]; - for (size_t i = 0; i < len; i++) { - Local int_value = array->Get(Integer::New(i)); - buf[i] = int_value->IntegerValue(); - } - connection->Send(buf, len); + enum encoding enc = ParseEncoding(args[1]); + ssize_t len = DecodeBytes(args[0], enc); - } else { + if (len < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } - return Undefined(); + char buf[len]; + ssize_t written = DecodeWrite(buf, len, args[0], enc); + + assert(written == len); + + connection->Send(buf, written); + + return scope.Close(Integer::New(written)); } void Connection::OnReceive (const void *buf, size_t len) { HandleScope scope; - - const int argc = 1; - Handle argv[argc]; - - if(len) { - if (encoding_ == RAW) { - // raw encoding - Local array = Array::New(len); - for (size_t i = 0; i < len; i++) { - unsigned char val = static_cast(buf)[i]; - array->Set(Integer::New(i), Integer::New(val)); - } - argv[0] = array; - - } else { - // utf8 or ascii encoding - Handle chunk = String::New((const char*)buf, len); - argv[0] = chunk; - } - } else { - argv[0] = Local::New(Null()); - } - - Emit("receive", argc, argv); + Local data = Encode(buf, len, encoding_); + Emit("receive", 1, &data); } void diff --git a/src/node.cc b/src/node.cc index 34f35bb5c3..0cd82a1e8e 100644 --- a/src/node.cc +++ b/src/node.cc @@ -30,6 +30,89 @@ using namespace v8; using namespace node; +Local +node::Encode (const void *buf, size_t len, enum encoding encoding) +{ + HandleScope scope; + + if (!len) return scope.Close(Null()); + + if (encoding == RAW) { + // raw encoding + Local array = Array::New(len); + for (size_t i = 0; i < len; i++) { + unsigned char val = static_cast(buf)[i]; + array->Set(Integer::New(i), Integer::New(val)); + } + return scope.Close(array); + } + + // utf8 or ascii encoding + Local chunk = String::New((const char*)buf, len); + return scope.Close(chunk); +} + +// Returns -1 if the handle was not valid for decoding +ssize_t +node::DecodeBytes (v8::Handle val, enum encoding encoding) +{ + HandleScope scope; + + if (val->IsArray()) { + if (encoding != RAW) return -1; + Handle array = Handle::Cast(val); + return array->Length(); + } + + if (!val->IsString()) return -1; + + Handle string = Handle::Cast(val); + + if (encoding == UTF8) return string->Utf8Length(); + + return string->Length(); +} + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +// Returns number of bytes written. +ssize_t +node::DecodeWrite (char *buf, size_t buflen, v8::Handle val, enum encoding encoding) +{ + size_t i; + HandleScope scope; + + // XXX + // A lot of improvement can be made here. See: + // http://code.google.com/p/v8/issues/detail?id=270 + // http://groups.google.com/group/v8-dev/browse_thread/thread/dba28a81d9215291/ece2b50a3b4022c + // http://groups.google.com/group/v8-users/browse_thread/thread/1f83b0ba1f0a611 + + if (val->IsArray()) { + if (encoding != RAW) return -1; + Handle array = Handle::Cast(val); + size_t array_len = array->Length(); + for (i = 0; i < MIN(buflen, array_len); i++) { + Local int_value = array->Get(Integer::New(i)); + buf[i] = int_value->IntegerValue(); + } + return i; + } + + assert(val->IsString()); + Handle string = Handle::Cast(val); + + if (encoding == UTF8) { + string->WriteUtf8(buf, buflen); + return buflen; + } + + string->WriteAscii(buf, 0, buflen); + return buflen; +} + // Extracts a C string from a V8 Utf8Value. const char* ToCString(const v8::String::Utf8Value& value) diff --git a/src/node.h b/src/node.h index 6e18221586..a5a153cf28 100644 --- a/src/node.h +++ b/src/node.h @@ -36,5 +36,17 @@ enum encoding {ASCII, UTF8, RAW}; enum encoding ParseEncoding (v8::Handle encoding_v); void FatalException (v8::TryCatch &try_catch); +v8::Local +Encode (const void *buf, size_t len, enum encoding encoding = RAW); + +// Returns -1 if the handle was not valid for decoding +ssize_t +DecodeBytes (v8::Handle, enum encoding encoding = RAW); + +// returns bytes written. +ssize_t +DecodeWrite (char *buf, size_t buflen, v8::Handle, enum encoding encoding = RAW); + + } // namespace node #endif // node_h diff --git a/src/node_stdio.cc b/src/node_stdio.cc index c52d632cb2..cff050714d 100644 --- a/src/node_stdio.cc +++ b/src/node_stdio.cc @@ -67,41 +67,18 @@ Write (const Arguments& args) { HandleScope scope; - ssize_t len; + enum encoding enc = ParseEncoding(args[1]); + ssize_t len = DecodeBytes(args[0], enc); - Local string; - Local array; - - if (args[0]->IsArray()) { - array = Local::Cast(args[0]); - len = array->Length(); - } else { - string = args[0]->ToString(); - len = string->Utf8Length(); + if (len < 0) { + Local exception = Exception::TypeError(String::New("Bad argument")); + return ThrowException(exception); } char buf[len]; - - if (args[0]->IsArray()) { - for (ssize_t index = 0; index < len; index++) { - Local int_value = array->Get(Integer::New(index)); - buf[index] = int_value->IntegerValue(); - } - } else { - switch (ParseEncoding(args[1])) { - case RAW: - case ASCII: - string->WriteAscii(buf, 0, len); - break; - - case UTF8: - string->WriteUtf8(buf, len); - break; - - default: - return ThrowException(String::New("Unknown encoding.")); - } - } + ssize_t written = DecodeWrite(buf, len, args[0], enc); + + assert(written == len); evcom_writer_write(&out, buf, len); @@ -149,23 +126,9 @@ on_read (evcom_reader *r, const void *buf, size_t len) return; } - Local input; - - if (stdin_encoding == RAW) { - // raw encoding - Local array = Array::New(len); - for (size_t i = 0; i < len; i++) { - unsigned char val = static_cast(buf)[i]; - array->Set(Integer::New(i), Integer::New(val)); - } - input = array; - - } else { - // utf8 or ascii encoding - input = String::New((const char*)buf, len); - } + Local data = Encode(buf, len, stdin_encoding); - EmitInput(input); + EmitInput(data); } static inline int