Files
2013-04-25 09:59:27 -07:00

229 lines
7.0 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "avmshell.h"
namespace avmshell
{
FileClass::FileClass(avmplus::VTable *cvtable)
: avmplus::ClassClosure(cvtable)
{
createVanillaPrototype();
}
bool FileClass::exists(avmplus::Stringp filename)
{
if (!filename) {
toplevel()->throwArgumentError(kNullArgumentError, "filename");
}
bool result = false;
avmplus::StUTF8String filenameUTF8(filename);
File* fp = Platform::GetInstance()->createFile();
if(fp)
{
if (fp->open(filenameUTF8.c_str(), File::OPEN_READ)) {
result = true;
fp->close();
}
Platform::GetInstance()->destroyFile(fp);
}
return result;
}
avmplus::Stringp FileClass::read(avmplus::Stringp filename)
{
avmplus::Toplevel* toplevel = this->toplevel();
avmplus::AvmCore* core = this->core();
if (!filename) {
toplevel->throwArgumentError(kNullArgumentError, "filename");
}
avmplus::StUTF8String filenameUTF8(filename);
File* fp = Platform::GetInstance()->createFile();
if(!fp || !fp->open(filenameUTF8.c_str(), File::OPEN_READ))
{
if(fp)
{
Platform::GetInstance()->destroyFile(fp);
}
toplevel->throwError(kFileOpenError, filename);
}
int64_t fileSize = fp->size();
if(fileSize >= (int64_t)INT32_T_MAX) //File APIs cannot handle files > 2GB
{
toplevel->throwRangeError(kOutOfRangeError, filename);
}
int len = (int)fileSize;
// Avoid avmStackAlloc - the buffer can be large and the memory is non-pointer-containing,
// but the GC will scan it conservatively.
uint8_t* c = (uint8_t*)core->gc->Alloc(len+1);
len = (int)fp->read(c, len); //need to force since the string creation functions expect an int
c[len] = 0;
fp->close();
Platform::GetInstance()->destroyFile(fp);
avmplus::Stringp ret = NULL;
if (len >= 3)
{
// UTF8 BOM
if ((c[0] == 0xef) && (c[1] == 0xbb) && (c[2] == 0xbf))
{
ret = core->newStringUTF8((const char*)c + 3, len - 3);
}
else if ((c[0] == 0xfe) && (c[1] == 0xff))
{
//UTF-16 big endian
c += 2;
len = (len - 2) >> 1;
ret = core->newStringEndianUTF16(/*littleEndian*/false, (wchar*)(void*)c, len);
}
else if ((c[0] == 0xff) && (c[1] == 0xfe))
{
//UTF-16 little endian
c += 2;
len = (len - 2) >> 1;
ret = core->newStringEndianUTF16(/*littleEndian*/true, (wchar*)(void*)c, len);
}
}
if (ret == NULL)
{
// Use newStringUTF8() with "strict" explicitly set to false to mimick old,
// buggy behavior, where malformed UTF-8 sequences are stored as single characters.
ret = core->newStringUTF8((const char*)c, len, false);
}
core->gc->Free(c);
return ret;
}
void FileClass::write(avmplus::Stringp filename,
avmplus::Stringp data)
{
avmplus::Toplevel* toplevel = this->toplevel();
if (!filename) {
toplevel->throwArgumentError(kNullArgumentError, "filename");
}
if (!data) {
toplevel->throwArgumentError(kNullArgumentError, "data");
}
avmplus::StUTF8String filenameUTF8(filename);
File* fp = Platform::GetInstance()->createFile();
if (!fp || !fp->open(filenameUTF8.c_str(), File::OPEN_WRITE))
{
if(fp)
{
Platform::GetInstance()->destroyFile(fp);
}
toplevel->throwError(kFileWriteError, filename);
}
avmplus::StUTF8String dataUTF8(data);
if ((int32_t)fp->write(dataUTF8.c_str(), dataUTF8.length()) != dataUTF8.length()) {
toplevel->throwError(kFileWriteError, filename);
}
fp->close();
Platform::GetInstance()->destroyFile(fp);
}
avmplus::ByteArrayObject* FileClass::readByteArray(avmplus::Stringp filename)
{
avmplus::Toplevel* toplevel = this->toplevel();
if (!filename) {
toplevel->throwArgumentError(kNullArgumentError, "filename");
}
avmplus::StUTF8String filenameUTF8(filename);
File* fp = Platform::GetInstance()->createFile();
if (fp == NULL || !fp->open(filenameUTF8.c_str(), File::OPEN_READ_BINARY))
{
if(fp)
{
Platform::GetInstance()->destroyFile(fp);
}
toplevel->throwError(kFileOpenError, filename);
}
int64_t len = fp->size();
if((uint64_t)len >= UINT32_T_MAX) //ByteArray APIs cannot handle files > 4GB
{
toplevel->throwRangeError(kOutOfRangeError, filename);
}
uint32_t readCount = (uint32_t)len;
unsigned char *c = mmfx_new_array( unsigned char, readCount+1);
avmplus::ByteArrayObject* b = toplevel->byteArrayClass()->constructByteArray();
b->set_length(0);
while (readCount > 0)
{
uint32_t actual = (uint32_t) fp->read(c, readCount);
if (actual > 0)
{
b->GetByteArray().Write(c, actual);
readCount -= readCount;
}
else
{
break;
}
}
b->set_position(0);
mmfx_delete_array( c );
fp->close();
Platform::GetInstance()->destroyFile(fp);
return b;
}
bool FileClass::writeByteArray(avmplus::Stringp filename, avmplus::ByteArrayObject* bytes)
{
avmplus::Toplevel* toplevel = this->toplevel();
if (!filename) {
toplevel->throwArgumentError(kNullArgumentError, "filename");
}
avmplus::StUTF8String filenameUTF8(filename);
File* fp = Platform::GetInstance()->createFile();
if (fp == NULL || !fp->open(filenameUTF8.c_str(), File::OPEN_WRITE_BINARY))
{
if(fp)
{
Platform::GetInstance()->destroyFile(fp);
}
toplevel->throwError(kFileWriteError, filename);
}
int32_t len = bytes->get_length();
bool success = (int32_t)fp->write(&(bytes->GetByteArray())[0], len) == len;
fp->close();
Platform::GetInstance()->destroyFile(fp);
if (!success) {
toplevel->throwError(kFileWriteError, filename);
}
return true;
}
}