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. 669
      src/file.cc
  2. 44
      src/file.h
  3. 189
      src/file.js
  4. 2
      src/node.h
  5. 2
      src/node.js

669
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<Object> 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<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;
// 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<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);
int fd = args[0]->Int32Value();
MAKE_CALLBACK_PTR
eio_close(fd, EIO_PRI_DEFAULT, AfterClose, callback);
return Undefined();
}
Handle<Value>
FileSystem::Rename (const Arguments& args)
DEFINE_SIMPLE_CB(Rename)
static Handle<Value> 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<Function> *callback = NULL;
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);
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<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
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);
}
argv[1] = Integer::New(req->result);
CALL_CALLBACK_PTR(req, argc, argv);
return 0;
}
Handle<Value>
FileSystem::Stat (const Arguments& args)
static Handle<Value>
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<mode_t>(args[2]->Int32Value());
Persistent<Function> *callback = NULL;
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);
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<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0);
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(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);
}
CALL_CALLBACK_PTR(req, argc, argv);
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[0]->IsNumber()) return v8::Undefined();
if ( args.Length() < 3
|| !args[0]->IsInt32()
) return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
int errorno = args[0]->IntegerValue();
Local<String> message = String::New(strerror(errorno));
return scope.Close(message);
}
int fd = args[0]->Int32Value();
off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
///////////////////// FILE /////////////////////
char *buf = NULL;
size_t len = 0;
File::File (Handle<Object> handle)
: ObjectWrap(handle)
{
HandleScope scope;
encoding_ = RAW;
handle->Set(ACTION_QUEUE_SYMBOL, Array::New());
}
if (args[1]->IsString()) {
// utf8 encoding
Local<String> string = args[1]->ToString();
len = string->Utf8Length();
buf = reinterpret_cast<char*>(malloc(len));
string->WriteUtf8(buf, len);
File::~File ()
{
; // XXX call close?
}
} 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
File::HasUtf8Encoding (void)
{
return false;
}
} else {
return ThrowException(BAD_ARGUMENTS);
}
int
File::GetFD (void)
{
Handle<Value> 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<Value>
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<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
int
File::AfterClose (eio_req *req)
{
File *file = reinterpret_cast<File*>(req->data);
char *buf = reinterpret_cast<char*>(req->ptr2);
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;
Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
CallTopCallback(file->handle_, argc, argv);
file->Detach();
CALL_CALLBACK_PTR(req, argc, argv);
return 0;
}
Handle<Value>
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<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
String::Utf8Value path(args[0]->ToString());
char *buf = reinterpret_cast<char*>(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<Value>::New(Null());
} else {
// raw encoding
size_t len = req->result;
Local<Array> array = Array::New(len);
for (unsigned int i = 0; i < len; i++) {
array->Set(Integer::New(i), Integer::New(buf[i]));
}
// 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<File*>(req->data);
HandleScope scope;
if(req->result >= 0) {
file->handle_->Set(FD_SYMBOL, Integer::New(req->result));
argv[1] = array;
}
const int argc = 1;
Handle<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
CallTopCallback(file->handle_, argc, argv);
file->Detach();
CALL_CALLBACK_PTR(req, argc, argv);
return 0;
}
Handle<Value>
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<Value>
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());
off_t pos = args[1]->IntegerValue();
char *buf = NULL;
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();
}
int fd = args[0]->Int32Value();
size_t len = args[1]->IntegerValue();
off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
if (file->handle_->Has(FD_SYMBOL) == false) {
printf("trying to write to a bad fd!\n");
return Undefined();
enum encoding encoding = RAW;
if (args[3]->IsInt32()) {
encoding = static_cast<enum encoding>(args[3]->Int32Value());
}
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<File*>(req->data);
//char *buf = reinterpret_cast<char*>(req->ptr2);
free(req->ptr2);
ssize_t written = req->result;
HandleScope scope;
const int argc = 2;
Local<Value> 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<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;
}
Handle<Value>
File::Read (const Arguments& args)
static Handle<Value>
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<Value>
StrError (const Arguments& args)
{
File *file = reinterpret_cast<File*>(req->data);
HandleScope scope;
const int argc = 2;
Local<Value> 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<char*>(req->ptr2);
HandleScope scope;
if (req->result == 0) {
// 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;
}
}
int errorno = args[0]->IntegerValue();
CallTopCallback(file->handle_, argc, argv);
Local<String> message = String::New(strerror(errorno));
file->Detach();
return 0;
return scope.Close(message);
}
Handle<Value>
File::New(const Arguments& args)
void
File::Initialize (Handle<Object> 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);
}

44
src/file.h

@ -5,51 +5,9 @@
namespace node {
class FileSystem {
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 {
class File {
public:
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

189
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,89 +37,43 @@ 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.prototype.close = function (callback) {
this._addAction("close", [], callback);
};
node.fs.File.prototype.read = function (length, pos, callback) {
this._addAction("read", [length, pos], callback);
};
node.fs.File = function (options) {
var self = this;
options = options || {};
node.fs.File.prototype.write = function (buf, pos, callback) {
this._addAction("write", [buf, pos], callback);
};
if (options.encoding === undefined)
self.encoding = node.fs.RAW;
else
self.encoding = options.encoding
node.fs.File.prototype.print = function (data, callback) {
this.write(data, -1, callback);
};
//node.debug("encoding: opts=" + options.encoding + " self=" + self.encoding);
self.fd = options.fd || null;
node.fs.File.prototype.puts = function (data, callback) {
this.write(data + "\n", -1, callback);
};
var actionQueue = [];
// 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.
// Adds a method to the queue.
function addAction (method, args, callback) {
var action = { method: method
, callback: callback
, args: args
};
this._actionQueue.push(action);
//node.debug("add action: " + JSON.stringify(action));
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 () {
// 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);
if (actionQueue.length == 1) 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];
// called after each action finishes (when it returns from the thread pool)
function poll () {
var action = 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.
if (self.onError)
self.onError(action.method, errno, node.fs.strerror(errno));
actionQueue = []; // empty the queue.
return;
}
@ -128,22 +81,100 @@ node.fs.File.prototype._pollActions = function () {
for (var i = 1; i < arguments.length; i++)
rest.push(arguments[i]);
//node.debug("poll action: " + JSON.stringify(action));
//node.debug("poll rest: " + JSON.stringify(rest));
if (action.callback)
action.callback.apply(this, rest);
this._actionQueue.shift();
actionQueue.shift();
act();
}
function act () {
var action = actionQueue[0]; // peek at the head of the queue
if (action) {
internal_methods[action.method].apply(this, action.args);
}
}
this._act();
};
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";
}
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);
}
};
self.open = function (path, mode, callback) {
addAction("open", [path, mode], callback);
};
self.close = function (callback) {
addAction("close", [], callback);
};
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);

2
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<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) \
do { \

2
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);

Loading…
Cancel
Save