<commit_before>#include "mmapFile.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#ifdef ENABLE_UDC
extern "C" {
#include "common.h"
#include "udc.h"
}
#endif

/* constants for header */
static const std::string FORMAT_NAME = "HAL-MMAP";
static const std::string MMAP_VERSION = "1.0";

/* is file a URL that requires UDC? */
static bool isUdcUrl(const std::string alignmentPath) {
    return (alignmentPath.find("http:") == 0) or (alignmentPath.find("https:") == 0)
        or (alignmentPath.find("ftp:") == 0);
}

/* get the file size from the OS */
static size_t getFileStatSize(int fd) {
    struct stat fileStat;
    if (::fstat(fd, &fileStat) < 0) {
        throw hal_errno_exception("stat failed", errno);
    }
    return fileStat.st_size;
}

/* constructor, used only by derived classes */
hal::MMapFile::MMapFile(const std::string alignmentPath,
                        unsigned mode,
                        bool mustFetch):
    _alignmentPath(alignmentPath),  _mode(halDefaultAccessMode(mode)),
    _basePtr(NULL), _fileSize(0), _mustFetch(mustFetch) {
}

/* error if file is not open for write accecss */
void hal::MMapFile::validateWriteAccess() const {
    if ((_mode & WRITE_ACCESS) == 0) {
        throw hal_exception(_alignmentPath + " is not open for write access");
    }
}

/* setup pointer to header */
void hal::MMapFile::setHeaderPtr() {
    fetchIfNeeded(0, sizeof(mmapHeader));
    _header = static_cast<mmapHeader*>(_basePtr);
}

/* validate the file header and save a pointer to it. */
void hal::MMapFile::loadHeader(bool markDirty) {
    if (_fileSize < sizeof(mmapHeader)) {
        throw hal_exception(_alignmentPath + ": file size of " + std::to_string(_fileSize)
                            + " is less that header size of " + std::to_string(sizeof(mmapHeader)));
    }
    setHeaderPtr();

    // don't print found strings as it might have garbage
    if (::strcmp(_header->format, FORMAT_NAME.c_str()) != 0) {
        throw hal_exception(_alignmentPath + ": invalid file header, expected format name of '" +
                            FORMAT_NAME + "'");
    }
    // FIXME: to need to check version compatibility
    if (::strcmp(_header->mmapVersion, MMAP_VERSION.c_str()) != 0) {
        throw hal_exception(_alignmentPath + ": incompatible mmap format versions: "
                            + "file version " + _header->mmapVersion
                            + ", mmap API version " + MMAP_VERSION);
    }
    if ((_header->nextOffset < sizeof(mmapHeader)) or (_header->nextOffset > _fileSize)) {
        throw hal_exception(_alignmentPath + ": header nextOffset field out of bounds, probably file corruption");
    }
    if ((_header->rootOffset < sizeof(mmapHeader)) or (_header->rootOffset > _fileSize)) {
        throw hal_exception(_alignmentPath + ": header rootOffset field out of bounds, probably file corruption");
    }
    if (_header->dirty) {
        throw hal_exception(_alignmentPath + ": file is marked as dirty, most likely an inconsistent state.");
    }
    if (markDirty) {
        _header->dirty = true;
    }
}

/* create the header */
void hal::MMapFile::createHeader() {
    assert(_mode & WRITE_ACCESS);
    setHeaderPtr();
    assert(FORMAT_NAME.size() < sizeof(_header->format));
    strncpy(_header->format, FORMAT_NAME.c_str(), sizeof(_header->format)-1);
    assert(MMAP_VERSION.size() < sizeof(_header->mmapVersion));
    strncpy(_header->mmapVersion, MMAP_VERSION.c_str(), sizeof(_header->mmapVersion)-1);
    assert(HAL_VERSION.size() < sizeof(_header->halVersion));
    strncpy(_header->halVersion, HAL_VERSION.c_str(), sizeof(_header->halVersion)-1);
    _header->nextOffset = alignRound(sizeof(mmapHeader));
    _header->dirty = true;
    _header->nextOffset = _header->nextOffset;
}

/* Grow file to allow for at least the specified amount.  This remaps the *
 * file, so the same sufficient virtual space must be available at the address
 * and it is expensive. */
void hal::MMapFile::growFile(size_t size) {
    assert(_mode & WRITE_ACCESS);
    growFileImpl(size);
}

/* override this for classes that support growing file */
void hal::MMapFile::growFileImpl(size_t size) {
    throw hal_exception("logic error: growFile() not available for this MMapFile implementation");
}

namespace hal {
    /* Class that implements local file version of MMapFile */
    class MMapFileLocal: public MMapFile {
        public:
        MMapFileLocal(const std::string& alignmentPath,
                      unsigned mode,
                      size_t initSize,
                      size_t growSize);
        virtual void close();
        virtual ~MMapFileLocal();

        private:
        int openFile();
        void closeFile();
        void adjustFileSize(size_t size);
        void* mapFile(void *requiredAddr=NULL);
        void unmapFile();
        void openRead();
        void openWrite(size_t initSize);
        void growFileImpl(size_t size);

        int _fd;              // open file descriptor
        size_t _growSize;     // amount to grow file by when needed.
    };
}

/* Constructor. Open or create the specified file. */
hal::MMapFileLocal::MMapFileLocal(const std::string& alignmentPath,
                                  unsigned mode,
                                  size_t initSize,
                                  size_t growSize):
    MMapFile(alignmentPath, mode, false), _fd(-1), _growSize(growSize) {
    if (_mode & WRITE_ACCESS) {
        openWrite(initSize);
    } else {
        openRead();
    }
}

/* close file, marking as clean.  Don't  */
void hal::MMapFileLocal::close() {
    if (_basePtr == NULL) {
        throw hal_exception(_alignmentPath + ": MMapFile::close() called on closed file");
    }
    if (_mode & WRITE_ACCESS) {
        adjustFileSize(_header->nextOffset);
        _header->dirty = false;
    }
    unmapFile();
    closeFile();
}

/* Destructor. write fields to header and close.  If write access and close
 * has not been called, file will me left mark dirty */
hal::MMapFileLocal::~MMapFileLocal() {
    unmapFile();
    closeFile();
}

/* open the file for the specified mode */
int hal::MMapFileLocal::openFile() {
    assert(_fd < 0);
    unsigned openMode = 0;
    if (_mode & WRITE_ACCESS) {
        openMode = O_RDWR | ((_mode & CREATE_ACCESS) ? (O_CREAT|O_TRUNC) : 0);
    } else {
        openMode = O_RDONLY;
    }
    int fd = ::open(_alignmentPath.c_str(), openMode, 0666);
    if (fd < 0) {
        throw hal_errno_exception(_alignmentPath, "open failed", errno);
    }
    return fd;
}

/* change size size of the file, possibly deleting data. */
void hal::MMapFileLocal::adjustFileSize(size_t size) {
    if (ftruncate(_fd, size) < 0) {
        throw hal_errno_exception(_alignmentPath, "set size failed", errno);
    }
    _fileSize = size;
}

/* map file into memory */
void* hal::MMapFileLocal::mapFile(void *requiredAddr) {
    assert(_basePtr == NULL);
    unsigned prot = PROT_READ | ((_mode & WRITE_ACCESS) ? PROT_WRITE : 0);
    void *ptr = mmap(requiredAddr, _fileSize, prot, MAP_SHARED|MAP_FILE, _fd, 0);
    if (ptr == MAP_FAILED) {
        throw hal_errno_exception(_alignmentPath, "mmap failed", errno);
    }
    return ptr;
}

/* unmap file, if mapped */
void hal::MMapFileLocal::unmapFile() {
    if (_basePtr != NULL) {
        if (::munmap(const_cast<void*>(_basePtr), _fileSize) < 0) {
            throw hal_errno_exception(_alignmentPath, "munmap failed", errno);
        }
        _basePtr = NULL;
    }
}

/* open the file for read access */
void hal::MMapFileLocal::openRead() {
    _fd = openFile();
    _fileSize = getFileStatSize(_fd);
    _basePtr = mapFile();
    loadHeader(false);
}

/* open the file for write access */
void hal::MMapFileLocal::openWrite(size_t initSize) {
    _fd = openFile();
    if (_mode & CREATE_ACCESS) {
        adjustFileSize(0);  // clear out existing data
    }
    if (initSize > _fileSize) {
        adjustFileSize(initSize);
    }
    _basePtr = mapFile();
    if (_mode & CREATE_ACCESS) {
        createHeader();
    } else {
        loadHeader(true);
    }
}

/* close the file if open */
void hal::MMapFileLocal::closeFile() {
    if (_fd >= 0) {
        if (::close(_fd) < 0) {
            throw hal_errno_exception(_alignmentPath, "close failed", errno);
        }
        _fd = -1;
    }
}

/* grow file  */
void hal::MMapFileLocal::growFileImpl(size_t size) {
    assert(_mode & WRITE_ACCESS);
    size_t newSize = _growSize;
    if (newSize < size) {
        newSize += size;  // will leave in extra
    }
    void * requiredAddr = _basePtr;
    unmapFile();
    _basePtr = mapFile(requiredAddr);
}

#ifdef ENABLE_UDC
namespace hal {
    /* Class that implements UDC file version of MMapFile */
    class MMapFileUdc: public MMapFile {
        public:
        MMapFileUdc(const std::string& alignmentPath,
                    unsigned mode,
                    size_t initSize,
                    size_t growSize,
                    const std::string& udcCacheDir);
        virtual void close();
        virtual ~MMapFileUdc();

        protected:
        virtual void fetch(size_t offset,
                           size_t accessSize) const;

        private:
        struct udcFile *_udcFile;
    };
}

/* Constructor. Open or create the specified file. */
hal::MMapFileUdc::MMapFileUdc(const std::string& alignmentPath,
                              unsigned mode,
                              size_t initSize,
                              size_t growSize,
                              const std::string& udcCacheDir):
    MMapFile(alignmentPath, mode, true), _udcFile(NULL) {
    if (_mode & WRITE_ACCESS) {
        throw hal_exception("write access not supported for UDC:" + alignmentPath);
    }
    _udcFile = udcFileOpen(const_cast<char*>(alignmentPath.c_str()),
                           (udcCacheDir.empty()) ? NULL : const_cast<char*>(udcCacheDir.c_str()));
    udcMMap(_udcFile);

    // get base point and fetch header
    _basePtr = udcMMapFetch(_udcFile, 0, sizeof(mmapHeader));
    _fileSize = udcSizeFromCache(const_cast<char*>(_alignmentPath.c_str()),
                                 const_cast<char*>(udcCacheDir.c_str()));
    loadHeader(false);
}

/* close file, marking as clean.  Don't  */
void hal::MMapFileUdc::close() {
    if (_basePtr == NULL) {
        throw hal_exception(_alignmentPath + ": MMapFile::close() called on closed file");
    }
    udcFileClose(&_udcFile);
}

/* Destructor. write fields to header and close.  If write access and close
 * has not been called, file will me left mark dirty */
hal::MMapFileUdc::~MMapFileUdc() {
    if (_udcFile != NULL) {
        udcFileClose(&_udcFile);
    }
}

/* fetch into UDC cache */
void hal::MMapFileUdc::fetch(size_t offset,
                             size_t accessSize) const {
    if ((offset < _fileSize) and (offset + accessSize) > _fileSize) {
        // FIXME  - length off end, iterator does this
        accessSize = _fileSize - offset;
    }

    udcMMapFetch(_udcFile, offset, accessSize);
}

#endif

/** create a MMapFile object, opening a local file */
hal::MMapFile *hal::MMapFile::factory(const std::string& alignmentPath,
                                      unsigned mode,
                                      size_t initSize,
                                      size_t growSize,
                                      const std::string& udcCacheDir) {
    if (isUdcUrl(alignmentPath)) {
        if (mode & (CREATE_ACCESS | WRITE_ACCESS)) {
            throw hal_exception("create or write access not support with URL: " + alignmentPath);
        }
#ifdef ENABLE_UDC
        return new MMapFileUdc(alignmentPath, mode, initSize, growSize, udcCacheDir);
#else
        throw hal_exception("URL access requires UDC support to be compiled into HAL library: " + alignmentPath);
#endif
    } else {
        return new MMapFileLocal(alignmentPath, mode, initSize, growSize);
    }
}

<commit_msg>Make growFileImpl grow the file<commit_after>#include "mmapFile.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#ifdef ENABLE_UDC
extern "C" {
#include "common.h"
#include "udc.h"
}
#endif

/* constants for header */
static const std::string FORMAT_NAME = "HAL-MMAP";
static const std::string MMAP_VERSION = "1.0";

/* is file a URL that requires UDC? */
static bool isUdcUrl(const std::string alignmentPath) {
    return (alignmentPath.find("http:") == 0) or (alignmentPath.find("https:") == 0)
        or (alignmentPath.find("ftp:") == 0);
}

/* get the file size from the OS */
static size_t getFileStatSize(int fd) {
    struct stat fileStat;
    if (::fstat(fd, &fileStat) < 0) {
        throw hal_errno_exception("stat failed", errno);
    }
    return fileStat.st_size;
}

/* constructor, used only by derived classes */
hal::MMapFile::MMapFile(const std::string alignmentPath,
                        unsigned mode,
                        bool mustFetch):
    _alignmentPath(alignmentPath),  _mode(halDefaultAccessMode(mode)),
    _basePtr(NULL), _fileSize(0), _mustFetch(mustFetch) {
}

/* error if file is not open for write accecss */
void hal::MMapFile::validateWriteAccess() const {
    if ((_mode & WRITE_ACCESS) == 0) {
        throw hal_exception(_alignmentPath + " is not open for write access");
    }
}

/* setup pointer to header */
void hal::MMapFile::setHeaderPtr() {
    fetchIfNeeded(0, sizeof(mmapHeader));
    _header = static_cast<mmapHeader*>(_basePtr);
}

/* validate the file header and save a pointer to it. */
void hal::MMapFile::loadHeader(bool markDirty) {
    if (_fileSize < sizeof(mmapHeader)) {
        throw hal_exception(_alignmentPath + ": file size of " + std::to_string(_fileSize)
                            + " is less that header size of " + std::to_string(sizeof(mmapHeader)));
    }
    setHeaderPtr();

    // don't print found strings as it might have garbage
    if (::strcmp(_header->format, FORMAT_NAME.c_str()) != 0) {
        throw hal_exception(_alignmentPath + ": invalid file header, expected format name of '" +
                            FORMAT_NAME + "'");
    }
    // FIXME: to need to check version compatibility
    if (::strcmp(_header->mmapVersion, MMAP_VERSION.c_str()) != 0) {
        throw hal_exception(_alignmentPath + ": incompatible mmap format versions: "
                            + "file version " + _header->mmapVersion
                            + ", mmap API version " + MMAP_VERSION);
    }
    if ((_header->nextOffset < sizeof(mmapHeader)) or (_header->nextOffset > _fileSize)) {
        throw hal_exception(_alignmentPath + ": header nextOffset field out of bounds, probably file corruption");
    }
    if ((_header->rootOffset < sizeof(mmapHeader)) or (_header->rootOffset > _fileSize)) {
        throw hal_exception(_alignmentPath + ": header rootOffset field out of bounds, probably file corruption");
    }
    if (_header->dirty) {
        throw hal_exception(_alignmentPath + ": file is marked as dirty, most likely an inconsistent state.");
    }
    if (markDirty) {
        _header->dirty = true;
    }
}

/* create the header */
void hal::MMapFile::createHeader() {
    assert(_mode & WRITE_ACCESS);
    setHeaderPtr();
    assert(FORMAT_NAME.size() < sizeof(_header->format));
    strncpy(_header->format, FORMAT_NAME.c_str(), sizeof(_header->format)-1);
    assert(MMAP_VERSION.size() < sizeof(_header->mmapVersion));
    strncpy(_header->mmapVersion, MMAP_VERSION.c_str(), sizeof(_header->mmapVersion)-1);
    assert(HAL_VERSION.size() < sizeof(_header->halVersion));
    strncpy(_header->halVersion, HAL_VERSION.c_str(), sizeof(_header->halVersion)-1);
    _header->nextOffset = alignRound(sizeof(mmapHeader));
    _header->dirty = true;
    _header->nextOffset = _header->nextOffset;
}

/* Grow file to allow for at least the specified amount.  This remaps the *
 * file, so the same sufficient virtual space must be available at the address
 * and it is expensive. */
void hal::MMapFile::growFile(size_t size) {
    assert(_mode & WRITE_ACCESS);
    growFileImpl(size);
}

/* override this for classes that support growing file */
void hal::MMapFile::growFileImpl(size_t size) {
    throw hal_exception("logic error: growFile() not available for this MMapFile implementation");
}

namespace hal {
    /* Class that implements local file version of MMapFile */
    class MMapFileLocal: public MMapFile {
        public:
        MMapFileLocal(const std::string& alignmentPath,
                      unsigned mode,
                      size_t initSize,
                      size_t growSize);
        virtual void close();
        virtual ~MMapFileLocal();

        private:
        int openFile();
        void closeFile();
        void adjustFileSize(size_t size);
        void* mapFile(void *requiredAddr=NULL);
        void unmapFile();
        void openRead();
        void openWrite(size_t initSize);
        void growFileImpl(size_t size);

        int _fd;              // open file descriptor
        size_t _growSize;     // amount to grow file by when needed.
    };
}

/* Constructor. Open or create the specified file. */
hal::MMapFileLocal::MMapFileLocal(const std::string& alignmentPath,
                                  unsigned mode,
                                  size_t initSize,
                                  size_t growSize):
    MMapFile(alignmentPath, mode, false), _fd(-1), _growSize(growSize) {
    if (_mode & WRITE_ACCESS) {
        openWrite(initSize);
    } else {
        openRead();
    }
}

/* close file, marking as clean.  Don't  */
void hal::MMapFileLocal::close() {
    if (_basePtr == NULL) {
        throw hal_exception(_alignmentPath + ": MMapFile::close() called on closed file");
    }
    if (_mode & WRITE_ACCESS) {
        adjustFileSize(_header->nextOffset);
        _header->dirty = false;
    }
    unmapFile();
    closeFile();
}

/* Destructor. write fields to header and close.  If write access and close
 * has not been called, file will me left mark dirty */
hal::MMapFileLocal::~MMapFileLocal() {
    unmapFile();
    closeFile();
}

/* open the file for the specified mode */
int hal::MMapFileLocal::openFile() {
    assert(_fd < 0);
    unsigned openMode = 0;
    if (_mode & WRITE_ACCESS) {
        openMode = O_RDWR | ((_mode & CREATE_ACCESS) ? (O_CREAT|O_TRUNC) : 0);
    } else {
        openMode = O_RDONLY;
    }
    int fd = ::open(_alignmentPath.c_str(), openMode, 0666);
    if (fd < 0) {
        throw hal_errno_exception(_alignmentPath, "open failed", errno);
    }
    return fd;
}

/* change size size of the file, possibly deleting data. */
void hal::MMapFileLocal::adjustFileSize(size_t size) {
    if (ftruncate(_fd, size) < 0) {
        throw hal_errno_exception(_alignmentPath, "set size failed", errno);
    }
    _fileSize = size;
}

/* map file into memory */
void* hal::MMapFileLocal::mapFile(void *requiredAddr) {
    assert(_basePtr == NULL);
    unsigned prot = PROT_READ | ((_mode & WRITE_ACCESS) ? PROT_WRITE : 0);
    void *ptr = mmap(requiredAddr, _fileSize, prot, MAP_SHARED|MAP_FILE, _fd, 0);
    if (ptr == MAP_FAILED) {
        throw hal_errno_exception(_alignmentPath, "mmap failed", errno);
    }
    return ptr;
}

/* unmap file, if mapped */
void hal::MMapFileLocal::unmapFile() {
    if (_basePtr != NULL) {
        if (::munmap(const_cast<void*>(_basePtr), _fileSize) < 0) {
            throw hal_errno_exception(_alignmentPath, "munmap failed", errno);
        }
        _basePtr = NULL;
    }
}

/* open the file for read access */
void hal::MMapFileLocal::openRead() {
    _fd = openFile();
    _fileSize = getFileStatSize(_fd);
    _basePtr = mapFile();
    loadHeader(false);
}

/* open the file for write access */
void hal::MMapFileLocal::openWrite(size_t initSize) {
    _fd = openFile();
    if (_mode & CREATE_ACCESS) {
        adjustFileSize(0);  // clear out existing data
    }
    if (initSize > _fileSize) {
        adjustFileSize(initSize);
    }
    _basePtr = mapFile();
    if (_mode & CREATE_ACCESS) {
        createHeader();
    } else {
        loadHeader(true);
    }
}

/* close the file if open */
void hal::MMapFileLocal::closeFile() {
    if (_fd >= 0) {
        if (::close(_fd) < 0) {
            throw hal_errno_exception(_alignmentPath, "close failed", errno);
        }
        _fd = -1;
    }
}

/* grow file  */
void hal::MMapFileLocal::growFileImpl(size_t size) {
    assert(_mode & WRITE_ACCESS);
    size_t newSize = _growSize;
    if (newSize < size) {
        newSize += size;  // will leave in extra
    }
    void * requiredAddr = _basePtr;
    unmapFile();
    adjustFileSize(newSize);
    _basePtr = mapFile(requiredAddr);
}

#ifdef ENABLE_UDC
namespace hal {
    /* Class that implements UDC file version of MMapFile */
    class MMapFileUdc: public MMapFile {
        public:
        MMapFileUdc(const std::string& alignmentPath,
                    unsigned mode,
                    size_t initSize,
                    size_t growSize,
                    const std::string& udcCacheDir);
        virtual void close();
        virtual ~MMapFileUdc();

        protected:
        virtual void fetch(size_t offset,
                           size_t accessSize) const;

        private:
        struct udcFile *_udcFile;
    };
}

/* Constructor. Open or create the specified file. */
hal::MMapFileUdc::MMapFileUdc(const std::string& alignmentPath,
                              unsigned mode,
                              size_t initSize,
                              size_t growSize,
                              const std::string& udcCacheDir):
    MMapFile(alignmentPath, mode, true), _udcFile(NULL) {
    if (_mode & WRITE_ACCESS) {
        throw hal_exception("write access not supported for UDC:" + alignmentPath);
    }
    _udcFile = udcFileOpen(const_cast<char*>(alignmentPath.c_str()),
                           (udcCacheDir.empty()) ? NULL : const_cast<char*>(udcCacheDir.c_str()));
    udcMMap(_udcFile);

    // get base point and fetch header
    _basePtr = udcMMapFetch(_udcFile, 0, sizeof(mmapHeader));
    _fileSize = udcSizeFromCache(const_cast<char*>(_alignmentPath.c_str()),
                                 const_cast<char*>(udcCacheDir.c_str()));
    loadHeader(false);
}

/* close file, marking as clean.  Don't  */
void hal::MMapFileUdc::close() {
    if (_basePtr == NULL) {
        throw hal_exception(_alignmentPath + ": MMapFile::close() called on closed file");
    }
    udcFileClose(&_udcFile);
}

/* Destructor. write fields to header and close.  If write access and close
 * has not been called, file will me left mark dirty */
hal::MMapFileUdc::~MMapFileUdc() {
    if (_udcFile != NULL) {
        udcFileClose(&_udcFile);
    }
}

/* fetch into UDC cache */
void hal::MMapFileUdc::fetch(size_t offset,
                             size_t accessSize) const {
    if ((offset < _fileSize) and (offset + accessSize) > _fileSize) {
        // FIXME  - length off end, iterator does this
        accessSize = _fileSize - offset;
    }

    udcMMapFetch(_udcFile, offset, accessSize);
}

#endif

/** create a MMapFile object, opening a local file */
hal::MMapFile *hal::MMapFile::factory(const std::string& alignmentPath,
                                      unsigned mode,
                                      size_t initSize,
                                      size_t growSize,
                                      const std::string& udcCacheDir) {
    if (isUdcUrl(alignmentPath)) {
        if (mode & (CREATE_ACCESS | WRITE_ACCESS)) {
            throw hal_exception("create or write access not support with URL: " + alignmentPath);
        }
#ifdef ENABLE_UDC
        return new MMapFileUdc(alignmentPath, mode, initSize, growSize, udcCacheDir);
#else
        throw hal_exception("URL access requires UDC support to be compiled into HAL library: " + alignmentPath);
#endif
    } else {
        return new MMapFileLocal(alignmentPath, mode, initSize, growSize);
    }
}

<|endoftext|>


<commit_before>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/contrib/tensorrt/convert/trt_optimization_pass.h"
#include "tensorflow/contrib/tensorrt/convert/convert_graph.h"
#include "tensorflow/core/grappler/clusters/cluster.h"
#include "tensorflow/core/grappler/grappler_item.h"
#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.h"
#include "tensorflow/core/lib/strings/numbers.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/logging.h"

#if GOOGLE_CUDA
#if GOOGLE_TENSORRT
namespace tensorflow {
namespace tensorrt {
namespace convert {
// TODO(sami): Remove VLOG messages once the code matures
using tensorflow::str_util::Uppercase;
using tensorflow::strings::StrAppend;
using tensorflow::strings::StrCat;

tensorflow::Status TRTOptimizationPass::Init(
    const tensorflow::RewriterConfig_CustomGraphOptimizer* config) {
  VLOG(1) << "Called INIT for " << name_ << " with config = " << config;
  if (config == nullptr) {
    maximum_workspace_size_ = 2 << 30;
    return tensorflow::Status::OK();
  }
  const auto params = config->parameter_map();
  if (params.count("minimum_segment_size")) {
    minimum_segment_size_ = params.at("minimum_segment_size").i();
  }
  if (params.count("max_batch_size")) {
    maximum_batch_size_ = params.at("max_batch_size").i();
  }
  is_dynamic_op_ = false;
  if (params.count("is_dynamic_op")) {
    is_dynamic_op_ = params.at("is_dynamic_op").b();
  }
  if (params.count("cached_engine_batches")) {
    auto batch_vec = params.at("cached_engine_batches").list();
    batches_.reserve(batch_vec.i_size());
    for (const auto i : batch_vec.i()) {
      batches_.push_back(i);
    }
  }
  max_cached_batches_ = 1;
  if (params.count("maximum_cached_engines")) {
    max_cached_batches_ = params.at("maximum_cached_engines").i();
  }
  if (params.count("max_workspace_size_bytes")) {
    maximum_workspace_size_ = params.at("max_workspace_size_bytes").i();
  }
  if (params.count("precision_mode")) {
    string pm = Uppercase(params.at("precision_mode").s());
    if (pm == "FP32") {
      precision_mode_ = 0;
    } else if (pm == "FP16") {
      precision_mode_ = 1;
    } else if (pm == "INT8") {
      precision_mode_ = 2;
    } else {
      LOG(ERROR) << "Unknown precision mode '" << pm << "'";
      return tensorflow::errors::InvalidArgument(
          "Unknown precision mode argument" + pm +
          " Valid values are FP32, FP16, INT8");
    }
  }
  return tensorflow::Status::OK();
}

void TRTOptimizationPass::PrintDebugInfo(
    tensorflow::grappler::Cluster* cluster,
    const tensorflow::grappler::GrapplerItem& item) {
  VLOG(1) << "Cluster = " << cluster;
  string offset("  ");
  string offset2 = StrCat(offset, offset);
  string offset3 = StrCat(offset2, offset);
  string offset4 = StrCat(offset2, offset2);
  if (cluster) {
    VLOG(1) << offset << "type             = " << cluster->type();
    VLOG(1) << offset << "num warmup steps = " << cluster->NumWarmupSteps();
    const auto dev_names = cluster->GetDeviceNames();
    if (dev_names.size()) {
      VLOG(1) << offset << " Device names:";
      for (const auto s : dev_names) {
        VLOG(1) << offset2 << s;
      }
    }
    std::unordered_map<string, uint64> peak_mem;
    auto status = cluster->GetPeakMemoryUsage(&peak_mem);
    if (status == tensorflow::Status::OK()) {
      VLOG(1) << offset << "Peak Memory Usage :";
      for (auto s : peak_mem) {
        VLOG(1) << offset2 << s.first << " = " << s.second;
      }
    }

    const auto dev_props = cluster->GetDevices();
    if (dev_props.size()) {
      VLOG(1) << offset << "Device properties:";
      for (auto k : dev_props) {
        VLOG(1) << offset2 << k.first;
        const auto& dt = k.second;
        VLOG(1) << offset3 << "type          = " << dt.type();
        VLOG(1) << offset3 << "vendor        = " << dt.vendor();
        VLOG(1) << offset3 << "model         = " << dt.model();
        VLOG(1) << offset3 << "frequency     = " << dt.frequency();
        VLOG(1) << offset3 << "num cores     = " << dt.num_cores();
        VLOG(1) << offset3 << "num registers = " << dt.num_registers();
        VLOG(1) << offset3 << "L1 cache size = " << dt.l1_cache_size();
        VLOG(1) << offset3 << "L2 cache size = " << dt.l2_cache_size();
        VLOG(1) << offset3 << "L3 cache size = " << dt.l3_cache_size();
        VLOG(1) << offset3 << "SHMem per SMP = "
                << dt.shared_memory_size_per_multiprocessor();
        VLOG(1) << offset3 << "memory size   = " << dt.memory_size();
        VLOG(1) << offset3 << "bandwidth     = " << dt.bandwidth();
        if (dt.environment_size()) {
          VLOG(1) << offset3 << "environment   :";
          for (const auto e : dt.environment()) {
            VLOG(1) << offset4 << e.first << " = " << e.second;
          }
        }
      }
    }
  }
  VLOG(1) << "item: " << item.id;
  if (item.feed.size()) {
    VLOG(1) << offset << "Feeds  :";
    for (const auto& f : item.feed) {
      const auto& shape = f.second.shape();
      VLOG(1) << offset2 << f.first << " = shaped " << shape.DebugString();
    }
  } else {
    VLOG(1) << offset << "No Feeds";
  }
  if (item.fetch.size()) {
    VLOG(1) << offset << "Fetches  :";
    for (const auto& f : item.fetch) {
      VLOG(1) << offset2 << f;
    }
  } else {
    VLOG(1) << offset << "No Fetches";
  }

  if (item.init_ops.size()) {
    VLOG(1) << offset << "init ops  :";
    for (const auto& f : item.init_ops) {
      VLOG(1) << offset2 << f;
    }
  } else {
    VLOG(1) << offset << "No init ops";
  }
  VLOG(1) << "Save Op = " << item.save_op;
  VLOG(1) << "Restore Op = " << item.restore_op;
  VLOG(1) << "save_restore_loc_tensor = " << item.save_restore_loc_tensor;
  if (item.keep_ops.size()) {
    VLOG(1) << offset << "keep ops  :";
    for (const auto& f : item.keep_ops) {
      VLOG(1) << offset2 << f;
    }
  } else {
    VLOG(1) << offset << "No keep ops";
  }
  VLOG(3) << item.graph.DebugString();
  for (const auto dev : cluster->GetDeviceSet()->devices()) {
    const auto& pname = dev->parsed_name();
    VLOG(1) << "Device name= " << dev->name()
            << " parsedname job= " << pname.job << " id= " << pname.id
            << " has_id: " << pname.has_id << " has_job: " << pname.has_job
            << "has_type: " << pname.has_type << " type =" << pname.type;
  }
}

tensorflow::Status TRTOptimizationPass::Optimize(
    tensorflow::grappler::Cluster* cluster,
    const tensorflow::grappler::GrapplerItem& item, GraphDef* optimized_graph) {
  VLOG(1) << "Called TRTOptimization Pass " << name_;
  if (VLOG_IS_ON(1)) {
    PrintDebugInfo(cluster, item);
  }
  // This is a hack to workaround optimizer issue. MetaOptimizer calls
  // optimization passes on function objects as well, we should not modify
  // generated funcdefs! This is fragile but we don't have any other option
  // until framework fixes it.
  if (item.id != "tf_graph") {
    LOG(WARNING) << name_
                 << " is probably called on funcdef! This optimizer must *NOT* "
                    "be called on function objects.";
    *optimized_graph = item.graph;
    return tensorflow::Status::OK();
  }
  int max_dim = -1;
  if (item.feed.size()) {
    for (const auto& f : item.feed) {
      const auto& shape = f.second.shape();
      if (shape.dims() > 0) {
        if (shape.dim_size(0) > max_dim) max_dim = shape.dim_size(0);
      }
    }
  }
  if (maximum_batch_size_ < 0) {  // automatic batch size from input
    if (max_dim > 0) {
      maximum_batch_size_ = max_dim;
      VLOG(1) << "Setting maximum batch size to " << max_dim;
    } else {
      maximum_batch_size_ = 128;
      LOG(WARNING) << "Maximum batch size is not set"
                      " and can't be deduced from inputs setting it to"
                   << maximum_batch_size_
                   << ". Suggest configuring it from configuration parameters";
    }
  } else {
    if (max_dim > maximum_batch_size_) {
      LOG(WARNING) << "Configured batch size " << maximum_batch_size_
                   << " is less than input batch size " << max_dim
                   << " adjusting maximum batch size to match input batch size";
    }
  }
  tensorflow::grappler::GraphProperties static_graph_properties(item);
  TF_RETURN_IF_ERROR(static_graph_properties.InferStatically(true));
  tensorflow::tensorrt::convert::ConversionParams cp;

  std::vector<string> nodes_to_preserve;
  for (const auto& n : item.NodesToPreserve()) {
    auto tokens = str_util::Split(n, ":");
    string s = tokens.at(0);
    for (int i = 1; i < tokens.size() - 1; ++i){
      StrAppend(&s, ":", tokens.at(i));
    }
    int dumm_port = -1;
    // If the last token is not an integer, it must be part of the name.
    // Otherwise it is port number.
    if (tokens.size() > 1 &&
       !strings::safe_strto32(tokens.back(), &dumm_port)) {
      StrAppend(&s, ":", tokens.back());
    }
    nodes_to_preserve.push_back(s);
  }
  cp.input_graph_def = &item.graph;
  cp.output_names = &nodes_to_preserve;
  cp.max_batch_size = maximum_batch_size_;
  cp.max_workspace_size_bytes = maximum_workspace_size_;
  cp.output_graph_def = optimized_graph;
  cp.precision_mode = precision_mode_;
  cp.minimum_segment_size = minimum_segment_size_;
  cp.graph_properties = &static_graph_properties;
  cp.cluster = cluster;
  cp.is_dyn_op = is_dynamic_op_;
  cp.cached_engine_batches = batches_;
  cp.max_cached_engines = max_cached_batches_;
  auto status = tensorflow::tensorrt::convert::ConvertAfterShapes(cp);
  VLOG(2) << optimized_graph->DebugString();
  VLOG(1) << "Returning from " << name_;
  return status;
}

void TRTOptimizationPass::Feedback(
    tensorflow::grappler::Cluster* cluster,
    const tensorflow::grappler::GrapplerItem& item,
    const GraphDef& optimized_graph, double result) {}

}  // namespace convert
}  // namespace tensorrt
}  // namespace tensorflow

class VerboseCustomGraphOptimizerRegistrar
    : public tensorflow::grappler::CustomGraphOptimizerRegistrar {
 public:
  VerboseCustomGraphOptimizerRegistrar(
      const tensorflow::grappler::CustomGraphOptimizerRegistry::Creator& cr,
      const tensorflow::string& name)
      : tensorflow::grappler::CustomGraphOptimizerRegistrar(cr, name) {
    VLOG(1) << "Constructing a CustomOptimizationPass registration object for "
            << name;
  }
};

static VerboseCustomGraphOptimizerRegistrar TRTOptimizationPass_Registrar(
    []() {
      VLOG(1)
          << "Instantiating CustomOptimizationPass object TensorRTOptimizer";
      return new tensorflow::tensorrt::convert::TRTOptimizationPass(
          "TensorRTOptimizer");
    },
    ("TensorRTOptimizer"));

#endif
#endif
<commit_msg>Update trt_optimization_pass.cc<commit_after>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/contrib/tensorrt/convert/trt_optimization_pass.h"
#include "tensorflow/contrib/tensorrt/convert/convert_graph.h"
#include "tensorflow/core/grappler/clusters/cluster.h"
#include "tensorflow/core/grappler/grappler_item.h"
#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.h"
#include "tensorflow/core/lib/strings/numbers.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/logging.h"

#if GOOGLE_CUDA
#if GOOGLE_TENSORRT
namespace tensorflow {
namespace tensorrt {
namespace convert {
// TODO(sami): Remove VLOG messages once the code matures
using tensorflow::str_util::Uppercase;
using tensorflow::strings::StrAppend;
using tensorflow::strings::StrCat;

tensorflow::Status TRTOptimizationPass::Init(
    const tensorflow::RewriterConfig_CustomGraphOptimizer* config) {
  VLOG(1) << "Called INIT for " << name_ << " with config = " << config;
  if (config == nullptr) {
    maximum_workspace_size_ = 2 << 30;
    return tensorflow::Status::OK();
  }
  const auto params = config->parameter_map();
  if (params.count("minimum_segment_size")) {
    minimum_segment_size_ = params.at("minimum_segment_size").i();
  }
  if (params.count("max_batch_size")) {
    maximum_batch_size_ = params.at("max_batch_size").i();
  }
  is_dynamic_op_ = false;
  if (params.count("is_dynamic_op")) {
    is_dynamic_op_ = params.at("is_dynamic_op").b();
  }
  if (params.count("cached_engine_batches")) {
    auto batch_vec = params.at("cached_engine_batches").list();
    batches_.reserve(batch_vec.i_size());
    for (const auto i : batch_vec.i()) {
      batches_.push_back(i);
    }
  }
  max_cached_batches_ = 1;
  if (params.count("maximum_cached_engines")) {
    max_cached_batches_ = params.at("maximum_cached_engines").i();
  }
  if (params.count("max_workspace_size_bytes")) {
    maximum_workspace_size_ = params.at("max_workspace_size_bytes").i();
  }
  if (params.count("precision_mode")) {
    string pm = Uppercase(params.at("precision_mode").s());
    if (pm == "FP32") {
      precision_mode_ = 0;
    } else if (pm == "FP16") {
      precision_mode_ = 1;
    } else if (pm == "INT8") {
      precision_mode_ = 2;
    } else {
      LOG(ERROR) << "Unknown precision mode '" << pm << "'";
      return tensorflow::errors::InvalidArgument(
          "Unknown precision mode argument" + pm +
          " Valid values are FP32, FP16, INT8");
    }
  }
  return tensorflow::Status::OK();
}

void TRTOptimizationPass::PrintDebugInfo(
    tensorflow::grappler::Cluster* cluster,
    const tensorflow::grappler::GrapplerItem& item) {
  VLOG(1) << "Cluster = " << cluster;
  string offset("  ");
  string offset2 = StrCat(offset, offset);
  string offset3 = StrCat(offset2, offset);
  string offset4 = StrCat(offset2, offset2);
  if (cluster) {
    VLOG(1) << offset << "type             = " << cluster->type();
    VLOG(1) << offset << "num warmup steps = " << cluster->NumWarmupSteps();
    const auto dev_names = cluster->GetDeviceNames();
    if (dev_names.size()) {
      VLOG(1) << offset << " Device names:";
      for (const auto s : dev_names) {
        VLOG(1) << offset2 << s;
      }
    }
    std::unordered_map<string, uint64> peak_mem;
    auto status = cluster->GetPeakMemoryUsage(&peak_mem);
    if (status == tensorflow::Status::OK()) {
      VLOG(1) << offset << "Peak Memory Usage :";
      for (auto s : peak_mem) {
        VLOG(1) << offset2 << s.first << " = " << s.second;
      }
    }

    const auto dev_props = cluster->GetDevices();
    if (dev_props.size()) {
      VLOG(1) << offset << "Device properties:";
      for (auto k : dev_props) {
        VLOG(1) << offset2 << k.first;
        const auto& dt = k.second;
        VLOG(1) << offset3 << "type          = " << dt.type();
        VLOG(1) << offset3 << "vendor        = " << dt.vendor();
        VLOG(1) << offset3 << "model         = " << dt.model();
        VLOG(1) << offset3 << "frequency     = " << dt.frequency();
        VLOG(1) << offset3 << "num cores     = " << dt.num_cores();
        VLOG(1) << offset3 << "num registers = " << dt.num_registers();
        VLOG(1) << offset3 << "L1 cache size = " << dt.l1_cache_size();
        VLOG(1) << offset3 << "L2 cache size = " << dt.l2_cache_size();
        VLOG(1) << offset3 << "L3 cache size = " << dt.l3_cache_size();
        VLOG(1) << offset3 << "SHMem per SMP = "
                << dt.shared_memory_size_per_multiprocessor();
        VLOG(1) << offset3 << "memory size   = " << dt.memory_size();
        VLOG(1) << offset3 << "bandwidth     = " << dt.bandwidth();
        if (dt.environment_size()) {
          VLOG(1) << offset3 << "environment   :";
          for (const auto e : dt.environment()) {
            VLOG(1) << offset4 << e.first << " = " << e.second;
          }
        }
      }
    }
  }
  VLOG(1) << "item: " << item.id;
  if (item.feed.size()) {
    VLOG(1) << offset << "Feeds  :";
    for (const auto& f : item.feed) {
      const auto& shape = f.second.shape();
      VLOG(1) << offset2 << f.first << " = shaped " << shape.DebugString();
    }
  } else {
    VLOG(1) << offset << "No Feeds";
  }
  if (item.fetch.size()) {
    VLOG(1) << offset << "Fetches  :";
    for (const auto& f : item.fetch) {
      VLOG(1) << offset2 << f;
    }
  } else {
    VLOG(1) << offset << "No Fetches";
  }

  if (item.init_ops.size()) {
    VLOG(1) << offset << "init ops  :";
    for (const auto& f : item.init_ops) {
      VLOG(1) << offset2 << f;
    }
  } else {
    VLOG(1) << offset << "No init ops";
  }
  VLOG(1) << "Save Op = " << item.save_op;
  VLOG(1) << "Restore Op = " << item.restore_op;
  VLOG(1) << "save_restore_loc_tensor = " << item.save_restore_loc_tensor;
  if (item.keep_ops.size()) {
    VLOG(1) << offset << "keep ops  :";
    for (const auto& f : item.keep_ops) {
      VLOG(1) << offset2 << f;
    }
  } else {
    VLOG(1) << offset << "No keep ops";
  }
  VLOG(3) << item.graph.DebugString();
  for (const auto dev : cluster->GetDeviceSet()->devices()) {
    const auto& pname = dev->parsed_name();
    VLOG(1) << "Device name= " << dev->name()
            << " parsedname job= " << pname.job << " id= " << pname.id
            << " has_id: " << pname.has_id << " has_job: " << pname.has_job
            << "has_type: " << pname.has_type << " type =" << pname.type;
  }
}

tensorflow::Status TRTOptimizationPass::Optimize(
    tensorflow::grappler::Cluster* cluster,
    const tensorflow::grappler::GrapplerItem& item, GraphDef* optimized_graph) {
  VLOG(1) << "Called TRTOptimization Pass " << name_;
  if (VLOG_IS_ON(1)) {
    PrintDebugInfo(cluster, item);
  }
  // This is a hack to workaround optimizer issue. MetaOptimizer calls
  // optimization passes on function objects as well, we should not modify
  // generated funcdefs! This is fragile but we don't have any other option
  // until framework fixes it.
  if (item.id != "tf_graph") {
    LOG(WARNING) << name_
                 << " is probably called on funcdef! This optimizer must *NOT* "
                    "be called on function objects.";
    *optimized_graph = item.graph;
    return tensorflow::Status::OK();
  }
  int max_dim = -1;
  if (item.feed.size()) {
    for (const auto& f : item.feed) {
      const auto& shape = f.second.shape();
      if (shape.dims() > 0) {
        if (shape.dim_size(0) > max_dim) max_dim = shape.dim_size(0);
      }
    }
  }
  if (maximum_batch_size_ < 0) {  // automatic batch size from input
    if (max_dim > 0) {
      maximum_batch_size_ = max_dim;
      VLOG(1) << "Setting maximum batch size to " << max_dim;
    } else {
      maximum_batch_size_ = 128;
      LOG(WARNING) << "Maximum batch size is not set"
                      " and can't be deduced from inputs setting it to"
                   << maximum_batch_size_
                   << ". Suggest configuring it from configuration parameters";
    }
  } else {
    if (max_dim > maximum_batch_size_) {
      LOG(WARNING) << "Configured batch size " << maximum_batch_size_
                   << " is less than input batch size " << max_dim
                   << " adjusting maximum batch size to match input batch size";
    }
  }
  tensorflow::grappler::GraphProperties static_graph_properties(item);
  TF_RETURN_IF_ERROR(static_graph_properties.InferStatically(true));
  tensorflow::tensorrt::convert::ConversionParams cp;

  std::vector<string> nodes_to_preserve;
  for (const auto& n : item.NodesToPreserve()) {
    auto tokens = str_util::Split(n, ":");
    string s = tokens.at(0);
    for (int i = 1; i < tokens.size() - 1; ++i){
      StrAppend(&s, ":", tokens.at(i));
    }
    int dumm_port = -1;
    // If the last token is not an integer, it must be part of the name.
    // Otherwise it is port number.
    if (tokens.size() > 1 &&
        !strings::safe_strto32(tokens.back(), &dumm_port)) {
      StrAppend(&s, ":", tokens.back());
    }
    nodes_to_preserve.push_back(s);
  }
  cp.input_graph_def = &item.graph;
  cp.output_names = &nodes_to_preserve;
  cp.max_batch_size = maximum_batch_size_;
  cp.max_workspace_size_bytes = maximum_workspace_size_;
  cp.output_graph_def = optimized_graph;
  cp.precision_mode = precision_mode_;
  cp.minimum_segment_size = minimum_segment_size_;
  cp.graph_properties = &static_graph_properties;
  cp.cluster = cluster;
  cp.is_dyn_op = is_dynamic_op_;
  cp.cached_engine_batches = batches_;
  cp.max_cached_engines = max_cached_batches_;
  auto status = tensorflow::tensorrt::convert::ConvertAfterShapes(cp);
  VLOG(2) << optimized_graph->DebugString();
  VLOG(1) << "Returning from " << name_;
  return status;
}

void TRTOptimizationPass::Feedback(
    tensorflow::grappler::Cluster* cluster,
    const tensorflow::grappler::GrapplerItem& item,
    const GraphDef& optimized_graph, double result) {}

}  // namespace convert
}  // namespace tensorrt
}  // namespace tensorflow

class VerboseCustomGraphOptimizerRegistrar
    : public tensorflow::grappler::CustomGraphOptimizerRegistrar {
 public:
  VerboseCustomGraphOptimizerRegistrar(
      const tensorflow::grappler::CustomGraphOptimizerRegistry::Creator& cr,
      const tensorflow::string& name)
      : tensorflow::grappler::CustomGraphOptimizerRegistrar(cr, name) {
    VLOG(1) << "Constructing a CustomOptimizationPass registration object for "
            << name;
  }
};

static VerboseCustomGraphOptimizerRegistrar TRTOptimizationPass_Registrar(
    []() {
      VLOG(1)
          << "Instantiating CustomOptimizationPass object TensorRTOptimizer";
      return new tensorflow::tensorrt::convert::TRTOptimizationPass(
          "TensorRTOptimizer");
    },
    ("TensorRTOptimizer"));

#endif
#endif
<|endoftext|>


<commit_before>/*
    Copyright (C) 2014 Alexandr Akulich <akulichalexander@gmail.com>

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

#include "CTelegramDispatcher.hpp"

#include "CTelegramConnection.hpp"
#include "CTelegramStream.hpp"

#include <QDebug>

const quint32 secretFormatVersion = 0;

CTelegramDispatcher::CTelegramDispatcher(QObject *parent) :
    QObject(parent),
    m_appInformation(0),
    m_activeDc(0),
    m_wantedActiveDc(0)
{
}

void CTelegramDispatcher::setAppInformation(const CAppInformation *newAppInfo)
{
    m_appInformation = newAppInfo;
}

QByteArray CTelegramDispatcher::connectionSecretInfo() const
{
    if (!activeConnection()) {
        return QByteArray();
    }

    QByteArray output;
    CTelegramStream outputStream(&output, /* write */ true);

    outputStream << secretFormatVersion;
    outputStream << activeConnection()->deltaTime();
    outputStream << activeConnection()->dcInfo();
    outputStream << activeConnection()->authKey();
    outputStream << activeConnection()->authId();
    outputStream << activeConnection()->serverSalt();

    return output;
}

void CTelegramDispatcher::initConnection(const QString &address, quint32 port)
{
    TLDcOption dcInfo;
    dcInfo.ipAddress = address;
    dcInfo.port = port;

    CTelegramConnection *connection = createConnection(dcInfo);

    m_connections.insert(0, connection);
    connection->connectToDc();

    setActiveDc(0);
}

bool CTelegramDispatcher::restoreConnection(const QByteArray &secret)
{
    CTelegramStream inputStream(secret);

    quint32 format;
    qint32 deltaTime;
    TLDcOption dcInfo;
    QByteArray authKey;
    quint64 authId;
    quint64 serverSalt;

    inputStream >> format;

    if (format != secretFormatVersion) {
        qDebug() << "Unknown format version";
        return false;
    }

    inputStream >> deltaTime;
    inputStream >> dcInfo;
    inputStream >> authKey;
    inputStream >> authId;
    inputStream >> serverSalt;

    CTelegramConnection *connection = createConnection(dcInfo);

    connection->setDeltaTime(deltaTime);
    connection->setAuthKey(authKey);
    connection->setServerSalt(serverSalt);

    if (connection->authId() != authId) {
        qDebug() << "Invalid auth data.";
        delete connection;
        return false;
    }

    m_connections.insert(dcInfo.id, connection);

    setActiveDc(dcInfo.id);

    connection->connectToDc();

    return true;
}

void CTelegramDispatcher::requestPhoneStatus(const QString &phoneNumber)
{
    activeConnection()->requestPhoneStatus(phoneNumber);
}

void CTelegramDispatcher::signIn(const QString &phoneNumber, const QString &authCode)
{
    activeConnection()->signIn(phoneNumber, authCode);
}

void CTelegramDispatcher::signUp(const QString &phoneNumber, const QString &authCode, const QString &firstName, const QString &lastName)
{
    activeConnection()->signUp(phoneNumber, authCode, firstName, lastName);
}

void CTelegramDispatcher::requestPhoneCode(const QString &phoneNumber)
{
    if (!activeConnection()) {
        qDebug() << "Can't request phone code: there is no active connection.";
        return;
    }

    if (m_dcConfiguration.isEmpty()) {
        qDebug() << "Can't request phone code: DC Configuration is unknown.";
        return;
    }

    activeConnection()->requestPhoneCode(phoneNumber);
}

void CTelegramDispatcher::requestContactList()
{
    activeConnection()->requestContacts();
}

void CTelegramDispatcher::requestContactAvatar(const QString &contact)
{
    qDebug() << Q_FUNC_INFO << contact;

    int contactIndex = m_contactList.indexOf(contact);

    if (contactIndex < 0) {
        qDebug() << Q_FUNC_INFO << "Unknown contact" << contact;
        return;
    }

    if (m_users.at(contactIndex).photo.tlType == UserProfilePhotoEmpty) {
        qDebug() << Q_FUNC_INFO << "Contact" << contact << "have no avatar";
        return;
    }

    TLFileLocation avatarLocation = m_users.at(contactIndex).photo.photoSmall;

    if (avatarLocation.tlType == FileLocationUnavailable) {
        qDebug() << Q_FUNC_INFO << "Contact" << contact << "avatar is not available";
        return;
    }

    TLInputFileLocation inputFile;
    inputFile.volumeId = avatarLocation.volumeId;
    inputFile.localId  = avatarLocation.localId;
    inputFile.secret   = avatarLocation.secret;

    requestFile(inputFile, avatarLocation.dcId, contactIndex);
    qDebug() << Q_FUNC_INFO << "Requested avatar for contact " << contactIndex << contact;
}

void CTelegramDispatcher::setUsers(const QVector<TLUser> &users)
{
    m_users = users;

    QStringList contactIds;
    contactIds.reserve(m_users.count());

    foreach (const TLUser &user, m_users) {
        contactIds.append(user.phone);
    }

    if (m_contactList != contactIds) {
        m_contactList = contactIds;
        emit contactListChanged();
    }
}

void CTelegramDispatcher::requestFile(const TLInputFileLocation &location, quint32 dc, quint32 fileId)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (!connection) {
        qDebug() << "No connection";
    }

    if (connection && (connection->authState() == CTelegramConnection::AuthStateSignedIn)) {
        connection->getFile(location, fileId);
    } else {
        qDebug() << Q_FUNC_INFO << "There is no connection to dest dc. Not implemented." << dc;
        // TODO
    }
}

void CTelegramDispatcher::whenConnectionAuthChanged(int dc, int newState)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (!connection) {
        return;
    }

    if (newState == CTelegramConnection::AuthStateSuccess) {
        if (m_dcConfiguration.isEmpty()) {
            connection->getConfiguration();
        }

        if (m_wantedActiveDc == dc) {
            setActiveDc(dc);
        }

        foreach (const QByteArray &data, m_delayedPackages.values(dc)) {
            connection->processRedirectedPackage(data);
        }

        m_delayedPackages.remove(dc);
    }

    if (newState == CTelegramConnection::AuthStateSignedIn) {
        connect(connection, SIGNAL(usersReceived(QVector<TLUser>)), SLOT(setUsers(QVector<TLUser>)));

        emit authenticated();
    }
}

void CTelegramDispatcher::whenConnectionConfigurationUpdated(int dc)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (!connection) {
        return;
    }

    m_dcConfiguration = connection->dcConfiguration();

    qDebug() << "Core: DC Configuration:";
    for (int i = 0; i < m_dcConfiguration.count(); ++i) {
        qDebug() << m_dcConfiguration.at(i).id << ": " << m_dcConfiguration.at(i).ipAddress << ":"<< m_dcConfiguration.at(i).port;
    }

    emit dcConfigurationObtained();
}

void CTelegramDispatcher::whenConnectionDcIdUpdated(int connectionId, int newDcId)
{
    CTelegramConnection *connection = m_connections.value(connectionId);

    if (!connection) {
        return;
    }

    if (m_connections.value(newDcId)) {
        qDebug() << "Connection with wanted id already exists";
    } else {
        m_connections.remove(connectionId);
        m_connections.insert(newDcId, connection);

        TLDcOption info = connection->dcInfo();
        info.id = newDcId;
        connection->setDcInfo(info);

        if (m_activeDc == connectionId) {

            if (m_wantedActiveDc == m_activeDc) {
                m_wantedActiveDc = newDcId;
            }

            setActiveDc(newDcId);
        }

        qDebug() << "Connection DC Id changed from" << connectionId << "to" << newDcId;
    }
}

void CTelegramDispatcher::whenPackageRedirected(const QByteArray &data, int dc)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (connection && connection->authState() >= CTelegramConnection::AuthStateSuccess) {
        connection->processRedirectedPackage(data);
    } else {
        m_delayedPackages.insertMulti(dc, data);
        establishConnectionToDc(dc);
    }
}

void CTelegramDispatcher::whenWantedActiveDcChanged(int dc)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (connection && connection->authState() >= CTelegramConnection::AuthStateSuccess) {
        setActiveDc(dc);
    } else {
        m_wantedActiveDc = dc;
        establishConnectionToDc(dc);
    }
}

void CTelegramDispatcher::whenFileReceived(const TLUploadFile &file, quint32 fileId)
{
    // Proto version.

    QString mimeType = mimeTypeByStorageFileType(file.type.tlType);

    if (m_contactList.count() < fileId) {
        qDebug() << Q_FUNC_INFO << "Unexpected fileId" << fileId << "contact-list:" << m_contactList;
        return;
    }

    QString contact = m_contactList.at(fileId);

    emit avatarReceived(contact, file.bytes, mimeType);
}

void CTelegramDispatcher::setActiveDc(int dc, bool syncWantedDc)
{
    m_activeDc = dc;

    if (syncWantedDc) {
        m_wantedActiveDc = dc;
    }

    qDebug() << "New active dc:" << dc;
}

CTelegramConnection *CTelegramDispatcher::createConnection(const TLDcOption &dc)
{
    CTelegramConnection *connection = new CTelegramConnection(m_appInformation, this);

    connect(connection, SIGNAL(authStateChanged(int,int)), SLOT(whenConnectionAuthChanged(int,int)));
    connect(connection, SIGNAL(dcConfigurationReceived(int)), SLOT(whenConnectionConfigurationUpdated(int)));
    connect(connection, SIGNAL(actualDcIdReceived(int,int)), SLOT(whenConnectionDcIdUpdated(int,int)));
    connect(connection, SIGNAL(newRedirectedPackage(QByteArray,int)), SLOT(whenPackageRedirected(QByteArray,int)));
    connect(connection, SIGNAL(wantedActiveDcChanged(int)), SLOT(whenWantedActiveDcChanged(int)));

    connect(connection, SIGNAL(phoneStatusReceived(QString,bool,bool)), SIGNAL(phoneStatusReceived(QString,bool,bool)));
    connect(connection, SIGNAL(phoneCodeRequired()), SIGNAL(phoneCodeRequired()));
    connect(connection, SIGNAL(phoneCodeIsInvalid()), SIGNAL(phoneCodeIsInvalid()));

    connect(connection, SIGNAL(fileReceived(TLUploadFile,quint32)), SLOT(whenFileReceived(TLUploadFile,quint32)));

    connection->setDcInfo(dc);

    return connection;
}

CTelegramConnection *CTelegramDispatcher::establishConnectionToDc(int dc)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (!connection) {
        const TLDcOption info = dcInfoById(dc);

        if (info.ipAddress.isEmpty()) {
            qDebug() << "Error: Attempt to connect to unknown DC" << dc;
            return 0;
        }

        connection = createConnection(info);
        m_connections.insert(dc, connection);
    }

    if (!connection->isConnected()) {
        connection->connectToDc();
    }

    return connection;
}

TLDcOption CTelegramDispatcher::dcInfoById(quint32 dc)
{
    foreach (const TLDcOption option, m_dcConfiguration) {
        if (option.id == dc) {
            return option;
        }
    }

    return TLDcOption();
}

QString CTelegramDispatcher::mimeTypeByStorageFileType(TLValue type)
{
    switch (type) {
    case StorageFileJpeg:
        return QLatin1String("image/jpeg");
    case StorageFileGif:
        return QLatin1String("image/gif");
    case StorageFilePng:
        return QLatin1String("image/png");
    case StorageFilePdf:
        return QLatin1String("application/pdf");
    case StorageFileMp3:
        return QLatin1String("audio/mpeg");
    case StorageFileMov:
        return QLatin1String("video/quicktime");
    case StorageFileMp4:
        return QLatin1String("audio/mp4");
    case StorageFileWebp:
        return QLatin1String("image/webp");
    default:
        return QString();
    }
}
<commit_msg>TelegramQt: whenConnectionAuthChanged() now aware of session restored cases.<commit_after>/*
    Copyright (C) 2014 Alexandr Akulich <akulichalexander@gmail.com>

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

#include "CTelegramDispatcher.hpp"

#include "CTelegramConnection.hpp"
#include "CTelegramStream.hpp"

#include <QDebug>

const quint32 secretFormatVersion = 0;

CTelegramDispatcher::CTelegramDispatcher(QObject *parent) :
    QObject(parent),
    m_appInformation(0),
    m_activeDc(0),
    m_wantedActiveDc(0)
{
}

void CTelegramDispatcher::setAppInformation(const CAppInformation *newAppInfo)
{
    m_appInformation = newAppInfo;
}

QByteArray CTelegramDispatcher::connectionSecretInfo() const
{
    if (!activeConnection()) {
        return QByteArray();
    }

    QByteArray output;
    CTelegramStream outputStream(&output, /* write */ true);

    outputStream << secretFormatVersion;
    outputStream << activeConnection()->deltaTime();
    outputStream << activeConnection()->dcInfo();
    outputStream << activeConnection()->authKey();
    outputStream << activeConnection()->authId();
    outputStream << activeConnection()->serverSalt();

    return output;
}

void CTelegramDispatcher::initConnection(const QString &address, quint32 port)
{
    TLDcOption dcInfo;
    dcInfo.ipAddress = address;
    dcInfo.port = port;

    CTelegramConnection *connection = createConnection(dcInfo);

    m_connections.insert(0, connection);
    connection->connectToDc();

    setActiveDc(0);
}

bool CTelegramDispatcher::restoreConnection(const QByteArray &secret)
{
    CTelegramStream inputStream(secret);

    quint32 format;
    qint32 deltaTime;
    TLDcOption dcInfo;
    QByteArray authKey;
    quint64 authId;
    quint64 serverSalt;

    inputStream >> format;

    if (format != secretFormatVersion) {
        qDebug() << "Unknown format version";
        return false;
    }

    inputStream >> deltaTime;
    inputStream >> dcInfo;
    inputStream >> authKey;
    inputStream >> authId;
    inputStream >> serverSalt;

    CTelegramConnection *connection = createConnection(dcInfo);

    connection->setDeltaTime(deltaTime);
    connection->setAuthKey(authKey);
    connection->setServerSalt(serverSalt);

    if (connection->authId() != authId) {
        qDebug() << "Invalid auth data.";
        delete connection;
        return false;
    }

    m_connections.insert(dcInfo.id, connection);

    setActiveDc(dcInfo.id);

    connection->connectToDc();

    return true;
}

void CTelegramDispatcher::requestPhoneStatus(const QString &phoneNumber)
{
    activeConnection()->requestPhoneStatus(phoneNumber);
}

void CTelegramDispatcher::signIn(const QString &phoneNumber, const QString &authCode)
{
    activeConnection()->signIn(phoneNumber, authCode);
}

void CTelegramDispatcher::signUp(const QString &phoneNumber, const QString &authCode, const QString &firstName, const QString &lastName)
{
    activeConnection()->signUp(phoneNumber, authCode, firstName, lastName);
}

void CTelegramDispatcher::requestPhoneCode(const QString &phoneNumber)
{
    if (!activeConnection()) {
        qDebug() << "Can't request phone code: there is no active connection.";
        return;
    }

    if (m_dcConfiguration.isEmpty()) {
        qDebug() << "Can't request phone code: DC Configuration is unknown.";
        return;
    }

    activeConnection()->requestPhoneCode(phoneNumber);
}

void CTelegramDispatcher::requestContactList()
{
    activeConnection()->requestContacts();
}

void CTelegramDispatcher::requestContactAvatar(const QString &contact)
{
    qDebug() << Q_FUNC_INFO << contact;

    int contactIndex = m_contactList.indexOf(contact);

    if (contactIndex < 0) {
        qDebug() << Q_FUNC_INFO << "Unknown contact" << contact;
        return;
    }

    if (m_users.at(contactIndex).photo.tlType == UserProfilePhotoEmpty) {
        qDebug() << Q_FUNC_INFO << "Contact" << contact << "have no avatar";
        return;
    }

    TLFileLocation avatarLocation = m_users.at(contactIndex).photo.photoSmall;

    if (avatarLocation.tlType == FileLocationUnavailable) {
        qDebug() << Q_FUNC_INFO << "Contact" << contact << "avatar is not available";
        return;
    }

    TLInputFileLocation inputFile;
    inputFile.volumeId = avatarLocation.volumeId;
    inputFile.localId  = avatarLocation.localId;
    inputFile.secret   = avatarLocation.secret;

    requestFile(inputFile, avatarLocation.dcId, contactIndex);
    qDebug() << Q_FUNC_INFO << "Requested avatar for contact " << contactIndex << contact;
}

void CTelegramDispatcher::setUsers(const QVector<TLUser> &users)
{
    m_users = users;

    QStringList contactIds;
    contactIds.reserve(m_users.count());

    foreach (const TLUser &user, m_users) {
        contactIds.append(user.phone);
    }

    if (m_contactList != contactIds) {
        m_contactList = contactIds;
        emit contactListChanged();
    }
}

void CTelegramDispatcher::requestFile(const TLInputFileLocation &location, quint32 dc, quint32 fileId)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (!connection) {
        qDebug() << "No connection";
    }

    if (connection && (connection->authState() == CTelegramConnection::AuthStateSignedIn)) {
        connection->getFile(location, fileId);
    } else {
        qDebug() << Q_FUNC_INFO << "There is no connection to dest dc. Not implemented." << dc;
        // TODO
    }
}

void CTelegramDispatcher::whenConnectionAuthChanged(int dc, int newState)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (!connection) {
        return;
    }

    if (newState >= CTelegramConnection::AuthStateSuccess) {
        if (m_dcConfiguration.isEmpty()) {
            connection->getConfiguration();
        }

        if (m_wantedActiveDc == dc) {
            setActiveDc(dc);
        }

        foreach (const QByteArray &data, m_delayedPackages.values(dc)) {
            connection->processRedirectedPackage(data);
        }

        m_delayedPackages.remove(dc);
    }

    if (newState == CTelegramConnection::AuthStateSignedIn) {
        connect(connection, SIGNAL(usersReceived(QVector<TLUser>)), SLOT(setUsers(QVector<TLUser>)));

        emit authenticated();
    }
}

void CTelegramDispatcher::whenConnectionConfigurationUpdated(int dc)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (!connection) {
        return;
    }

    m_dcConfiguration = connection->dcConfiguration();

    qDebug() << "Core: DC Configuration:";
    for (int i = 0; i < m_dcConfiguration.count(); ++i) {
        qDebug() << m_dcConfiguration.at(i).id << ": " << m_dcConfiguration.at(i).ipAddress << ":"<< m_dcConfiguration.at(i).port;
    }

    emit dcConfigurationObtained();
}

void CTelegramDispatcher::whenConnectionDcIdUpdated(int connectionId, int newDcId)
{
    CTelegramConnection *connection = m_connections.value(connectionId);

    if (!connection) {
        return;
    }

    if (m_connections.value(newDcId)) {
        qDebug() << "Connection with wanted id already exists";
    } else {
        m_connections.remove(connectionId);
        m_connections.insert(newDcId, connection);

        TLDcOption info = connection->dcInfo();
        info.id = newDcId;
        connection->setDcInfo(info);

        if (m_activeDc == connectionId) {

            if (m_wantedActiveDc == m_activeDc) {
                m_wantedActiveDc = newDcId;
            }

            setActiveDc(newDcId);
        }

        qDebug() << "Connection DC Id changed from" << connectionId << "to" << newDcId;
    }
}

void CTelegramDispatcher::whenPackageRedirected(const QByteArray &data, int dc)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (connection && connection->authState() >= CTelegramConnection::AuthStateSuccess) {
        connection->processRedirectedPackage(data);
    } else {
        m_delayedPackages.insertMulti(dc, data);
        establishConnectionToDc(dc);
    }
}

void CTelegramDispatcher::whenWantedActiveDcChanged(int dc)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (connection && connection->authState() >= CTelegramConnection::AuthStateSuccess) {
        setActiveDc(dc);
    } else {
        m_wantedActiveDc = dc;
        establishConnectionToDc(dc);
    }
}

void CTelegramDispatcher::whenFileReceived(const TLUploadFile &file, quint32 fileId)
{
    // Proto version.

    QString mimeType = mimeTypeByStorageFileType(file.type.tlType);

    if (m_contactList.count() < fileId) {
        qDebug() << Q_FUNC_INFO << "Unexpected fileId" << fileId << "contact-list:" << m_contactList;
        return;
    }

    QString contact = m_contactList.at(fileId);

    emit avatarReceived(contact, file.bytes, mimeType);
}

void CTelegramDispatcher::setActiveDc(int dc, bool syncWantedDc)
{
    m_activeDc = dc;

    if (syncWantedDc) {
        m_wantedActiveDc = dc;
    }

    qDebug() << "New active dc:" << dc;
}

CTelegramConnection *CTelegramDispatcher::createConnection(const TLDcOption &dc)
{
    CTelegramConnection *connection = new CTelegramConnection(m_appInformation, this);

    connect(connection, SIGNAL(authStateChanged(int,int)), SLOT(whenConnectionAuthChanged(int,int)));
    connect(connection, SIGNAL(dcConfigurationReceived(int)), SLOT(whenConnectionConfigurationUpdated(int)));
    connect(connection, SIGNAL(actualDcIdReceived(int,int)), SLOT(whenConnectionDcIdUpdated(int,int)));
    connect(connection, SIGNAL(newRedirectedPackage(QByteArray,int)), SLOT(whenPackageRedirected(QByteArray,int)));
    connect(connection, SIGNAL(wantedActiveDcChanged(int)), SLOT(whenWantedActiveDcChanged(int)));

    connect(connection, SIGNAL(phoneStatusReceived(QString,bool,bool)), SIGNAL(phoneStatusReceived(QString,bool,bool)));
    connect(connection, SIGNAL(phoneCodeRequired()), SIGNAL(phoneCodeRequired()));
    connect(connection, SIGNAL(phoneCodeIsInvalid()), SIGNAL(phoneCodeIsInvalid()));

    connect(connection, SIGNAL(fileReceived(TLUploadFile,quint32)), SLOT(whenFileReceived(TLUploadFile,quint32)));

    connection->setDcInfo(dc);

    return connection;
}

CTelegramConnection *CTelegramDispatcher::establishConnectionToDc(int dc)
{
    CTelegramConnection *connection = m_connections.value(dc);

    if (!connection) {
        const TLDcOption info = dcInfoById(dc);

        if (info.ipAddress.isEmpty()) {
            qDebug() << "Error: Attempt to connect to unknown DC" << dc;
            return 0;
        }

        connection = createConnection(info);
        m_connections.insert(dc, connection);
    }

    if (!connection->isConnected()) {
        connection->connectToDc();
    }

    return connection;
}

TLDcOption CTelegramDispatcher::dcInfoById(quint32 dc)
{
    foreach (const TLDcOption option, m_dcConfiguration) {
        if (option.id == dc) {
            return option;
        }
    }

    return TLDcOption();
}

QString CTelegramDispatcher::mimeTypeByStorageFileType(TLValue type)
{
    switch (type) {
    case StorageFileJpeg:
        return QLatin1String("image/jpeg");
    case StorageFileGif:
        return QLatin1String("image/gif");
    case StorageFilePng:
        return QLatin1String("image/png");
    case StorageFilePdf:
        return QLatin1String("application/pdf");
    case StorageFileMp3:
        return QLatin1String("audio/mpeg");
    case StorageFileMov:
        return QLatin1String("video/quicktime");
    case StorageFileMp4:
        return QLatin1String("audio/mp4");
    case StorageFileWebp:
        return QLatin1String("image/webp");
    default:
        return QString();
    }
}
<|endoftext|>


<commit_before>#include "master.hpp"

namespace factor
{

factor_vm *vm;

void init_globals()
{
	init_platform_globals();
}

void factor_vm::default_parameters(vm_parameters *p)
{
	p->image_path = NULL;

	/* We make a wild guess here that if we're running on ARM, we don't
	have a lot of memory. */
#ifdef FACTOR_ARM
	p->ds_size = 8 * sizeof(cell);
	p->rs_size = 8 * sizeof(cell);

	p->gen_count = 2;
	p->code_size = 4;
	p->young_size = 1;
	p->aging_size = 1;
	p->tenured_size = 6;
#else
	p->ds_size = 32 * sizeof(cell);
	p->rs_size = 32 * sizeof(cell);

	p->gen_count = 3;
	p->code_size = 8 * sizeof(cell);
	p->young_size = sizeof(cell) / 4;
	p->aging_size = sizeof(cell) / 2;
	p->tenured_size = 4 * sizeof(cell);
#endif

	p->max_pic_size = 3;

	p->secure_gc = false;
	p->fep = false;

#ifdef WINDOWS
	p->console = false;
#else
	if (this == vm)
		p->console = true;
	else
		p->console = false;

#endif

	p->stack_traces = true;
}

bool factor_vm::factor_arg(const vm_char* str, const vm_char* arg, cell* value)
{
	int val;
	if(SSCANF(str,arg,&val) > 0)
	{
		*value = val;
		return true;
	}
	else
		return false;
}

void factor_vm::init_parameters_from_args(vm_parameters *p, int argc, vm_char **argv)
{
	default_parameters(p);
	p->executable_path = argv[0];

	int i = 0;

	for(i = 1; i < argc; i++)
	{
		if(factor_arg(argv[i],STRING_LITERAL("-datastack=%d"),&p->ds_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-retainstack=%d"),&p->rs_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-generations=%d"),&p->gen_count));
		else if(factor_arg(argv[i],STRING_LITERAL("-young=%d"),&p->young_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-aging=%d"),&p->aging_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-tenured=%d"),&p->tenured_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-codeheap=%d"),&p->code_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-pic=%d"),&p->max_pic_size));
		else if(STRCMP(argv[i],STRING_LITERAL("-securegc")) == 0) p->secure_gc = true;
		else if(STRCMP(argv[i],STRING_LITERAL("-fep")) == 0) p->fep = true;
		else if(STRNCMP(argv[i],STRING_LITERAL("-i="),3) == 0) p->image_path = argv[i] + 3;
		else if(STRCMP(argv[i],STRING_LITERAL("-console")) == 0) p->console = true;
		else if(STRCMP(argv[i],STRING_LITERAL("-no-stack-traces")) == 0) p->stack_traces = false;
	}
}

/* Do some initialization that we do once only */
void factor_vm::do_stage1_init()
{
	print_string("*** Stage 2 early init... ");
	fflush(stdout);

	compile_all_words();
	userenv[STAGE2_ENV] = T;

	print_string("done\n");
	fflush(stdout);
}

void factor_vm::init_factor(vm_parameters *p)
{
	/* Kilobytes */
	p->ds_size = align_page(p->ds_size << 10);
	p->rs_size = align_page(p->rs_size << 10);

	/* Megabytes */
	p->young_size <<= 20;
	p->aging_size <<= 20;
	p->tenured_size <<= 20;
	p->code_size <<= 20;

	/* Disable GC during init as a sanity check */
	gc_off = true;

	/* OS-specific initialization */
	early_init();

	const vm_char *executable_path = vm_executable_path();

	if(executable_path)
		p->executable_path = executable_path;

	if(p->image_path == NULL)
		p->image_path = default_image_path();

	srand(current_micros());
	init_ffi();
	init_stacks(p->ds_size,p->rs_size);
	load_image(p);
	init_c_io();
	init_inline_caching(p->max_pic_size);
	init_signals();

	if(p->console)
		open_console();

	init_profiler();

	userenv[CPU_ENV] = allot_alien(F,(cell)FACTOR_CPU_STRING);
	userenv[OS_ENV] = allot_alien(F,(cell)FACTOR_OS_STRING);
	userenv[CELL_SIZE_ENV] = tag_fixnum(sizeof(cell));
	userenv[EXECUTABLE_ENV] = allot_alien(F,(cell)p->executable_path);
	userenv[ARGS_ENV] = F;
	userenv[EMBEDDED_ENV] = F;

	/* We can GC now */
	gc_off = false;

	if(userenv[STAGE2_ENV] == F)
	{
		userenv[STACK_TRACES_ENV] = tag_boolean(p->stack_traces);
		do_stage1_init();
	}
}

/* May allocate memory */
void factor_vm::pass_args_to_factor(int argc, vm_char **argv)
{
	growable_array args(this);
	int i;

	for(i = 1; i < argc; i++){
		args.add(allot_alien(F,(cell)argv[i]));
	}

	args.trim();
	userenv[ARGS_ENV] = args.elements.value();
}

void factor_vm::start_factor(vm_parameters *p)
{
	if(p->fep) factorbug();

	nest_stacks();
	c_to_factor_toplevel(userenv[BOOT_ENV]);
	unnest_stacks();
}

char *factor_vm::factor_eval_string(char *string)
{
	char *(*callback)(char *) = (char *(*)(char *))alien_offset(userenv[EVAL_CALLBACK_ENV]);
	return callback(string);
}

void factor_vm::factor_eval_free(char *result)
{
	free(result);
}

void factor_vm::factor_yield()
{
	void (*callback)() = (void (*)())alien_offset(userenv[YIELD_CALLBACK_ENV]);
	callback();
}

void factor_vm::factor_sleep(long us)
{
	void (*callback)(long) = (void (*)(long))alien_offset(userenv[SLEEP_CALLBACK_ENV]);
	callback(us);
}

void factor_vm::start_standalone_factor(int argc, vm_char **argv)
{
	vm_parameters p;
	default_parameters(&p);
	init_parameters_from_args(&p,argc,argv);
	init_factor(&p);
	pass_args_to_factor(argc,argv);
	start_factor(&p);
}

struct startargs {
	int argc;
	vm_char **argv;
};

void* start_standalone_factor_thread(void *arg)
{
	factor_vm *newvm = new factor_vm;
	register_vm_with_thread(newvm);
	startargs *args = (startargs*) arg;
	newvm->start_standalone_factor(args->argc, args->argv);
	return 0;
}

VM_C_API void start_standalone_factor(int argc, vm_char **argv)
{
	factor_vm *newvm = new factor_vm;
	vm = newvm;
	register_vm_with_thread(newvm);
	return newvm->start_standalone_factor(argc,argv);
}

VM_C_API THREADHANDLE start_standalone_factor_in_new_thread(int argc, vm_char **argv)
{
	startargs *args = new startargs;   // leaks startargs structure
	args->argc = argc; args->argv = argv;
	return start_thread(start_standalone_factor_thread,args);
}

}
<commit_msg>don't leak startargs in start_standalone_factor_in_new_thread<commit_after>#include "master.hpp"

namespace factor
{

factor_vm *vm;

void init_globals()
{
	init_platform_globals();
}

void factor_vm::default_parameters(vm_parameters *p)
{
	p->image_path = NULL;

	/* We make a wild guess here that if we're running on ARM, we don't
	have a lot of memory. */
#ifdef FACTOR_ARM
	p->ds_size = 8 * sizeof(cell);
	p->rs_size = 8 * sizeof(cell);

	p->gen_count = 2;
	p->code_size = 4;
	p->young_size = 1;
	p->aging_size = 1;
	p->tenured_size = 6;
#else
	p->ds_size = 32 * sizeof(cell);
	p->rs_size = 32 * sizeof(cell);

	p->gen_count = 3;
	p->code_size = 8 * sizeof(cell);
	p->young_size = sizeof(cell) / 4;
	p->aging_size = sizeof(cell) / 2;
	p->tenured_size = 4 * sizeof(cell);
#endif

	p->max_pic_size = 3;

	p->secure_gc = false;
	p->fep = false;

#ifdef WINDOWS
	p->console = false;
#else
	if (this == vm)
		p->console = true;
	else
		p->console = false;

#endif

	p->stack_traces = true;
}

bool factor_vm::factor_arg(const vm_char* str, const vm_char* arg, cell* value)
{
	int val;
	if(SSCANF(str,arg,&val) > 0)
	{
		*value = val;
		return true;
	}
	else
		return false;
}

void factor_vm::init_parameters_from_args(vm_parameters *p, int argc, vm_char **argv)
{
	default_parameters(p);
	p->executable_path = argv[0];

	int i = 0;

	for(i = 1; i < argc; i++)
	{
		if(factor_arg(argv[i],STRING_LITERAL("-datastack=%d"),&p->ds_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-retainstack=%d"),&p->rs_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-generations=%d"),&p->gen_count));
		else if(factor_arg(argv[i],STRING_LITERAL("-young=%d"),&p->young_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-aging=%d"),&p->aging_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-tenured=%d"),&p->tenured_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-codeheap=%d"),&p->code_size));
		else if(factor_arg(argv[i],STRING_LITERAL("-pic=%d"),&p->max_pic_size));
		else if(STRCMP(argv[i],STRING_LITERAL("-securegc")) == 0) p->secure_gc = true;
		else if(STRCMP(argv[i],STRING_LITERAL("-fep")) == 0) p->fep = true;
		else if(STRNCMP(argv[i],STRING_LITERAL("-i="),3) == 0) p->image_path = argv[i] + 3;
		else if(STRCMP(argv[i],STRING_LITERAL("-console")) == 0) p->console = true;
		else if(STRCMP(argv[i],STRING_LITERAL("-no-stack-traces")) == 0) p->stack_traces = false;
	}
}

/* Do some initialization that we do once only */
void factor_vm::do_stage1_init()
{
	print_string("*** Stage 2 early init... ");
	fflush(stdout);

	compile_all_words();
	userenv[STAGE2_ENV] = T;

	print_string("done\n");
	fflush(stdout);
}

void factor_vm::init_factor(vm_parameters *p)
{
	/* Kilobytes */
	p->ds_size = align_page(p->ds_size << 10);
	p->rs_size = align_page(p->rs_size << 10);

	/* Megabytes */
	p->young_size <<= 20;
	p->aging_size <<= 20;
	p->tenured_size <<= 20;
	p->code_size <<= 20;

	/* Disable GC during init as a sanity check */
	gc_off = true;

	/* OS-specific initialization */
	early_init();

	const vm_char *executable_path = vm_executable_path();

	if(executable_path)
		p->executable_path = executable_path;

	if(p->image_path == NULL)
		p->image_path = default_image_path();

	srand(current_micros());
	init_ffi();
	init_stacks(p->ds_size,p->rs_size);
	load_image(p);
	init_c_io();
	init_inline_caching(p->max_pic_size);
	init_signals();

	if(p->console)
		open_console();

	init_profiler();

	userenv[CPU_ENV] = allot_alien(F,(cell)FACTOR_CPU_STRING);
	userenv[OS_ENV] = allot_alien(F,(cell)FACTOR_OS_STRING);
	userenv[CELL_SIZE_ENV] = tag_fixnum(sizeof(cell));
	userenv[EXECUTABLE_ENV] = allot_alien(F,(cell)p->executable_path);
	userenv[ARGS_ENV] = F;
	userenv[EMBEDDED_ENV] = F;

	/* We can GC now */
	gc_off = false;

	if(userenv[STAGE2_ENV] == F)
	{
		userenv[STACK_TRACES_ENV] = tag_boolean(p->stack_traces);
		do_stage1_init();
	}
}

/* May allocate memory */
void factor_vm::pass_args_to_factor(int argc, vm_char **argv)
{
	growable_array args(this);
	int i;

	for(i = 1; i < argc; i++){
		args.add(allot_alien(F,(cell)argv[i]));
	}

	args.trim();
	userenv[ARGS_ENV] = args.elements.value();
}

void factor_vm::start_factor(vm_parameters *p)
{
	if(p->fep) factorbug();

	nest_stacks();
	c_to_factor_toplevel(userenv[BOOT_ENV]);
	unnest_stacks();
}

char *factor_vm::factor_eval_string(char *string)
{
	char *(*callback)(char *) = (char *(*)(char *))alien_offset(userenv[EVAL_CALLBACK_ENV]);
	return callback(string);
}

void factor_vm::factor_eval_free(char *result)
{
	free(result);
}

void factor_vm::factor_yield()
{
	void (*callback)() = (void (*)())alien_offset(userenv[YIELD_CALLBACK_ENV]);
	callback();
}

void factor_vm::factor_sleep(long us)
{
	void (*callback)(long) = (void (*)(long))alien_offset(userenv[SLEEP_CALLBACK_ENV]);
	callback(us);
}

void factor_vm::start_standalone_factor(int argc, vm_char **argv)
{
	vm_parameters p;
	default_parameters(&p);
	init_parameters_from_args(&p,argc,argv);
	init_factor(&p);
	pass_args_to_factor(argc,argv);
	start_factor(&p);
}

struct startargs {
	int argc;
	vm_char **argv;
};

// arg must be new'ed because we're going to delete it!
void* start_standalone_factor_thread(void *arg)
{
	factor_vm *newvm = new factor_vm;
	register_vm_with_thread(newvm);
	startargs *args = (startargs*) arg;
        int argc = args->argc; vm_char **argv = args->argv;
        delete args;
	newvm->start_standalone_factor(argc, argv);
	return 0;
}

VM_C_API void start_standalone_factor(int argc, vm_char **argv)
{
	factor_vm *newvm = new factor_vm;
	vm = newvm;
	register_vm_with_thread(newvm);
	return newvm->start_standalone_factor(argc,argv);
}

VM_C_API THREADHANDLE start_standalone_factor_in_new_thread(int argc, vm_char **argv)
{
	startargs *args = new startargs;
	args->argc = argc; args->argv = argv;
	return start_thread(start_standalone_factor_thread,args);
}

}
<|endoftext|>


<commit_before>#include "master.hpp"

namespace factor
{

void init_globals()
{
	init_mvm();
}

void factor_vm::default_parameters(vm_parameters *p)
{
	p->image_path = NULL;

	p->datastack_size = 32 * sizeof(cell);
	p->retainstack_size = 32 * sizeof(cell);

#if defined(__OpenBSD__) && defined(FACTOR_X86)
	p->callstack_size = 64 * sizeof(cell);
#elif defined(FACTOR_PPC)
	p->callstack_size = 256 * sizeof(cell);
#else
	p->callstack_size = 128 * sizeof(cell);
#endif

	p->code_size = 64;
	p->young_size = sizeof(cell) / 4;
	p->aging_size = sizeof(cell) / 2;
	p->tenured_size = 24 * sizeof(cell);

	p->max_pic_size = 3;

	p->fep = false;
	p->signals = true;

#ifdef WINDOWS
	p->console = false;
#else
	p->console = true;
#endif

	p->callback_size = 256;
}

bool factor_vm::factor_arg(const vm_char* str, const vm_char* arg, cell* value)
{
	int val;
	if(SSCANF(str,arg,&val) > 0)
	{
		*value = val;
		return true;
	}
	else
		return false;
}

void factor_vm::init_parameters_from_args(vm_parameters *p, int argc, vm_char **argv)
{
	default_parameters(p);
	p->executable_path = argv[0];

	int i = 0;

	for(i = 1; i < argc; i++)
	{
		vm_char *arg = argv[i];
		if(STRCMP(arg,STRING_LITERAL("--")) == 0) break;
		else if(factor_arg(arg,STRING_LITERAL("-datastack=%d"),&p->datastack_size));
		else if(factor_arg(arg,STRING_LITERAL("-retainstack=%d"),&p->retainstack_size));
		else if(factor_arg(arg,STRING_LITERAL("-callstack=%d"),&p->callstack_size));
		else if(factor_arg(arg,STRING_LITERAL("-young=%d"),&p->young_size));
		else if(factor_arg(arg,STRING_LITERAL("-aging=%d"),&p->aging_size));
		else if(factor_arg(arg,STRING_LITERAL("-tenured=%d"),&p->tenured_size));
		else if(factor_arg(arg,STRING_LITERAL("-codeheap=%d"),&p->code_size));
		else if(factor_arg(arg,STRING_LITERAL("-pic=%d"),&p->max_pic_size));
		else if(factor_arg(arg,STRING_LITERAL("-callbacks=%d"),&p->callback_size));
		else if(STRCMP(arg,STRING_LITERAL("-fep")) == 0) p->fep = true;
		else if(STRCMP(arg,STRING_LITERAL("-nosignals")) == 0) p->signals = false;
		else if(STRNCMP(arg,STRING_LITERAL("-i="),3) == 0) p->image_path = arg + 3;
		else if(STRCMP(arg,STRING_LITERAL("-console")) == 0) p->console = true;
	}
}

/* Compile code in boot image so that we can execute the startup quotation */
void factor_vm::prepare_boot_image()
{
	std::cout << "*** Stage 2 early init... ";
	fflush(stdout);

	compile_all_words();
	update_code_heap_words(true);
	initialize_all_quotations();
	special_objects[OBJ_STAGE2] = true_object;

	std::cout << "done\n";
}

void factor_vm::init_factor(vm_parameters *p)
{
	/* Kilobytes */
	p->datastack_size = align_page(p->datastack_size << 10);
	p->retainstack_size = align_page(p->retainstack_size << 10);
	p->callstack_size = align_page(p->callstack_size << 10);
	p->callback_size = align_page(p->callback_size << 10);

	/* Megabytes */
	p->young_size <<= 20;
	p->aging_size <<= 20;
	p->tenured_size <<= 20;
	p->code_size <<= 20;

	/* Disable GC during init as a sanity check */
	gc_off = true;

	/* OS-specific initialization */
	early_init();

	const vm_char *executable_path = vm_executable_path();

	if(executable_path)
		p->executable_path = executable_path;

	if(p->image_path == NULL)
		p->image_path = default_image_path();

	srand((unsigned int)nano_count());
	init_ffi();
	init_contexts(p->datastack_size,p->retainstack_size,p->callstack_size);
	init_callbacks(p->callback_size);
	load_image(p);
	init_c_io();
	init_inline_caching((int)p->max_pic_size);
	if(p->signals)
		init_signals();

	if(p->console)
		open_console();

	init_profiler();

	special_objects[OBJ_CPU] = allot_alien(false_object,(cell)FACTOR_CPU_STRING);
	special_objects[OBJ_OS] = allot_alien(false_object,(cell)FACTOR_OS_STRING);
	special_objects[OBJ_CELL_SIZE] = tag_fixnum(sizeof(cell));
	special_objects[OBJ_EXECUTABLE] = allot_alien(false_object,(cell)p->executable_path);
	special_objects[OBJ_ARGS] = false_object;
	special_objects[OBJ_EMBEDDED] = false_object;
	special_objects[OBJ_VM_COMPILER] = allot_alien(false_object,(cell)FACTOR_COMPILER_VERSION);

	/* We can GC now */
	gc_off = false;

	if(!to_boolean(special_objects[OBJ_STAGE2]))
		prepare_boot_image();
}

/* May allocate memory */
void factor_vm::pass_args_to_factor(int argc, vm_char **argv)
{
	growable_array args(this);

	for(fixnum i = 1; i < argc; i++)
		args.add(allot_alien(false_object,(cell)argv[i]));

	args.trim();
	special_objects[OBJ_ARGS] = args.elements.value();
}

void factor_vm::start_factor(vm_parameters *p)
{
	if(p->fep) factorbug();

	c_to_factor_toplevel(special_objects[OBJ_STARTUP_QUOT]);
}

void factor_vm::stop_factor()
{
	c_to_factor_toplevel(special_objects[OBJ_SHUTDOWN_QUOT]);
}

char *factor_vm::factor_eval_string(char *string)
{
	void *func = alien_offset(special_objects[OBJ_EVAL_CALLBACK]);
	CODE_TO_FUNCTION_POINTER(func);
	return ((char *(*)(char *))func)(string);
}

void factor_vm::factor_eval_free(char *result)
{
	free(result);
}

void factor_vm::factor_yield()
{
	void *func = alien_offset(special_objects[OBJ_YIELD_CALLBACK]);
	CODE_TO_FUNCTION_POINTER(func);
	((void (*)())func)();
}

void factor_vm::factor_sleep(long us)
{
	void *func = alien_offset(special_objects[OBJ_SLEEP_CALLBACK]);
	CODE_TO_FUNCTION_POINTER(func);
	((void (*)(long))func)(us);
}

void factor_vm::start_standalone_factor(int argc, vm_char **argv)
{
	vm_parameters p;
	default_parameters(&p);
	init_parameters_from_args(&p,argc,argv);
	init_factor(&p);
	pass_args_to_factor(argc,argv);
	start_factor(&p);
}

factor_vm *new_factor_vm()
{
	factor_vm *newvm = new factor_vm();
	register_vm_with_thread(newvm);
	thread_vms[thread_id()] = newvm;

	return newvm;
}

VM_C_API void start_standalone_factor(int argc, vm_char **argv)
{
	factor_vm *newvm = new_factor_vm();
	return newvm->start_standalone_factor(argc,argv);
}

}
<commit_msg>vm: don't mix iostream and stdio<commit_after>#include "master.hpp"

namespace factor
{

void init_globals()
{
	init_mvm();
}

void factor_vm::default_parameters(vm_parameters *p)
{
	p->image_path = NULL;

	p->datastack_size = 32 * sizeof(cell);
	p->retainstack_size = 32 * sizeof(cell);

#if defined(__OpenBSD__) && defined(FACTOR_X86)
	p->callstack_size = 64 * sizeof(cell);
#elif defined(FACTOR_PPC)
	p->callstack_size = 256 * sizeof(cell);
#else
	p->callstack_size = 128 * sizeof(cell);
#endif

	p->code_size = 64;
	p->young_size = sizeof(cell) / 4;
	p->aging_size = sizeof(cell) / 2;
	p->tenured_size = 24 * sizeof(cell);

	p->max_pic_size = 3;

	p->fep = false;
	p->signals = true;

#ifdef WINDOWS
	p->console = false;
#else
	p->console = true;
#endif

	p->callback_size = 256;
}

bool factor_vm::factor_arg(const vm_char* str, const vm_char* arg, cell* value)
{
	int val;
	if(SSCANF(str,arg,&val) > 0)
	{
		*value = val;
		return true;
	}
	else
		return false;
}

void factor_vm::init_parameters_from_args(vm_parameters *p, int argc, vm_char **argv)
{
	default_parameters(p);
	p->executable_path = argv[0];

	int i = 0;

	for(i = 1; i < argc; i++)
	{
		vm_char *arg = argv[i];
		if(STRCMP(arg,STRING_LITERAL("--")) == 0) break;
		else if(factor_arg(arg,STRING_LITERAL("-datastack=%d"),&p->datastack_size));
		else if(factor_arg(arg,STRING_LITERAL("-retainstack=%d"),&p->retainstack_size));
		else if(factor_arg(arg,STRING_LITERAL("-callstack=%d"),&p->callstack_size));
		else if(factor_arg(arg,STRING_LITERAL("-young=%d"),&p->young_size));
		else if(factor_arg(arg,STRING_LITERAL("-aging=%d"),&p->aging_size));
		else if(factor_arg(arg,STRING_LITERAL("-tenured=%d"),&p->tenured_size));
		else if(factor_arg(arg,STRING_LITERAL("-codeheap=%d"),&p->code_size));
		else if(factor_arg(arg,STRING_LITERAL("-pic=%d"),&p->max_pic_size));
		else if(factor_arg(arg,STRING_LITERAL("-callbacks=%d"),&p->callback_size));
		else if(STRCMP(arg,STRING_LITERAL("-fep")) == 0) p->fep = true;
		else if(STRCMP(arg,STRING_LITERAL("-nosignals")) == 0) p->signals = false;
		else if(STRNCMP(arg,STRING_LITERAL("-i="),3) == 0) p->image_path = arg + 3;
		else if(STRCMP(arg,STRING_LITERAL("-console")) == 0) p->console = true;
	}
}

/* Compile code in boot image so that we can execute the startup quotation */
void factor_vm::prepare_boot_image()
{
	std::cout << "*** Stage 2 early init... " << std::flush;

	compile_all_words();
	update_code_heap_words(true);
	initialize_all_quotations();
	special_objects[OBJ_STAGE2] = true_object;

	std::cout << "done" << std::endl;
}

void factor_vm::init_factor(vm_parameters *p)
{
	/* Kilobytes */
	p->datastack_size = align_page(p->datastack_size << 10);
	p->retainstack_size = align_page(p->retainstack_size << 10);
	p->callstack_size = align_page(p->callstack_size << 10);
	p->callback_size = align_page(p->callback_size << 10);

	/* Megabytes */
	p->young_size <<= 20;
	p->aging_size <<= 20;
	p->tenured_size <<= 20;
	p->code_size <<= 20;

	/* Disable GC during init as a sanity check */
	gc_off = true;

	/* OS-specific initialization */
	early_init();

	const vm_char *executable_path = vm_executable_path();

	if(executable_path)
		p->executable_path = executable_path;

	if(p->image_path == NULL)
		p->image_path = default_image_path();

	srand((unsigned int)nano_count());
	init_ffi();
	init_contexts(p->datastack_size,p->retainstack_size,p->callstack_size);
	init_callbacks(p->callback_size);
	load_image(p);
	init_c_io();
	init_inline_caching((int)p->max_pic_size);
	if(p->signals)
		init_signals();

	if(p->console)
		open_console();

	init_profiler();

	special_objects[OBJ_CPU] = allot_alien(false_object,(cell)FACTOR_CPU_STRING);
	special_objects[OBJ_OS] = allot_alien(false_object,(cell)FACTOR_OS_STRING);
	special_objects[OBJ_CELL_SIZE] = tag_fixnum(sizeof(cell));
	special_objects[OBJ_EXECUTABLE] = allot_alien(false_object,(cell)p->executable_path);
	special_objects[OBJ_ARGS] = false_object;
	special_objects[OBJ_EMBEDDED] = false_object;
	special_objects[OBJ_VM_COMPILER] = allot_alien(false_object,(cell)FACTOR_COMPILER_VERSION);

	/* We can GC now */
	gc_off = false;

	if(!to_boolean(special_objects[OBJ_STAGE2]))
		prepare_boot_image();
}

/* May allocate memory */
void factor_vm::pass_args_to_factor(int argc, vm_char **argv)
{
	growable_array args(this);

	for(fixnum i = 1; i < argc; i++)
		args.add(allot_alien(false_object,(cell)argv[i]));

	args.trim();
	special_objects[OBJ_ARGS] = args.elements.value();
}

void factor_vm::start_factor(vm_parameters *p)
{
	if(p->fep) factorbug();

	c_to_factor_toplevel(special_objects[OBJ_STARTUP_QUOT]);
}

void factor_vm::stop_factor()
{
	c_to_factor_toplevel(special_objects[OBJ_SHUTDOWN_QUOT]);
}

char *factor_vm::factor_eval_string(char *string)
{
	void *func = alien_offset(special_objects[OBJ_EVAL_CALLBACK]);
	CODE_TO_FUNCTION_POINTER(func);
	return ((char *(*)(char *))func)(string);
}

void factor_vm::factor_eval_free(char *result)
{
	free(result);
}

void factor_vm::factor_yield()
{
	void *func = alien_offset(special_objects[OBJ_YIELD_CALLBACK]);
	CODE_TO_FUNCTION_POINTER(func);
	((void (*)())func)();
}

void factor_vm::factor_sleep(long us)
{
	void *func = alien_offset(special_objects[OBJ_SLEEP_CALLBACK]);
	CODE_TO_FUNCTION_POINTER(func);
	((void (*)(long))func)(us);
}

void factor_vm::start_standalone_factor(int argc, vm_char **argv)
{
	vm_parameters p;
	default_parameters(&p);
	init_parameters_from_args(&p,argc,argv);
	init_factor(&p);
	pass_args_to_factor(argc,argv);
	start_factor(&p);
}

factor_vm *new_factor_vm()
{
	factor_vm *newvm = new factor_vm();
	register_vm_with_thread(newvm);
	thread_vms[thread_id()] = newvm;

	return newvm;
}

VM_C_API void start_standalone_factor(int argc, vm_char **argv)
{
	factor_vm *newvm = new_factor_vm();
	return newvm->start_standalone_factor(argc,argv);
}

}
<|endoftext|>