|
22 | 22 | #include "node.h"
|
23 | 23 | #include "node_file.h"
|
24 | 24 | #include "node_buffer.h"
|
| 25 | +#include "node_internals.h" |
25 | 26 | #include "node_stat_watcher.h"
|
26 | 27 | #include "req_wrap.h"
|
| 28 | +#include "string_bytes.h" |
27 | 29 |
|
28 | 30 | #include <fcntl.h>
|
29 | 31 | #include <sys/types.h>
|
@@ -62,10 +64,12 @@ using v8::Value;
|
62 | 64 | class FSReqWrap: public ReqWrap<uv_fs_t> {
|
63 | 65 | public:
|
64 | 66 | FSReqWrap(const char* syscall)
|
65 |
| - : syscall_(syscall) { |
| 67 | + : must_free_(false), |
| 68 | + syscall_(syscall) { |
66 | 69 | }
|
67 | 70 |
|
68 | 71 | const char* syscall() { return syscall_; }
|
| 72 | + bool must_free_; // request is responsible for free'ing memory oncomplete |
69 | 73 |
|
70 | 74 | private:
|
71 | 75 | const char* syscall_;
|
@@ -97,6 +101,10 @@ static void After(uv_fs_t *req) {
|
97 | 101 | FSReqWrap* req_wrap = (FSReqWrap*) req->data;
|
98 | 102 | assert(&req_wrap->req_ == req);
|
99 | 103 |
|
| 104 | + // check if data needs to be cleaned |
| 105 | + if (req_wrap->must_free_ == true) |
| 106 | + delete[] static_cast<char*>(req_wrap->data_); |
| 107 | + |
100 | 108 | // there is always at least one argument. "error"
|
101 | 109 | int argc = 1;
|
102 | 110 |
|
@@ -485,7 +493,7 @@ static void Rename(const FunctionCallbackInfo<Value>& args) {
|
485 | 493 | if (len < 2) return TYPE_ERROR("new path required");
|
486 | 494 | if (!args[0]->IsString()) return TYPE_ERROR("old path must be a string");
|
487 | 495 | if (!args[1]->IsString()) return TYPE_ERROR("new path must be a string");
|
488 |
| - |
| 496 | + |
489 | 497 | String::Utf8Value old_path(args[0]);
|
490 | 498 | String::Utf8Value new_path(args[1]);
|
491 | 499 |
|
@@ -650,56 +658,114 @@ static void Open(const FunctionCallbackInfo<Value>& args) {
|
650 | 658 | }
|
651 | 659 | }
|
652 | 660 |
|
653 |
| -// bytesWritten = write(fd, data, position, enc, callback) |
| 661 | + |
654 | 662 | // Wrapper for write(2).
|
655 | 663 | //
|
| 664 | +// bytesWritten = write(fd, buffer, offset, length, position, callback) |
656 | 665 | // 0 fd integer. file descriptor
|
657 | 666 | // 1 buffer the data to write
|
658 | 667 | // 2 offset where in the buffer to start from
|
659 | 668 | // 3 length how much to write
|
660 | 669 | // 4 position if integer, position to write at in the file.
|
661 | 670 | // if null, write from the current position
|
662 |
| -static void Write(const FunctionCallbackInfo<Value>& args) { |
| 671 | +static void WriteBuffer(const FunctionCallbackInfo<Value>& args) { |
663 | 672 | HandleScope scope(node_isolate);
|
664 | 673 |
|
665 |
| - if (!args[0]->IsInt32()) { |
666 |
| - return THROW_BAD_ARGS; |
667 |
| - } |
| 674 | + assert(args[0]->IsInt32()); |
| 675 | + assert(Buffer::HasInstance(args[1])); |
668 | 676 |
|
669 | 677 | int fd = args[0]->Int32Value();
|
| 678 | + Local<Object> obj = args[1].As<Object>(); |
| 679 | + const char* buf = Buffer::Data(obj); |
| 680 | + size_t buffer_length = Buffer::Length(obj); |
| 681 | + size_t off = args[2]->Uint32Value(); |
| 682 | + size_t len = args[3]->Uint32Value(); |
| 683 | + int64_t pos = GET_OFFSET(args[4]); |
| 684 | + Local<Value> cb = args[5]; |
670 | 685 |
|
671 |
| - if (!Buffer::HasInstance(args[1])) { |
672 |
| - return ThrowError("Second argument needs to be a buffer"); |
673 |
| - } |
| 686 | + if (off > buffer_length) |
| 687 | + return ThrowRangeError("offset out of bounds"); |
| 688 | + if (len > buffer_length) |
| 689 | + return ThrowRangeError("length out of bounds"); |
| 690 | + if (off + len < off) |
| 691 | + return ThrowRangeError("off + len overflow"); |
| 692 | + if (off + len > buffer_length) |
| 693 | + return ThrowRangeError("off + len > buffer.length"); |
674 | 694 |
|
675 |
| - Local<Object> buffer_obj = args[1]->ToObject(); |
676 |
| - char *buffer_data = Buffer::Data(buffer_obj); |
677 |
| - size_t buffer_length = Buffer::Length(buffer_obj); |
| 695 | + buf += off; |
678 | 696 |
|
679 |
| - size_t off = args[2]->Int32Value(); |
680 |
| - if (off >= buffer_length) { |
681 |
| - return ThrowError("Offset is out of bounds"); |
| 697 | + if (cb->IsFunction()) { |
| 698 | + ASYNC_CALL(write, cb, fd, buf, len, pos) |
| 699 | + return; |
682 | 700 | }
|
683 | 701 |
|
684 |
| - ssize_t len = args[3]->Int32Value(); |
685 |
| - if (off + len > buffer_length) { |
686 |
| - return ThrowError("off + len > buffer.length"); |
687 |
| - } |
| 702 | + SYNC_CALL(write, NULL, fd, buf, len, pos) |
| 703 | + args.GetReturnValue().Set(SYNC_RESULT); |
| 704 | +} |
688 | 705 |
|
689 |
| - ASSERT_OFFSET(args[4]); |
690 |
| - int64_t pos = GET_OFFSET(args[4]); |
691 | 706 |
|
692 |
| - char * buf = (char*)buffer_data + off; |
693 |
| - Local<Value> cb = args[5]; |
| 707 | +// Wrapper for write(2). |
| 708 | +// |
| 709 | +// bytesWritten = write(fd, string, position, enc, callback) |
| 710 | +// 0 fd integer. file descriptor |
| 711 | +// 1 string non-buffer values are converted to strings |
| 712 | +// 2 position if integer, position to write at in the file. |
| 713 | +// if null, write from the current position |
| 714 | +// 3 enc encoding of string |
| 715 | +static void WriteString(const FunctionCallbackInfo<Value>& args) { |
| 716 | + HandleScope scope(node_isolate); |
| 717 | + |
| 718 | + if (!args[0]->IsInt32()) |
| 719 | + return ThrowTypeError("First argument must be file descriptor"); |
| 720 | + |
| 721 | + Local<Value> cb; |
| 722 | + Local<Value> string = args[1]; |
| 723 | + int fd = args[0]->Int32Value(); |
| 724 | + char* buf = NULL; |
| 725 | + int64_t pos; |
| 726 | + size_t len; |
| 727 | + bool must_free_ = false; |
| 728 | + |
| 729 | + // will assign buf and len if string was external |
| 730 | + if (!StringBytes::GetExternalParts(string, |
| 731 | + const_cast<const char**>(&buf), |
| 732 | + &len)) { |
| 733 | + enum encoding enc = ParseEncoding(args[3], UTF8); |
| 734 | + len = StringBytes::StorageSize(string, enc); |
| 735 | + buf = new char[len]; |
| 736 | + // StorageSize may return too large a char, so correct the actual length |
| 737 | + // by the write size |
| 738 | + len = StringBytes::Write(buf, len, args[1], enc); |
| 739 | + must_free_ = true; |
| 740 | + } |
| 741 | + pos = GET_OFFSET(args[2]); |
| 742 | + cb = args[4]; |
694 | 743 |
|
695 | 744 | if (cb->IsFunction()) {
|
696 |
| - ASYNC_CALL(write, cb, fd, buf, len, pos) |
697 |
| - } else { |
698 |
| - SYNC_CALL(write, 0, fd, buf, len, pos) |
699 |
| - args.GetReturnValue().Set(SYNC_RESULT); |
| 745 | + FSReqWrap* req_wrap = new FSReqWrap("write"); |
| 746 | + int err = uv_fs_write(uv_default_loop(), &req_wrap->req_, |
| 747 | + fd, buf, len, pos, After); |
| 748 | + req_wrap->object()->Set(oncomplete_sym, cb); |
| 749 | + req_wrap->must_free_ = must_free_; |
| 750 | + req_wrap->Dispatched(); |
| 751 | + req_wrap->data_ = buf; |
| 752 | + if (err < 0) { |
| 753 | + uv_fs_t* req = &req_wrap->req_; |
| 754 | + req->result = err; |
| 755 | + req->path = NULL; |
| 756 | + After(req); |
| 757 | + } |
| 758 | + return args.GetReturnValue().Set(req_wrap->persistent()); |
700 | 759 | }
|
| 760 | + |
| 761 | + SYNC_CALL(write, NULL, fd, buf, len, pos) |
| 762 | + args.GetReturnValue().Set(SYNC_RESULT); |
| 763 | + |
| 764 | + if (must_free_) |
| 765 | + delete[] buf; |
701 | 766 | }
|
702 | 767 |
|
| 768 | + |
703 | 769 | /*
|
704 | 770 | * Wrapper for read(2).
|
705 | 771 | *
|
@@ -918,7 +984,8 @@ void File::Initialize(Handle<Object> target) {
|
918 | 984 | NODE_SET_METHOD(target, "symlink", Symlink);
|
919 | 985 | NODE_SET_METHOD(target, "readlink", ReadLink);
|
920 | 986 | NODE_SET_METHOD(target, "unlink", Unlink);
|
921 |
| - NODE_SET_METHOD(target, "write", Write); |
| 987 | + NODE_SET_METHOD(target, "writeBuffer", WriteBuffer); |
| 988 | + NODE_SET_METHOD(target, "writeString", WriteString); |
922 | 989 |
|
923 | 990 | NODE_SET_METHOD(target, "chmod", Chmod);
|
924 | 991 | NODE_SET_METHOD(target, "fchmod", FChmod);
|
|
0 commit comments