Browse Source

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.
v0.7.4-release
Ryan 16 years ago
parent
commit
d1b0ce6d37
  1. 673
      src/file.cc
  2. 44
      src/file.h
  3. 215
      src/file.js
  4. 2
      src/node.h
  5. 2
      src/node.js

673
src/file.cc

@ -12,490 +12,405 @@
using namespace v8; using namespace v8;
using namespace node; using namespace node;
#define FD_SYMBOL String::NewSymbol("fd") #define DEV_SYMBOL String::NewSymbol("dev")
#define ACTION_QUEUE_SYMBOL String::NewSymbol("_actionQueue") #define INO_SYMBOL String::NewSymbol("ino")
#define ENCODING_SYMBOL String::NewSymbol("encoding") #define MODE_SYMBOL String::NewSymbol("mode")
#define CALLBACK_SYMBOL String::NewSymbol("callbaccallback") #define NLINK_SYMBOL String::NewSymbol("nlink")
#define POLL_ACTIONS_SYMBOL String::NewSymbol("_pollActions") #define UID_SYMBOL String::NewSymbol("uid")
#define GID_SYMBOL String::NewSymbol("gid")
#define UTF8_SYMBOL String::NewSymbol("utf8") #define RDEV_SYMBOL String::NewSymbol("rdev")
#define RAW_SYMBOL String::NewSymbol("raw") #define SIZE_SYMBOL String::NewSymbol("size")
#define BLKSIZE_SYMBOL String::NewSymbol("blksize")
void #define BLOCKS_SYMBOL String::NewSymbol("blocks")
File::Initialize (Handle<Object> target) #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<Function> *callback = NULL; \
Local<Value> last_arg = args[args.Length()-1]; \
if (last_arg->IsFunction()) { \
Local<Function> l = Local<Function>::Cast(last_arg); \
callback = new Persistent<Function>(); \
*callback = Persistent<Function>::New(l); \
} \
node::eio_warmup(); \
#define CALL_CALLBACK_PTR(req, argc, argv) \
do { \
if (req->data) { \
Persistent<Function> *callback = \
reinterpret_cast<Persistent<Function>*>(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<Value> argv[] = { Integer::New(req->errorno) }; \
CALL_CALLBACK_PTR(req, 1, argv); \
return 0; \
} \
DEFINE_SIMPLE_CB(Close)
static Handle<Value> Close (const Arguments& args)
{ {
if (args.Length() < 1 || !args[0]->IsInt32())
return ThrowException(BAD_ARGUMENTS);
HandleScope scope; HandleScope scope;
int fd = args[0]->Int32Value();
// file system methods MAKE_CALLBACK_PTR
NODE_SET_METHOD(target, "rename", FileSystem::Rename); eio_close(fd, EIO_PRI_DEFAULT, AfterClose, callback);
NODE_SET_METHOD(target, "stat", FileSystem::Stat); return Undefined();
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<FunctionTemplate> 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<Value>
File::GetEncoding (Local<String> 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<String> property, Local<Value> value, const AccessorInfo& info)
{
File *file = NODE_UNWRAP(File, info.This());
if (value->IsString()) {
Local<String> 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<Object> handle, const int argc, Handle<Value> argv[])
{
HandleScope scope;
// poll_actions
Local<Value> poll_actions_value = handle->Get(POLL_ACTIONS_SYMBOL);
assert(poll_actions_value->IsFunction());
Handle<Function> poll_actions = Handle<Function>::Cast(poll_actions_value);
TryCatch try_catch;
poll_actions->Call(handle, argc, argv);
if(try_catch.HasCaught())
node::fatal_exception(try_catch);
} }
Handle<Value> DEFINE_SIMPLE_CB(Rename)
FileSystem::Rename (const Arguments& args) static Handle<Value> Rename (const Arguments& args)
{ {
if (args.Length() < 2) if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString())
return Undefined(); return ThrowException(BAD_ARGUMENTS);
HandleScope scope; HandleScope scope;
String::Utf8Value path(args[0]->ToString()); String::Utf8Value path(args[0]->ToString());
String::Utf8Value new_path(args[1]->ToString()); String::Utf8Value new_path(args[1]->ToString());
MAKE_CALLBACK_PTR
Persistent<Function> *callback = NULL; eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, callback);
if (args[2]->IsFunction()) {
Local<Function> l = Local<Function>::Cast(args[2]);
callback = new Persistent<Function>();
*callback = Persistent<Function>::New(l);
}
node::eio_warmup();
eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, NULL);
return Undefined(); return Undefined();
} }
int static int
FileSystem::AfterRename (eio_req *req) AfterOpen (eio_req *req)
{ {
HandleScope scope; HandleScope scope;
const int argc = 1; const int argc = 2;
Local<Value> argv[argc]; Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno); argv[0] = Integer::New(req->errorno);
argv[1] = Integer::New(req->result);
if (req->data) { CALL_CALLBACK_PTR(req, argc, argv);
Persistent<Function> *callback = reinterpret_cast<Persistent<Function>*>(req->data);
TryCatch try_catch;
(*callback)->Call(Context::GetCurrent()->Global(), argc, argv);
if(try_catch.HasCaught())
node::fatal_exception(try_catch);
free(callback);
}
return 0; return 0;
} }
Handle<Value> static Handle<Value>
FileSystem::Stat (const Arguments& args) Open (const Arguments& args)
{ {
if (args.Length() < 1) if ( args.Length() < 3
return v8::Undefined(); || !args[0]->IsString()
|| !args[1]->IsInt32()
|| !args[2]->IsInt32()
) return ThrowException(BAD_ARGUMENTS);
HandleScope scope; HandleScope scope;
String::Utf8Value path(args[0]->ToString()); String::Utf8Value path(args[0]->ToString());
int flags = args[1]->Int32Value();
mode_t mode = static_cast<mode_t>(args[2]->Int32Value());
Persistent<Function> *callback = NULL; MAKE_CALLBACK_PTR
if (args[1]->IsFunction()) {
Local<Function> l = Local<Function>::Cast(args[1]);
callback = new Persistent<Function>();
*callback = Persistent<Function>::New(l);
}
node::eio_warmup();
eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback);
eio_open(*path, flags, mode, EIO_PRI_DEFAULT, AfterOpen, callback);
return Undefined(); return Undefined();
} }
int static int
FileSystem::AfterStat (eio_req *req) AfterWrite (eio_req *req)
{ {
HandleScope scope; HandleScope scope;
free(req->ptr2);
ssize_t written = req->result;
const int argc = 2; const int argc = 2;
Local<Value> argv[argc]; Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno); argv[0] = Integer::New(req->errorno);
argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0);
Local<Object> stats = Object::New(); CALL_CALLBACK_PTR(req, argc, argv);
argv[1] = stats;
if (req->result == 0) {
struct stat *s = reinterpret_cast<struct stat*>(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<double>(s->st_atime)));
/* time of last modification */
stats->Set(NODE_SYMBOL("mtime"), Date::New(1000*static_cast<double>(s->st_mtime)));
/* time of last status change */
stats->Set(NODE_SYMBOL("ctime"), Date::New(1000*static_cast<double>(s->st_ctime)));
}
if (req->data) {
Persistent<Function> *callback = reinterpret_cast<Persistent<Function>*>(req->data);
TryCatch try_catch;
(*callback)->Call(Context::GetCurrent()->Global(), argc, argv);
if(try_catch.HasCaught())
node::fatal_exception(try_catch);
free(callback);
}
return 0; return 0;
} }
Handle<Value>
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<Value>
Write (const Arguments& args)
{ {
if (args.Length() < 1) return v8::Undefined(); if ( args.Length() < 3
if (!args[0]->IsNumber()) return v8::Undefined(); || !args[0]->IsInt32()
) return ThrowException(BAD_ARGUMENTS);
HandleScope scope; HandleScope scope;
int errorno = args[0]->IntegerValue(); int fd = args[0]->Int32Value();
off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
Local<String> message = String::New(strerror(errorno));
return scope.Close(message);
}
///////////////////// FILE /////////////////////
File::File (Handle<Object> handle) char *buf = NULL;
: ObjectWrap(handle) size_t len = 0;
{
HandleScope scope;
encoding_ = RAW;
handle->Set(ACTION_QUEUE_SYMBOL, Array::New());
}
File::~File () if (args[1]->IsString()) {
{ // utf8 encoding
; // XXX call close? Local<String> string = args[1]->ToString();
} len = string->Utf8Length();
buf = reinterpret_cast<char*>(malloc(len));
string->WriteUtf8(buf, len);
} else if (args[1]->IsArray()) {
// raw encoding
Local<Array> array = Local<Array>::Cast(args[1]);
len = array->Length();
buf = reinterpret_cast<char*>(malloc(len));
for (unsigned int i = 0; i < len; i++) {
Local<Value> int_value = array->Get(Integer::New(i));
buf[i] = int_value->Int32Value();
}
bool } else {
File::HasUtf8Encoding (void) return ThrowException(BAD_ARGUMENTS);
{ }
return false;
}
int MAKE_CALLBACK_PTR
File::GetFD (void) eio_write(fd, buf, len, pos, EIO_PRI_DEFAULT, AfterWrite, callback);
{ return Undefined();
Handle<Value> fd_value = handle_->Get(FD_SYMBOL);
int fd = fd_value->IntegerValue();
return fd;
} }
Handle<Value> static int
File::Close (const Arguments& args) AfterUtf8Read (eio_req *req)
{ {
HandleScope scope; HandleScope scope;
File *file = NODE_UNWRAP(File, args.Holder()); const int argc = 2;
Local<Value> argv[argc];
int fd = file->GetFD(); argv[0] = Integer::New(req->errorno);
node::eio_warmup();
eio_close (fd, EIO_PRI_DEFAULT, File::AfterClose, file);
file->Attach();
return Undefined();
}
int char *buf = reinterpret_cast<char*>(req->ptr2);
File::AfterClose (eio_req *req)
{
File *file = reinterpret_cast<File*>(req->data);
if (req->result == 0) { if (req->result == 0) {
file->handle_->Delete(FD_SYMBOL); // eof
argv[1] = Local<Value>::New(Null());
} else {
size_t len = req->result;
argv[1] = String::New(buf, len);
} }
const int argc = 1; CALL_CALLBACK_PTR(req, argc, argv);
Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
CallTopCallback(file->handle_, argc, argv);
file->Detach();
return 0; return 0;
} }
Handle<Value> static int
File::Open (const Arguments& args) AfterRawRead(eio_req *req)
{ {
/* check arguments */
if (args.Length() < 1) return Undefined();
if (!args[0]->IsString()) return Undefined();
HandleScope scope; HandleScope scope;
File *file = NODE_UNWRAP(File, args.Holder()); const int argc = 2;
Local<Value> argv[argc];
// make sure that we don't already have a pending open argv[0] = Integer::New(req->errorno);
if (file->handle_->Has(FD_SYMBOL)) {
return ThrowException(String::New("File object is opened."));
}
String::Utf8Value path(args[0]->ToString()); char *buf = reinterpret_cast<char*>(req->ptr2);
int flags = O_RDONLY; // default if (req->result == 0) {
if (args[1]->IsString()) { // eof
String::AsciiValue mode_v(args[1]->ToString()); argv[1] = Local<Value>::New(Null());
char *mode = *mode_v; } else {
// XXX is this interpretation of the mode correct? // raw encoding
// I don't want to to use fopen() directly because eio doesn't support it. size_t len = req->result;
switch(mode[0]) { Local<Array> array = Array::New(len);
case 'r': for (unsigned int i = 0; i < len; i++) {
flags = (mode[1] == '+' ? O_RDWR : O_RDONLY); array->Set(Integer::New(i), Integer::New(buf[i]));
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;
} }
argv[1] = array;
} }
// TODO how should the mode be set? CALL_CALLBACK_PTR(req, argc, argv);
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<File*>(req->data);
HandleScope scope;
if(req->result >= 0) {
file->handle_->Set(FD_SYMBOL, Integer::New(req->result));
}
const int argc = 1;
Handle<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
CallTopCallback(file->handle_, argc, argv);
file->Detach();
return 0; return 0;
} }
Handle<Value> /* node.fs.read(fd, length, position, encoding, callback)
File::Write (const Arguments& args) * 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<Value>
Read (const Arguments& args)
{ {
if (args.Length() < 2) return Undefined(); if ( args.Length() < 3
if (!args[1]->IsNumber()) return Undefined(); || !args[0]->IsInt32() // fd
|| !args[1]->IsNumber() // len
) return ThrowException(BAD_ARGUMENTS);
HandleScope scope; 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(); enum encoding encoding = RAW;
if (args[3]->IsInt32()) {
char *buf = NULL; encoding = static_cast<enum encoding>(args[3]->Int32Value());
size_t length = 0;
if (args[0]->IsString()) {
// utf8 encoding
Local<String> string = args[0]->ToString();
length = string->Utf8Length();
buf = reinterpret_cast<char*>(malloc(length));
string->WriteUtf8(buf, length);
} else if (args[0]->IsArray()) {
// raw encoding
Local<Array> array = Local<Array>::Cast(args[0]);
length = array->Length();
buf = reinterpret_cast<char*>(malloc(length));
for (unsigned int i = 0; i < length; i++) {
Local<Value> int_value = array->Get(Integer::New(i));
buf[i] = int_value->Int32Value();
}
} else {
// bad arguments. raise error?
return Undefined();
} }
if (file->handle_->Has(FD_SYMBOL) == false) { MAKE_CALLBACK_PTR
printf("trying to write to a bad fd!\n"); // NOTE: 2nd param: NULL pointer tells eio to allocate it itself
return Undefined(); eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT,
} encoding == UTF8 ? AfterUtf8Read : AfterRawRead, callback);
int fd = file->GetFD();
node::eio_warmup();
eio_write(fd, buf, length, pos, EIO_PRI_DEFAULT, File::AfterWrite, file);
file->Attach();
return Undefined(); return Undefined();
} }
int static int
File::AfterWrite (eio_req *req) AfterStat (eio_req *req)
{ {
File *file = reinterpret_cast<File*>(req->data);
//char *buf = reinterpret_cast<char*>(req->ptr2);
free(req->ptr2);
ssize_t written = req->result;
HandleScope scope; HandleScope scope;
const int argc = 2; const int argc = 2;
Local<Value> argv[argc]; Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno); argv[0] = Integer::New(req->errorno);
argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0);
CallTopCallback(file->handle_, argc, argv);
file->Detach(); Local<Object> stats = Object::New();
argv[1] = stats;
if (req->result == 0) {
struct stat *s = reinterpret_cast<struct stat*>(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<double>(s->st_atime)));
/* time of last modification */
stats->Set(MTIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_mtime)));
/* time of last status change */
stats->Set(CTIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_ctime)));
}
CALL_CALLBACK_PTR(req, argc, argv); \
return 0; return 0;
} }
Handle<Value> static Handle<Value>
File::Read (const Arguments& args) 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()) { HandleScope scope;
return ThrowException(String::New("Bad parameter for _ffi_read()"));
}
File *file = NODE_UNWRAP(File, args.Holder()); String::Utf8Value path(args[0]->ToString());
size_t len = args[0]->IntegerValue();
off_t pos = args[1]->IntegerValue();
int fd = file->GetFD(); MAKE_CALLBACK_PTR
assert(fd >= 0);
// NOTE: 2nd param: NULL pointer tells eio to allocate it itself eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback);
node::eio_warmup();
eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT, File::AfterRead, file);
file->Attach();
return Undefined(); return Undefined();
} }
int static Handle<Value>
File::AfterRead (eio_req *req) StrError (const Arguments& args)
{ {
File *file = reinterpret_cast<File*>(req->data); if (args.Length() < 1) return v8::Undefined();
HandleScope scope; if (!args[0]->IsNumber()) return v8::Undefined();
const int argc = 2;
Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
char *buf = reinterpret_cast<char*>(req->ptr2); HandleScope scope;
if (req->result == 0) { int errorno = args[0]->IntegerValue();
// eof
argv[1] = Local<Value>::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 = Array::New(len);
for (unsigned int i = 0; i < len; i++) {
array->Set(Integer::New(i), Integer::New(buf[i]));
}
argv[1] = array;
}
}
CallTopCallback(file->handle_, argc, argv); Local<String> message = String::New(strerror(errorno));
file->Detach(); return scope.Close(message);
return 0;
} }
Handle<Value> void
File::New(const Arguments& args) File::Initialize (Handle<Object> target)
{ {
HandleScope scope; HandleScope scope;
File *f = new File(args.Holder()); // file system methods
ObjectWrap::InformV8ofAllocation(f); NODE_SET_METHOD(target, "rename", Rename);
NODE_SET_METHOD(target, "stat", Stat);
return args.This(); 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);
} }

44
src/file.h

@ -5,51 +5,9 @@
namespace node { namespace node {
class FileSystem { class File {
public:
static v8::Handle<v8::Value> Rename (const v8::Arguments& args);
static int AfterRename (eio_req *req);
static v8::Handle<v8::Value> Stat (const v8::Arguments& args);
static int AfterStat (eio_req *req);
static v8::Handle<v8::Value> StrError (const v8::Arguments& args);
};
class File : ObjectWrap {
public: public:
static void Initialize (v8::Handle<v8::Object> target); static void Initialize (v8::Handle<v8::Object> target);
virtual size_t size (void) { return sizeof(File); }
protected:
File (v8::Handle<v8::Object> handle);
~File ();
static v8::Handle<v8::Value> New (const v8::Arguments& args);
static v8::Handle<v8::Value> Open (const v8::Arguments& args);
static int AfterOpen (eio_req *req);
static v8::Handle<v8::Value> Close (const v8::Arguments& args);
static int AfterClose (eio_req *req);
static v8::Handle<v8::Value> Write (const v8::Arguments& args);
static int AfterWrite (eio_req *req);
static v8::Handle<v8::Value> Read (const v8::Arguments& args);
static int AfterRead (eio_req *req);
static v8::Handle<v8::Value> GetEncoding (v8::Local<v8::String> property,
const v8::AccessorInfo& info);
static void SetEncoding (v8::Local<v8::String> property,
v8::Local<v8::Value> value,
const v8::AccessorInfo& info);
bool HasUtf8Encoding (void);
int GetFD (void);
enum encoding encoding_;
}; };
} // namespace node } // namespace node

215
src/file.js

@ -5,14 +5,13 @@ node.fs.exists = function (path, callback) {
} }
node.fs.cat = function (path, encoding, callback) { node.fs.cat = function (path, encoding, callback) {
var file = new node.fs.File(); var file = new node.fs.File({encoding: encoding});
file.encoding = encoding;
file.onError = function (method, errno, msg) { file.onError = function (method, errno, msg) {
callback(-1); callback(-1);
}; };
var content = (file.encoding == "raw" ? "" : []); var content = (encoding == node.fs.UTF8 ? "" : []);
var pos = 0; var pos = 0;
var chunkSize = 10*1024; var chunkSize = 10*1024;
@ -38,112 +37,144 @@ node.fs.cat = function (path, encoding, callback) {
}); });
} }
node.fs.File.prototype.open = function (path, mode, callback) { node.fs.File = function (options) {
this._addAction("open", [path, mode], callback); 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) { // called after each action finishes (when it returns from the thread pool)
this._addAction("close", [], callback); function poll () {
}; var action = actionQueue[0];
node.fs.File.prototype.read = function (length, pos, callback) { var errno = arguments[0];
this._addAction("read", [length, pos], callback);
};
node.fs.File.prototype.write = function (buf, pos, callback) { if (errno < 0) {
this._addAction("write", [buf, pos], callback); 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) { var rest = [];
this.write(data, -1, callback); for (var i = 1; i < arguments.length; i++)
}; rest.push(arguments[i]);
node.fs.File.prototype.puts = function (data, callback) { //node.debug("poll action: " + JSON.stringify(action));
this.write(data + "\n", -1, callback); //node.debug("poll rest: " + JSON.stringify(rest));
};
// Some explanation of the File binding. if (action.callback)
// action.callback.apply(this, rest);
// 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();
};
node.fs.File.prototype._act = function () { actionQueue.shift();
// peek at the head of the queue act();
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);
} }
};
// 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) { function act () {
if (this.onError) var action = actionQueue[0]; // peek at the head of the queue
this.onError(action.method, errno, node.fs.strerror(errno)); if (action) {
this._actionQueue = []; // empty the queue. internal_methods[action.method].apply(this, action.args);
return; }
} }
var rest = []; var internal_methods = {
for (var i = 1; i < arguments.length; i++) open: function (path, mode) {
rest.push(arguments[i]); 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) node.fs.open(path, m, 0666, function (status, fd) {
action.callback.apply(this, rest); 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(); self.write = function (buf, pos, callback) {
stdout.fd = node.fs.File.STDOUT_FILENO; addAction("write", [buf, pos], callback);
};
self.print = function (data, callback) {
return self.write(data, null, callback);
};
stderr = new node.fs.File(); self.puts = function (data, callback) {
stderr.fd = node.fs.File.STDERR_FILENO; return self.write(data + "\n", null, callback);
};
};
stdin = new node.fs.File(); stdout = new node.fs.File({ fd: node.fs.STDOUT_FILENO });
stdin.fd = node.fs.File.STDIN_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) { puts = function (data, callback) {
stdout.puts(data, callback); stdout.puts(data, callback);

2
src/node.h

@ -12,6 +12,8 @@ namespace node {
#define NODE_SET_METHOD(obj, name, callback) \ #define NODE_SET_METHOD(obj, name, callback) \
obj->Set(NODE_SYMBOL(name), v8::FunctionTemplate::New(callback)->GetFunction()) obj->Set(NODE_SYMBOL(name), v8::FunctionTemplate::New(callback)->GetFunction())
#define NODE_UNWRAP(type, value) static_cast<type*>(node::ObjectWrap::Unwrap(value)) #define NODE_UNWRAP(type, value) static_cast<type*>(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) \ #define NODE_SET_PROTOTYPE_METHOD(templ, name, callback) \
do { \ do { \

2
src/node.js

@ -134,7 +134,7 @@ clearInterval = clearTimeout;
} }
function loadScript (filename, target, callback) { 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) { if (status != 0) {
stderr.puts("Error reading " + filename + ": " + node.fs.strerror(status)); stderr.puts("Error reading " + filename + ": " + node.fs.strerror(status));
node.exit(1); node.exit(1);

Loading…
Cancel
Save