From d1b0ce6d37accf5e0007df8f975d487f173b72fa Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 26 May 2009 03:30:51 +0200 Subject: [PATCH] Large refactor of file code. All the c++ code is now reduced to simple wrappers. The node.fs.File object is defined entirely in javascript now. As is the actionQueue methods. This makes the boundaries much cleaner. There is still some thought that needs to go into how exactly the API should behave but this simplification is a first step. --- src/file.cc | 673 ++++++++++++++++++++++++++---------------------------------- src/file.h | 44 +--- src/file.js | 215 ++++++++++--------- src/node.h | 2 + src/node.js | 2 +- 5 files changed, 421 insertions(+), 515 deletions(-) diff --git a/src/file.cc b/src/file.cc index c8f6fae..d5f261d 100644 --- a/src/file.cc +++ b/src/file.cc @@ -12,490 +12,405 @@ using namespace v8; using namespace node; -#define FD_SYMBOL String::NewSymbol("fd") -#define ACTION_QUEUE_SYMBOL String::NewSymbol("_actionQueue") -#define ENCODING_SYMBOL String::NewSymbol("encoding") -#define CALLBACK_SYMBOL String::NewSymbol("callbaccallback") -#define POLL_ACTIONS_SYMBOL String::NewSymbol("_pollActions") - -#define UTF8_SYMBOL String::NewSymbol("utf8") -#define RAW_SYMBOL String::NewSymbol("raw") - -void -File::Initialize (Handle target) +#define DEV_SYMBOL String::NewSymbol("dev") +#define INO_SYMBOL String::NewSymbol("ino") +#define MODE_SYMBOL String::NewSymbol("mode") +#define NLINK_SYMBOL String::NewSymbol("nlink") +#define UID_SYMBOL String::NewSymbol("uid") +#define GID_SYMBOL String::NewSymbol("gid") +#define RDEV_SYMBOL String::NewSymbol("rdev") +#define SIZE_SYMBOL String::NewSymbol("size") +#define BLKSIZE_SYMBOL String::NewSymbol("blksize") +#define BLOCKS_SYMBOL String::NewSymbol("blocks") +#define ATIME_SYMBOL String::NewSymbol("atime") +#define MTIME_SYMBOL String::NewSymbol("mtime") +#define CTIME_SYMBOL String::NewSymbol("ctime") +#define BAD_ARGUMENTS String::New("Bad argument") + +#define MAKE_CALLBACK_PTR \ + Persistent *callback = NULL; \ + Local last_arg = args[args.Length()-1]; \ + if (last_arg->IsFunction()) { \ + Local l = Local::Cast(last_arg); \ + callback = new Persistent(); \ + *callback = Persistent::New(l); \ + } \ + node::eio_warmup(); \ + +#define CALL_CALLBACK_PTR(req, argc, argv) \ +do { \ + if (req->data) { \ + Persistent *callback = \ + reinterpret_cast*>(req->data); \ + TryCatch try_catch; \ + (*callback)->Call(Context::GetCurrent()->Global(), argc, argv); \ + if(try_catch.HasCaught()) \ + node::fatal_exception(try_catch); \ + free(callback); \ + } \ +} while(0) + +#define DEFINE_SIMPLE_CB(name) \ +static int After##name (eio_req *req) \ +{ \ + HandleScope scope; \ + Local argv[] = { Integer::New(req->errorno) }; \ + CALL_CALLBACK_PTR(req, 1, argv); \ + return 0; \ +} \ + +DEFINE_SIMPLE_CB(Close) +static Handle Close (const Arguments& args) { + if (args.Length() < 1 || !args[0]->IsInt32()) + return ThrowException(BAD_ARGUMENTS); HandleScope scope; - - // file system methods - NODE_SET_METHOD(target, "rename", FileSystem::Rename); - NODE_SET_METHOD(target, "stat", FileSystem::Stat); - NODE_SET_METHOD(target, "strerror", FileSystem::StrError); - - target->Set(String::NewSymbol("STDIN_FILENO"), Integer::New(STDIN_FILENO)); - target->Set(String::NewSymbol("STDOUT_FILENO"), Integer::New(STDOUT_FILENO)); - target->Set(String::NewSymbol("STDERR_FILENO"), Integer::New(STDERR_FILENO)); - - - Local file_template = FunctionTemplate::New(File::New); - file_template->InstanceTemplate()->SetInternalFieldCount(1); - - // file methods - NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_open", File::Open); - NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_close", File::Close); - NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_write", File::Write); - NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_read", File::Read); - file_template->InstanceTemplate()->SetAccessor(ENCODING_SYMBOL, File::GetEncoding, File::SetEncoding); - target->Set(String::NewSymbol("File"), file_template->GetFunction()); -} - -Handle -File::GetEncoding (Local property, const AccessorInfo& info) -{ - File *file = NODE_UNWRAP(File, info.This()); - - if (file->encoding_ == UTF8) - return UTF8_SYMBOL; - else - return RAW_SYMBOL; -} - -void -File::SetEncoding (Local property, Local value, const AccessorInfo& info) -{ - File *file = NODE_UNWRAP(File, info.This()); - - if (value->IsString()) { - Local encoding_string = value->ToString(); - char buf[5]; // need enough room for "utf8" or "raw" - encoding_string->WriteAscii(buf, 0, 4); - buf[4] = '\0'; - file->encoding_ = strcasecmp(buf, "utf8") == 0 ? UTF8 : RAW; - } else { - file->encoding_ = RAW; - } -} - -static void -CallTopCallback (Handle handle, const int argc, Handle argv[]) -{ - HandleScope scope; - - // poll_actions - Local poll_actions_value = handle->Get(POLL_ACTIONS_SYMBOL); - assert(poll_actions_value->IsFunction()); - Handle poll_actions = Handle::Cast(poll_actions_value); - - TryCatch try_catch; - poll_actions->Call(handle, argc, argv); - if(try_catch.HasCaught()) - node::fatal_exception(try_catch); + int fd = args[0]->Int32Value(); + MAKE_CALLBACK_PTR + eio_close(fd, EIO_PRI_DEFAULT, AfterClose, callback); + return Undefined(); } -Handle -FileSystem::Rename (const Arguments& args) +DEFINE_SIMPLE_CB(Rename) +static Handle Rename (const Arguments& args) { - if (args.Length() < 2) - return Undefined(); - + if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) + return ThrowException(BAD_ARGUMENTS); HandleScope scope; - String::Utf8Value path(args[0]->ToString()); String::Utf8Value new_path(args[1]->ToString()); - - Persistent *callback = NULL; - if (args[2]->IsFunction()) { - Local l = Local::Cast(args[2]); - callback = new Persistent(); - *callback = Persistent::New(l); - } - - node::eio_warmup(); - eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, NULL); - + MAKE_CALLBACK_PTR + eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, callback); return Undefined(); } -int -FileSystem::AfterRename (eio_req *req) +static int +AfterOpen (eio_req *req) { HandleScope scope; - const int argc = 1; + const int argc = 2; Local argv[argc]; argv[0] = Integer::New(req->errorno); - - if (req->data) { - Persistent *callback = reinterpret_cast*>(req->data); - - TryCatch try_catch; - (*callback)->Call(Context::GetCurrent()->Global(), argc, argv); - if(try_catch.HasCaught()) - node::fatal_exception(try_catch); - - free(callback); - } - + argv[1] = Integer::New(req->result); + CALL_CALLBACK_PTR(req, argc, argv); return 0; } -Handle -FileSystem::Stat (const Arguments& args) +static Handle +Open (const Arguments& args) { - if (args.Length() < 1) - return v8::Undefined(); + if ( args.Length() < 3 + || !args[0]->IsString() + || !args[1]->IsInt32() + || !args[2]->IsInt32() + ) return ThrowException(BAD_ARGUMENTS); HandleScope scope; - String::Utf8Value path(args[0]->ToString()); + int flags = args[1]->Int32Value(); + mode_t mode = static_cast(args[2]->Int32Value()); - Persistent *callback = NULL; - if (args[1]->IsFunction()) { - Local l = Local::Cast(args[1]); - callback = new Persistent(); - *callback = Persistent::New(l); - } - - node::eio_warmup(); - eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback); + MAKE_CALLBACK_PTR + eio_open(*path, flags, mode, EIO_PRI_DEFAULT, AfterOpen, callback); return Undefined(); } -int -FileSystem::AfterStat (eio_req *req) +static int +AfterWrite (eio_req *req) { HandleScope scope; + free(req->ptr2); + + ssize_t written = req->result; + const int argc = 2; Local argv[argc]; argv[0] = Integer::New(req->errorno); + argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0); - Local stats = Object::New(); - argv[1] = stats; - - if (req->result == 0) { - struct stat *s = reinterpret_cast(req->ptr2); - - /* ID of device containing file */ - stats->Set(NODE_SYMBOL("dev"), Integer::New(s->st_dev)); - /* inode number */ - stats->Set(NODE_SYMBOL("ino"), Integer::New(s->st_ino)); - /* protection */ - stats->Set(NODE_SYMBOL("mode"), Integer::New(s->st_mode)); - /* number of hard links */ - stats->Set(NODE_SYMBOL("nlink"), Integer::New(s->st_nlink)); - /* user ID of owner */ - stats->Set(NODE_SYMBOL("uid"), Integer::New(s->st_uid)); - /* group ID of owner */ - stats->Set(NODE_SYMBOL("gid"), Integer::New(s->st_gid)); - /* device ID (if special file) */ - stats->Set(NODE_SYMBOL("rdev"), Integer::New(s->st_rdev)); - /* total size, in bytes */ - stats->Set(NODE_SYMBOL("size"), Integer::New(s->st_size)); - /* blocksize for filesystem I/O */ - stats->Set(NODE_SYMBOL("blksize"), Integer::New(s->st_blksize)); - /* number of blocks allocated */ - stats->Set(NODE_SYMBOL("blocks"), Integer::New(s->st_blocks)); - /* time of last access */ - stats->Set(NODE_SYMBOL("atime"), Date::New(1000*static_cast(s->st_atime))); - /* time of last modification */ - stats->Set(NODE_SYMBOL("mtime"), Date::New(1000*static_cast(s->st_mtime))); - /* time of last status change */ - stats->Set(NODE_SYMBOL("ctime"), Date::New(1000*static_cast(s->st_ctime))); - } - - if (req->data) { - Persistent *callback = reinterpret_cast*>(req->data); - - TryCatch try_catch; - (*callback)->Call(Context::GetCurrent()->Global(), argc, argv); - if(try_catch.HasCaught()) - node::fatal_exception(try_catch); - - free(callback); - } - + CALL_CALLBACK_PTR(req, argc, argv); return 0; } -Handle -FileSystem::StrError (const Arguments& args) + +/* node.fs.write(fd, data, position, callback) + * Wrapper for write(2). + * + * 0 fd integer. file descriptor + * 1 data the data to write + * 2 position if integer, position to write at in the file. + * if null, write from the current position + * + * 3 callback(errorno, written) + */ +static Handle +Write (const Arguments& args) { - if (args.Length() < 1) return v8::Undefined(); - if (!args[0]->IsNumber()) return v8::Undefined(); + if ( args.Length() < 3 + || !args[0]->IsInt32() + ) return ThrowException(BAD_ARGUMENTS); HandleScope scope; - int errorno = args[0]->IntegerValue(); - - Local message = String::New(strerror(errorno)); - - return scope.Close(message); -} - -///////////////////// FILE ///////////////////// + int fd = args[0]->Int32Value(); + off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; -File::File (Handle handle) - : ObjectWrap(handle) -{ - HandleScope scope; - encoding_ = RAW; - handle->Set(ACTION_QUEUE_SYMBOL, Array::New()); -} + char *buf = NULL; + size_t len = 0; -File::~File () -{ - ; // XXX call close? -} + 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(); + } -bool -File::HasUtf8Encoding (void) -{ - return false; -} + } else { + return ThrowException(BAD_ARGUMENTS); + } -int -File::GetFD (void) -{ - Handle fd_value = handle_->Get(FD_SYMBOL); - int fd = fd_value->IntegerValue(); - return fd; + MAKE_CALLBACK_PTR + eio_write(fd, buf, len, pos, EIO_PRI_DEFAULT, AfterWrite, callback); + return Undefined(); } -Handle -File::Close (const Arguments& args) +static int +AfterUtf8Read (eio_req *req) { HandleScope scope; - File *file = NODE_UNWRAP(File, args.Holder()); - - int fd = file->GetFD(); - - node::eio_warmup(); - eio_close (fd, EIO_PRI_DEFAULT, File::AfterClose, file); - file->Attach(); - return Undefined(); -} + const int argc = 2; + Local argv[argc]; + argv[0] = Integer::New(req->errorno); -int -File::AfterClose (eio_req *req) -{ - File *file = reinterpret_cast(req->data); + char *buf = reinterpret_cast(req->ptr2); - if (req->result == 0) { - file->handle_->Delete(FD_SYMBOL); + if (req->result == 0) { + // eof + argv[1] = Local::New(Null()); + } else { + size_t len = req->result; + argv[1] = String::New(buf, len); } - const int argc = 1; - Local argv[argc]; - argv[0] = Integer::New(req->errorno); - CallTopCallback(file->handle_, argc, argv); - file->Detach(); + CALL_CALLBACK_PTR(req, argc, argv); return 0; } -Handle -File::Open (const Arguments& args) +static int +AfterRawRead(eio_req *req) { - /* check arguments */ - if (args.Length() < 1) return Undefined(); - if (!args[0]->IsString()) return Undefined(); - HandleScope scope; - File *file = NODE_UNWRAP(File, args.Holder()); - - // make sure that we don't already have a pending open - if (file->handle_->Has(FD_SYMBOL)) { - return ThrowException(String::New("File object is opened.")); - } + const int argc = 2; + Local argv[argc]; + argv[0] = Integer::New(req->errorno); - String::Utf8Value path(args[0]->ToString()); + char *buf = reinterpret_cast(req->ptr2); - int flags = O_RDONLY; // default - if (args[1]->IsString()) { - String::AsciiValue mode_v(args[1]->ToString()); - char *mode = *mode_v; - // XXX is this interpretation of the mode correct? - // I don't want to to use fopen() directly because eio doesn't support it. - switch(mode[0]) { - case 'r': - flags = (mode[1] == '+' ? O_RDWR : O_RDONLY); - break; - case 'w': - flags = O_CREAT | O_TRUNC | (mode[1] == '+' ? O_RDWR : O_WRONLY); - break; - case 'a': - flags = O_APPEND | O_CREAT | (mode[1] == '+' ? O_RDWR : O_WRONLY); - break; + if (req->result == 0) { + // eof + argv[1] = Local::New(Null()); + } else { + // raw encoding + size_t len = req->result; + Local array = Array::New(len); + for (unsigned int i = 0; i < len; i++) { + array->Set(Integer::New(i), Integer::New(buf[i])); } + argv[1] = array; } - // TODO how should the mode be set? - node::eio_warmup(); - eio_open (*path, flags, 0666, EIO_PRI_DEFAULT, File::AfterOpen, file); - file->Attach(); - return Undefined(); -} - -int -File::AfterOpen (eio_req *req) -{ - File *file = reinterpret_cast(req->data); - HandleScope scope; - - if(req->result >= 0) { - file->handle_->Set(FD_SYMBOL, Integer::New(req->result)); - } - - const int argc = 1; - Handle argv[argc]; - argv[0] = Integer::New(req->errorno); - CallTopCallback(file->handle_, argc, argv); - - file->Detach(); + CALL_CALLBACK_PTR(req, argc, argv); return 0; } -Handle -File::Write (const Arguments& args) +/* node.fs.read(fd, length, position, encoding, callback) + * Wrapper for read(2). + * + * 0 fd integer. file descriptor + * 1 length integer. length to read + * 2 position if integer, position to read from in the file. + * if null, read from the current position + * 3 encoding either node.fs.UTF8 or node.fs.RAW + * + * 4 callback(errorno, data) + */ +static Handle +Read (const Arguments& args) { - if (args.Length() < 2) return Undefined(); - if (!args[1]->IsNumber()) return Undefined(); + if ( args.Length() < 3 + || !args[0]->IsInt32() // fd + || !args[1]->IsNumber() // len + ) return ThrowException(BAD_ARGUMENTS); HandleScope scope; - File *file = NODE_UNWRAP(File, args.Holder()); + int fd = args[0]->Int32Value(); + size_t len = args[1]->IntegerValue(); + off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; - off_t pos = args[1]->IntegerValue(); - - char *buf = NULL; - size_t length = 0; - - if (args[0]->IsString()) { - // utf8 encoding - Local string = args[0]->ToString(); - length = string->Utf8Length(); - buf = reinterpret_cast(malloc(length)); - string->WriteUtf8(buf, length); - - } else if (args[0]->IsArray()) { - // raw encoding - Local array = Local::Cast(args[0]); - length = array->Length(); - buf = reinterpret_cast(malloc(length)); - for (unsigned int i = 0; i < length; i++) { - Local int_value = array->Get(Integer::New(i)); - buf[i] = int_value->Int32Value(); - } - - } else { - // bad arguments. raise error? - return Undefined(); + enum encoding encoding = RAW; + if (args[3]->IsInt32()) { + encoding = static_cast(args[3]->Int32Value()); } - if (file->handle_->Has(FD_SYMBOL) == false) { - printf("trying to write to a bad fd!\n"); - return Undefined(); - } - - int fd = file->GetFD(); - - node::eio_warmup(); - eio_write(fd, buf, length, pos, EIO_PRI_DEFAULT, File::AfterWrite, file); - - file->Attach(); + MAKE_CALLBACK_PTR + // NOTE: 2nd param: NULL pointer tells eio to allocate it itself + eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT, + encoding == UTF8 ? AfterUtf8Read : AfterRawRead, callback); return Undefined(); } -int -File::AfterWrite (eio_req *req) +static int +AfterStat (eio_req *req) { - File *file = reinterpret_cast(req->data); - - //char *buf = reinterpret_cast(req->ptr2); - free(req->ptr2); - ssize_t written = req->result; - HandleScope scope; const int argc = 2; Local argv[argc]; argv[0] = Integer::New(req->errorno); - argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0); - CallTopCallback(file->handle_, argc, argv); - file->Detach(); + Local stats = Object::New(); + argv[1] = stats; + + if (req->result == 0) { + struct stat *s = reinterpret_cast(req->ptr2); + + /* ID of device containing file */ + stats->Set(DEV_SYMBOL, Integer::New(s->st_dev)); + /* inode number */ + stats->Set(INO_SYMBOL, Integer::New(s->st_ino)); + /* protection */ + stats->Set(MODE_SYMBOL, Integer::New(s->st_mode)); + /* number of hard links */ + stats->Set(NLINK_SYMBOL, Integer::New(s->st_nlink)); + /* user ID of owner */ + stats->Set(UID_SYMBOL, Integer::New(s->st_uid)); + /* group ID of owner */ + stats->Set(GID_SYMBOL, Integer::New(s->st_gid)); + /* device ID (if special file) */ + stats->Set(RDEV_SYMBOL, Integer::New(s->st_rdev)); + /* total size, in bytes */ + stats->Set(SIZE_SYMBOL, Integer::New(s->st_size)); + /* blocksize for filesystem I/O */ + stats->Set(BLKSIZE_SYMBOL, Integer::New(s->st_blksize)); + /* number of blocks allocated */ + stats->Set(BLOCKS_SYMBOL, Integer::New(s->st_blocks)); + /* time of last access */ + stats->Set(ATIME_SYMBOL, Date::New(1000*static_cast(s->st_atime))); + /* time of last modification */ + stats->Set(MTIME_SYMBOL, Date::New(1000*static_cast(s->st_mtime))); + /* time of last status change */ + stats->Set(CTIME_SYMBOL, Date::New(1000*static_cast(s->st_ctime))); + } + CALL_CALLBACK_PTR(req, argc, argv); \ return 0; } -Handle -File::Read (const Arguments& args) +static Handle +Stat (const Arguments& args) { - HandleScope scope; + if (args.Length() < 1 || !args[0]->IsString()) + return ThrowException(BAD_ARGUMENTS); - if (args.Length() < 1 || !args[0]->IsNumber() || !args[1]->IsNumber()) { - return ThrowException(String::New("Bad parameter for _ffi_read()")); - } + HandleScope scope; - File *file = NODE_UNWRAP(File, args.Holder()); - size_t len = args[0]->IntegerValue(); - off_t pos = args[1]->IntegerValue(); + String::Utf8Value path(args[0]->ToString()); - int fd = file->GetFD(); - assert(fd >= 0); + MAKE_CALLBACK_PTR - // NOTE: 2nd param: NULL pointer tells eio to allocate it itself - node::eio_warmup(); - eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT, File::AfterRead, file); + eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback); - file->Attach(); return Undefined(); } -int -File::AfterRead (eio_req *req) +static Handle +StrError (const Arguments& args) { - File *file = reinterpret_cast(req->data); - HandleScope scope; - - const int argc = 2; - Local argv[argc]; - argv[0] = Integer::New(req->errorno); + if (args.Length() < 1) return v8::Undefined(); + if (!args[0]->IsNumber()) return v8::Undefined(); - char *buf = reinterpret_cast(req->ptr2); + HandleScope scope; - if (req->result == 0) { - // eof - argv[1] = Local::New(Null()); - } else { - size_t len = req->result; - if (file->encoding_ == UTF8) { - // utf8 encoding - argv[1] = String::New(buf, req->result); - } else { - // raw encoding - Local array = Array::New(len); - for (unsigned int i = 0; i < len; i++) { - array->Set(Integer::New(i), Integer::New(buf[i])); - } - argv[1] = array; - } - } + int errorno = args[0]->IntegerValue(); - CallTopCallback(file->handle_, argc, argv); + Local message = String::New(strerror(errorno)); - file->Detach(); - return 0; + return scope.Close(message); } -Handle -File::New(const Arguments& args) +void +File::Initialize (Handle target) { HandleScope scope; - File *f = new File(args.Holder()); - ObjectWrap::InformV8ofAllocation(f); - - return args.This(); + // file system methods + NODE_SET_METHOD(target, "rename", Rename); + NODE_SET_METHOD(target, "stat", Stat); + NODE_SET_METHOD(target, "close", Close); + NODE_SET_METHOD(target, "open", Open); + NODE_SET_METHOD(target, "write", Write); + NODE_SET_METHOD(target, "read", Read); + + NODE_SET_METHOD(target, "strerror", StrError); + + NODE_DEFINE_CONSTANT(target, RAW); + NODE_DEFINE_CONSTANT(target, UTF8); + + NODE_DEFINE_CONSTANT(target, STDIN_FILENO); + NODE_DEFINE_CONSTANT(target, STDOUT_FILENO); + NODE_DEFINE_CONSTANT(target, STDERR_FILENO); + // file access modes + NODE_DEFINE_CONSTANT(target, O_RDONLY); + NODE_DEFINE_CONSTANT(target, O_WRONLY); + NODE_DEFINE_CONSTANT(target, O_RDWR); + // file creation flags + NODE_DEFINE_CONSTANT(target, O_CREAT); + NODE_DEFINE_CONSTANT(target, O_EXCL); + NODE_DEFINE_CONSTANT(target, O_NOCTTY); + NODE_DEFINE_CONSTANT(target, O_TRUNC); + // file status flags + NODE_DEFINE_CONSTANT(target, O_APPEND); +// NODE_DEFINE_CONSTANT(target, O_ASYNC); + NODE_DEFINE_CONSTANT(target, O_CLOEXEC); + NODE_DEFINE_CONSTANT(target, O_DIRECT); + NODE_DEFINE_CONSTANT(target, O_DIRECTORY); + NODE_DEFINE_CONSTANT(target, O_EXCL); + NODE_DEFINE_CONSTANT(target, O_LARGEFILE); + NODE_DEFINE_CONSTANT(target, O_NOATIME); + NODE_DEFINE_CONSTANT(target, O_NOFOLLOW); +// NODE_DEFINE_CONSTANT(target, O_NONBLOCK); +// NODE_DEFINE_CONSTANT(target, O_SYNC); + NODE_DEFINE_CONSTANT(target, O_SYNC); + + NODE_DEFINE_CONSTANT(target, S_IRWXU); + + NODE_DEFINE_CONSTANT(target, S_IRUSR); + NODE_DEFINE_CONSTANT(target, S_IWUSR); + NODE_DEFINE_CONSTANT(target, S_IXUSR); + + NODE_DEFINE_CONSTANT(target, S_IRWXG); + + NODE_DEFINE_CONSTANT(target, S_IRGRP); + NODE_DEFINE_CONSTANT(target, S_IWGRP); + NODE_DEFINE_CONSTANT(target, S_IXGRP); + + NODE_DEFINE_CONSTANT(target, S_IRWXO); + + NODE_DEFINE_CONSTANT(target, S_IROTH); + NODE_DEFINE_CONSTANT(target, S_IWOTH); + NODE_DEFINE_CONSTANT(target, S_IXOTH); } - diff --git a/src/file.h b/src/file.h index b42d1ae..e48694c 100644 --- a/src/file.h +++ b/src/file.h @@ -5,51 +5,9 @@ namespace node { -class FileSystem { -public: - static v8::Handle Rename (const v8::Arguments& args); - static int AfterRename (eio_req *req); - - static v8::Handle Stat (const v8::Arguments& args); - static int AfterStat (eio_req *req); - - static v8::Handle StrError (const v8::Arguments& args); -}; - -class File : ObjectWrap { +class File { public: static void Initialize (v8::Handle target); - - virtual size_t size (void) { return sizeof(File); } - -protected: - File (v8::Handle handle); - ~File (); - - static v8::Handle New (const v8::Arguments& args); - - static v8::Handle Open (const v8::Arguments& args); - static int AfterOpen (eio_req *req); - - static v8::Handle Close (const v8::Arguments& args); - static int AfterClose (eio_req *req); - - static v8::Handle Write (const v8::Arguments& args); - static int AfterWrite (eio_req *req); - - static v8::Handle Read (const v8::Arguments& args); - static int AfterRead (eio_req *req); - - static v8::Handle GetEncoding (v8::Local property, - const v8::AccessorInfo& info); - static void SetEncoding (v8::Local property, - v8::Local value, - const v8::AccessorInfo& info); - - bool HasUtf8Encoding (void); - int GetFD (void); - - enum encoding encoding_; }; } // namespace node diff --git a/src/file.js b/src/file.js index a4caafc..66879b2 100644 --- a/src/file.js +++ b/src/file.js @@ -5,14 +5,13 @@ node.fs.exists = function (path, callback) { } node.fs.cat = function (path, encoding, callback) { - var file = new node.fs.File(); - file.encoding = encoding; + var file = new node.fs.File({encoding: encoding}); file.onError = function (method, errno, msg) { callback(-1); }; - var content = (file.encoding == "raw" ? "" : []); + var content = (encoding == node.fs.UTF8 ? "" : []); var pos = 0; var chunkSize = 10*1024; @@ -38,112 +37,144 @@ node.fs.cat = function (path, encoding, callback) { }); } -node.fs.File.prototype.open = function (path, mode, callback) { - this._addAction("open", [path, mode], callback); -}; +node.fs.File = function (options) { + var self = this; + options = options || {}; + + if (options.encoding === undefined) + self.encoding = node.fs.RAW; + else + self.encoding = options.encoding + + //node.debug("encoding: opts=" + options.encoding + " self=" + self.encoding); + self.fd = options.fd || null; + + var actionQueue = []; + + // Adds a method to the queue. + function addAction (method, args, callback) { + var action = { method: method + , callback: callback + , args: args + }; + //node.debug("add action: " + JSON.stringify(action)); + actionQueue.push(action); + + // If the queue was empty, immediately call the method. + if (actionQueue.length == 1) act(); + } -node.fs.File.prototype.close = function (callback) { - this._addAction("close", [], callback); -}; + // called after each action finishes (when it returns from the thread pool) + function poll () { + var action = actionQueue[0]; -node.fs.File.prototype.read = function (length, pos, callback) { - this._addAction("read", [length, pos], callback); -}; + var errno = arguments[0]; -node.fs.File.prototype.write = function (buf, pos, callback) { - this._addAction("write", [buf, pos], callback); -}; + if (errno < 0) { + if (self.onError) + self.onError(action.method, errno, node.fs.strerror(errno)); + actionQueue = []; // empty the queue. + return; + } -node.fs.File.prototype.print = function (data, callback) { - this.write(data, -1, callback); -}; + var rest = []; + for (var i = 1; i < arguments.length; i++) + rest.push(arguments[i]); -node.fs.File.prototype.puts = function (data, callback) { - this.write(data + "\n", -1, callback); -}; + //node.debug("poll action: " + JSON.stringify(action)); + //node.debug("poll rest: " + JSON.stringify(rest)); -// Some explanation of the File binding. -// -// All file operations are blocking. To get around this they are executed -// in a thread pool in C++ (libeio). -// -// The ordering of method calls to a file should be preserved, so they are -// only executed one at a time. A queue, called _actionQueue is employed. -// -// The constructor File() is implemented in C++. It initlizes -// the member _actionQueue = [] -// -// Any of the methods called on a file are put into this queue. When they -// reach the head of the queue they will be executed. C++ calls the -// method _pollActions each time it becomes idle. If there is no action -// currently being executed then _pollActions will not be called. When -// actions are added to an empty _actionQueue, they will be immediately -// executed. -// -// When an action has completed, the C++ side is looks at the first -// element of _actionQueue in order to get a handle on the callback -// function. Only after that completion callback has been made can the -// action be shifted out of the queue. -// -// See File::CallTopCallback() in file.cc for the other side of the binding. - - -node.fs.File.prototype._addAction = function (method, args, callback) { - // This adds a method to the queue. - var action = { method: method - , callback: callback - , args: args - }; - this._actionQueue.push(action); - - // If the queue was empty, immediately call the method. - if (this._actionQueue.length == 1) this._act(); -}; + if (action.callback) + action.callback.apply(this, rest); -node.fs.File.prototype._act = function () { - // peek at the head of the queue - var action = this._actionQueue[0]; - if (action) { - // execute the c++ version of the method. the c++ version - // is gotten by appending "_ffi_" to the method name. - this["_ffi_" + action.method].apply(this, action.args); + actionQueue.shift(); + act(); } -}; - -// called from C++ after each action finishes -// (i.e. when it returns from the thread pool) -node.fs.File.prototype._pollActions = function () { - var action = this._actionQueue[0]; - - var errno = arguments[0]; - if (errno < 0) { - if (this.onError) - this.onError(action.method, errno, node.fs.strerror(errno)); - this._actionQueue = []; // empty the queue. - return; + function act () { + var action = actionQueue[0]; // peek at the head of the queue + if (action) { + internal_methods[action.method].apply(this, action.args); + } } - var rest = []; - for (var i = 1; i < arguments.length; i++) - rest.push(arguments[i]); + var internal_methods = { + open: function (path, mode) { + var m = node.fs.O_RDONLY; + switch (mode) { + case "r": + m = node.fs.O_RDONLY; + break; + case "r+": + m = node.fs.O_RDWR; + break; + case "w": + m = O_CREAT | O_TRUNC | O_WRONLY; + break; + case "w+": + m = O_CREAT | O_TRUNC | O_RDWR; + break; + case "a": + m = O_APPEND | O_CREAT | O_WRONLY; + break; + case "a+": + m = O_APPEND | O_CREAT | O_RDWR; + break; + default: + throw "Unknown mode"; + } - if (action.callback) - action.callback.apply(this, rest); + node.fs.open(path, m, 0666, function (status, fd) { + self.fd = fd; + poll(status, fd); + }); + }, + + close: function ( ) { + node.fs.close(self.fd, function (status) { + self.fd = null; + poll(status); + }); + }, + + read: function (length, position) { + //node.debug("encoding: " + self.encoding); + node.fs.read(self.fd, length, position, self.encoding, poll); + }, + + write: function (data, position) { + node.fs.write(self.fd, data, position, poll); + } + }; - this._actionQueue.shift(); + self.open = function (path, mode, callback) { + addAction("open", [path, mode], callback); + }; + + self.close = function (callback) { + addAction("close", [], callback); + }; - this._act(); -}; + self.read = function (length, pos, callback) { + addAction("read", [length, pos], callback); + }; -stdout = new node.fs.File(); -stdout.fd = node.fs.File.STDOUT_FILENO; + self.write = function (buf, pos, callback) { + addAction("write", [buf, pos], callback); + }; + + self.print = function (data, callback) { + return self.write(data, null, callback); + }; -stderr = new node.fs.File(); -stderr.fd = node.fs.File.STDERR_FILENO; + self.puts = function (data, callback) { + return self.write(data + "\n", null, callback); + }; +}; -stdin = new node.fs.File(); -stdin.fd = node.fs.File.STDIN_FILENO; +stdout = new node.fs.File({ fd: node.fs.STDOUT_FILENO }); +stderr = new node.fs.File({ fd: node.fs.STDERR_FILENO }); +stdin = new node.fs.File({ fd: node.fs.STDIN_FILENO }); puts = function (data, callback) { stdout.puts(data, callback); diff --git a/src/node.h b/src/node.h index d6db49d..7d1d296 100644 --- a/src/node.h +++ b/src/node.h @@ -12,6 +12,8 @@ namespace node { #define NODE_SET_METHOD(obj, name, callback) \ obj->Set(NODE_SYMBOL(name), v8::FunctionTemplate::New(callback)->GetFunction()) #define NODE_UNWRAP(type, value) static_cast(node::ObjectWrap::Unwrap(value)) +#define NODE_DEFINE_CONSTANT(target, constant) \ + (target)->Set(v8::String::NewSymbol(#constant), v8::Integer::New(constant)) #define NODE_SET_PROTOTYPE_METHOD(templ, name, callback) \ do { \ diff --git a/src/node.js b/src/node.js index e3d9d16..6bdef37 100644 --- a/src/node.js +++ b/src/node.js @@ -134,7 +134,7 @@ clearInterval = clearTimeout; } function loadScript (filename, target, callback) { - node.fs.cat(filename, "utf8", function (status, content) { + node.fs.cat(filename, node.fs.UTF8, function (status, content) { if (status != 0) { stderr.puts("Error reading " + filename + ": " + node.fs.strerror(status)); node.exit(1); -- 2.7.4