#pragma once

#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <torch/csrc/deploy/Exception.h>
#include <unistd.h>
#include <cerrno>
#include <cstdio>

namespace torch {
namespace deploy {

// Memory maps a file into the address space read-only, and manages the lifetime
// of the mapping. Here are a few use cases:
// 1. Used in the loader to read in initial image, and to inspect
// ELF files for dependencies before callling dlopen.
//
// 2. Used in unity to load the elf file.
struct MemFile {
  explicit MemFile(const char* filename_) : fd_(0), mem_(nullptr), n_bytes_(0) {
    fd_ = open(filename_, O_RDONLY);
    MULTIPY_CHECK(
        fd_ != -1, "failed to open {}: {}" + filename_ + strerror(errno));
    // NOLINTNEXTLINE
    struct stat s;
    if (-1 == fstat(fd_, &s)) {
      close(fd_); // destructors don't run during exceptions
      MULTIPY_CHECK(
          false, "failed to stat {}: {}" + filename_ + strerror(errno));
    }
    n_bytes_ = s.st_size;
    mem_ = mmap(nullptr, n_bytes_, PROT_READ, MAP_SHARED, fd_, 0);
    if (MAP_FAILED == mem_) {
      close(fd_);
      MULTIPY_CHECK(
          false, "failed to mmap {}: {}" + filename_ + strerror(errno));
    }
  }
  MemFile(const MemFile&) = delete;
  MemFile& operator=(const MemFile&) = delete;
  [[nodiscard]] const char* data() const {
    return (const char*)mem_;
  }
  ~MemFile() {
    if (mem_) {
      munmap((void*)mem_, n_bytes_);
    }
    if (fd_) {
      close(fd_);
    }
  }
  size_t size() {
    return n_bytes_;
  }
  [[nodiscard]] int fd() const {
    return fd_;
  }

 private:
  int fd_;
  void* mem_;
  size_t n_bytes_;
};

} // namespace deploy
} // namespace torch
