#include "FileTransferObject.h" #include "../../logger.h" #include #include namespace fs = std::experimental::filesystem; using namespace tc; using namespace tc::ft; using namespace std; #ifdef NODEJS_API TransferJSBufferTarget::TransferJSBufferTarget() { log_allocate("TransferJSBufferTarget", this); if(!this->_js_buffer.IsEmpty()) { assert(v8::Isolate::GetCurrent()); this->_js_buffer.Reset(); } } TransferJSBufferTarget::~TransferJSBufferTarget() { log_free("TransferJSBufferTarget", this); } bool TransferJSBufferTarget::initialize(std::string &error) { return true; /* we've already have data */ } void TransferJSBufferTarget::finalize() { } uint64_t TransferJSBufferTarget::stream_index() const { return this->_js_buffer_index; } error::value TransferJSBufferTarget::write_bytes(std::string &error, uint8_t *source, uint64_t length) { uint64_t write_length = length; if(length > this->_js_buffer_length - this->_js_buffer_index) write_length = this->_js_buffer_length - this->_js_buffer_index; if(write_length > 0) { memcpy((char*) this->_js_buffer_source + this->_js_buffer_index, source, write_length); this->_js_buffer_index += write_length; } if(write_length == 0) return error::out_of_space; return error::success; } NAN_METHOD(TransferJSBufferTarget::create_from_buffer) { if(info.Length() != 1 || !info[0]->IsArrayBuffer()) { Nan::ThrowError("invalid argument"); return; } auto buffer = info[0].As(); auto instance = make_shared(); instance->_js_buffer = v8::Global(info.GetIsolate(), info[0].As()); instance->_js_buffer_source = buffer->GetContents().Data(); instance->_js_buffer_length = buffer->GetContents().ByteLength(); instance->_js_buffer_index = 0; auto object_wrap = new TransferObjectWrap(instance); auto object = Nan::NewInstance(Nan::New(TransferObjectWrap::constructor()), 0, nullptr).ToLocalChecked(); object_wrap->do_wrap(object); info.GetReturnValue().Set(object); } TransferJSBufferSource::~TransferJSBufferSource() { log_free("TransferJSBufferSource", this); if(!this->_js_buffer.IsEmpty()) { assert(v8::Isolate::GetCurrent()); this->_js_buffer.Reset(); } } TransferJSBufferSource::TransferJSBufferSource() { log_allocate("TransferJSBufferSource", this); } bool TransferJSBufferSource::initialize(std::string &string) { return true; } void TransferJSBufferSource::finalize() { } uint64_t TransferJSBufferSource::stream_index() const { return this->_js_buffer_index; } uint64_t TransferJSBufferSource::byte_length() const { return this->_js_buffer_length; } error::value TransferJSBufferSource::read_bytes(std::string &error, uint8_t *target, uint64_t &length) { auto org_length = length; if(this->_js_buffer_index + length > this->_js_buffer_length) length = this->_js_buffer_length - this->_js_buffer_index; memcpy(target, (char*) this->_js_buffer_source + this->_js_buffer_index, length); this->_js_buffer_index += length; if(org_length != 0 && length == 0) return error::out_of_space; return error::success; } NAN_METHOD(TransferJSBufferSource::create_from_buffer) { if(info.Length() != 1 || !info[0]->IsArrayBuffer()) { Nan::ThrowError("invalid argument"); return; } auto buffer = info[0].As(); auto instance = make_shared(); instance->_js_buffer = v8::Global(info.GetIsolate(), info[0].As()); instance->_js_buffer_source = buffer->GetContents().Data(); instance->_js_buffer_length = buffer->GetContents().ByteLength(); instance->_js_buffer_index = 0; auto object_wrap = new TransferObjectWrap(instance); auto object = Nan::NewInstance(Nan::New(TransferObjectWrap::constructor()), 0, nullptr).ToLocalChecked(); object_wrap->do_wrap(object); info.GetReturnValue().Set(object); } NAN_MODULE_INIT(TransferObjectWrap::Init) { auto klass = Nan::New(TransferObjectWrap::NewInstance); klass->SetClassName(Nan::New("TransferObjectWrap").ToLocalChecked()); klass->InstanceTemplate()->SetInternalFieldCount(1); constructor().Reset(Nan::GetFunction(klass).ToLocalChecked()); } NAN_METHOD(TransferObjectWrap::NewInstance) { if(!info.IsConstructCall()) Nan::ThrowError("invalid invoke!"); } void TransferObjectWrap::do_wrap(v8::Local object) { this->Wrap(object); auto source = dynamic_pointer_cast(this->target()); auto target = dynamic_pointer_cast(this->target()); auto direction = source ? "upload" : "download"; Nan::Set(object, Nan::New("direction").ToLocalChecked(), v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), direction).ToLocalChecked() ); Nan::Set(object, Nan::New("name").ToLocalChecked(), v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), this->target()->name().c_str()).ToLocalChecked() ); if(source) { Nan::Set(object, Nan::New("total_size").ToLocalChecked(), Nan::New(source->byte_length()) ); } if(target) { Nan::Set(object, Nan::New("expected_size").ToLocalChecked(), Nan::New(target->expected_length()) ); } } #endif TransferFileSource::TransferFileSource(std::string path, std::string name) : _path{std::move(path)}, _name{std::move(name)} { if(!this->_path.empty()) { if(this->_path.back() == '/') this->_path.pop_back(); #ifdef WIN32 if(this->_path.back() == '\\') this->_path.pop_back(); #endif } } #ifdef WIN32 #define u8path path #endif uint64_t TransferFileSource::byte_length() const { if(file_size.has_value()) return file_size.value(); auto file = fs::u8path(this->_path) / fs::u8path(this->_name); error_code error; auto size = fs::file_size(file,error); if(error) size = 0; return (this->file_size = std::make_optional(size)).value(); } bool TransferFileSource::initialize(std::string &error) { auto file = fs::u8path(this->_path) / fs::u8path(this->_name); error_code errc; if(!fs::exists(file)) { error = "file not found"; return false; } if(errc) { error = "failed to test for file existence: " + to_string(errc.value()) + "/" + errc.message(); return false; } if(!fs::is_regular_file(file, errc)) { error = "target file isn't a regular file"; return false; } if(errc) { error = "failed to test for file regularity: " + to_string(errc.value()) + "/" + errc.message(); return false; } this->file_stream = std::ifstream{file, std::ifstream::in | std::ifstream::binary}; if(!this->file_stream) { error = "failed to open file"; return false; } this->file_stream.seekg(0, std::ifstream::end); auto length = this->file_stream.tellg(); if(length != this->byte_length()) { error = "file length missmatch"; return false; } this->file_stream.seekg(0, std::ifstream::beg); this->position = 0; return true; } void TransferFileSource::finalize() { if(this->file_stream) this->file_stream.close(); this->position = 0; } error::value TransferFileSource::read_bytes(std::string &error, uint8_t *buffer, uint64_t &length) { auto result = this->file_stream.readsome((char*) buffer, length); if(result > 0) { length = result; this->position += result; return error::success; } if(!this->file_stream) { if(this->file_stream.eof()) error = "eof reached"; else error = "io error. failed to read"; } else { error = "read returned " + to_string(result) + "/" + to_string(length); } return error::custom; } uint64_t TransferFileSource::stream_index() const { return this->position; } #ifdef NODEJS_API NAN_METHOD(TransferFileSource::create) { if(info.Length() != 2 || !info[0]->IsString() || !info[1]->IsString()) { Nan::ThrowError("invalid argument"); return; } auto instance = make_shared(*Nan::Utf8String{info[0]}, *Nan::Utf8String{info[1]}); auto object_wrap = new TransferObjectWrap(instance); auto object = Nan::NewInstance(Nan::New(TransferObjectWrap::constructor()), 0, nullptr).ToLocalChecked(); object_wrap->do_wrap(object); info.GetReturnValue().Set(object); } #endif