// viewer-io.cpp: memory-mapped file access and dequantization
//
// Maps a file read-only into memory. Dequantization dispatches to ggml's
// to_float which covers all supported quantization types (F32, F16, BF16,
// Q4_0, Q4_1, Q5_0, Q5_1, Q8_0, Q2_K through Q8_K, IQ, TQ, I8/I16/I32/I64).

#include "viewer-io.h"

#include <cstring>

#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#else
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

bool mmap_open(const char *path, mapped_file &out, std::string &error) {
  out = {};

#ifdef _WIN32
  HANDLE fh = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr,
                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
  if (fh == INVALID_HANDLE_VALUE) {
    error = "failed to open file";
    return false;
  }

  LARGE_INTEGER sz;
  if (!GetFileSizeEx(fh, &sz)) {
    CloseHandle(fh);
    error = "failed to get file size";
    return false;
  }
  out.size = static_cast<size_t>(sz.QuadPart);

  HANDLE mh = CreateFileMappingA(fh, nullptr, PAGE_READONLY, 0, 0, nullptr);
  if (!mh) {
    CloseHandle(fh);
    error = "failed to create file mapping";
    return false;
  }

  void *ptr = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, 0);
  if (!ptr) {
    CloseHandle(mh);
    CloseHandle(fh);
    error = "failed to map file";
    return false;
  }

  out.data = static_cast<const uint8_t *>(ptr);
  out.file_handle = fh;
  out.map_handle = mh;
#else
  int fd = open(path, O_RDONLY);
  if (fd < 0) {
    error = "failed to open file";
    return false;
  }

  struct stat st;
  if (fstat(fd, &st) != 0) {
    close(fd);
    error = "failed to stat file";
    return false;
  }
  out.size = static_cast<size_t>(st.st_size);

  void *ptr = mmap(nullptr, out.size, PROT_READ, MAP_PRIVATE, fd, 0);
  if (ptr == MAP_FAILED) {
    close(fd);
    error = "failed to mmap file";
    return false;
  }

  out.data = static_cast<const uint8_t *>(ptr);
  out.fd = fd;
#endif

  return true;
}

void mmap_close(mapped_file &file) {
#ifdef _WIN32
  if (file.data) {
    UnmapViewOfFile(file.data);
  }
  if (file.map_handle) {
    CloseHandle(file.map_handle);
  }
  if (file.file_handle) {
    CloseHandle(file.file_handle);
  }
#else
  if (file.data && file.size > 0) {
    munmap(const_cast<uint8_t *>(file.data), file.size);
  }
  if (file.fd >= 0) {
    close(file.fd);
  }
#endif
  file = {};
}

// dequantize one block using ggml's type traits table.
// covers all types that ggml supports (F32, F16, BF16, Q4_0..Q8_K, IQ, TQ, etc).
size_t dequant_to_float(enum ggml_type type, const void *src, float *dst,
                        size_t block_size) {
  const struct ggml_type_traits *traits = ggml_get_type_traits(type);
  if (!traits || !traits->to_float) {
    return 0;
  }
  traits->to_float(src, dst, static_cast<int64_t>(block_size));
  return block_size;
}
