yumetodoの旅とプログラミングとかの記録

旅や登山の記録やプログラミング関連の話とかフリーソフト紹介とか

C++erらしくRAIIしつつmmap

teratail.com

に回答するために、mmapを調べていたら

nnabeyang.hatenablog.com

という記事を見つけたのだが、これはCで書かれているのでC++から使うためにラップした。

どうもWindowsの場合は上述ブログ中のget_filecontentで開いてclose_mapで閉じるらしい。このときget_filecontentで得たmap領域へのポインタとハンドル両方いるのだそう。
unixではget_filecontentで開いてmunmapで閉じるらしい。このときget_filecontentで得たmap領域へのポインタとサイズがいるらしい。

というわけでこれをRAII技法を使ってラップした。

なおついでに

  • C++にはvoid*から任意のポインタへの暗黙変換はないのでキャストが必要
  • キャストはstatic_castを使う

などした。

#ifndef YUMETODO_MMAP_HPP_
#define YUMETODO_MMAP_HPP_
// ref: http://nnabeyang.hatenablog.com/entry/20110620/1308560702
#include <cstdint>
#include <cassert>
#ifdef OS_WINDOWS
#include <stdio.h>
#include <windows.h>
#else

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>

#endif

#ifdef OS_WINDOWS
namespace impl{
std::size_t get_filecontent(char** buf, const char* fpath, HANDLE* map_handle) noexcept {
  unsigned int mode1, mode2, mode3, mode4;
  mode1 = GENERIC_READ;
  //assert(0x80000000 == mode1);
  mode2 = PAGE_READONLY;
  mode3 = FILE_MAP_READ;
  mode4 = FILE_SHARE_READ;
  wchar_t fname[80];
  int len;
  char* p = fpath;
  int i = 0;
  while(*p != '\0')  {
    mbtowc(&fname[i++], p, MB_CUR_MAX);
    p++;
  }
  fname[i] = '\0';
  HANDLE handle = CreateFileW(fname, mode1, mode4, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if(handle == INVALID_HANDLE_VALUE) {
    fprintf(stderr, "file open failed\n");
    exit(1);
  }
  auto size = GetFileSize(handle, 0);
  assert(size >= 0);
  *map_handle = CreateFileMapping(handle, 0, mode2, 0, 0, 0);
  *buf = static_cast<char*>(MapViewOfFile(*map_handle, mode3, 0, 0, 0));
  if(handle != INVALID_HANDLE_VALUE) {
    CloseHandle(handle);
    handle = INVALID_HANDLE_VALUE;
  }
  return std::size_t(size);
}

void close_map(char** inp, HANDLE* map_handle) noexcept {
  UnmapViewOfFile(*inp);
  if(*map_handle != INVALID_HANDLE_VALUE) {
    CloseHandle(*map_handle);
    *map_handle = INVALID_HANDLE_VALUE;
  }
  *inp_ = NULL;
}
}
class mmap_handle {
  char *inp_;
  std::size_t size_;
  HANDLE map_handle_;
public:
  mmap_handle(const char* fpath) noexcept {
    this->size_ = impl::get_filecontent(&inp_, fpath, &map_handle_);
  }
  ~mmap_handle() noexcept {
    impl::close_map(&inp_, &map_handle_);
  }
  char* get() noexcept;
  const char* get() const noexcept;
  std::size_t size() const noexcept;
  char* begin() noexcept;
  const char* begin() const noexcept;
  const char* cbegin() const noexcept;
  char* end() noexcept;
  const char* end() const noexcept;
  const char* cend() const noexcept;
};
#else
namespace impl{
std::size_t get_filecontent(char** buf, const char* fpath) noexcept {
  int fd = open(fpath, O_RDONLY);
  assert(fd > 0);
  struct stat st;
  assert(fstat(fd, &st) >= 0 && st.st_size >= 0);
  const auto re = std::size_t(st.st_size);
  *buf = static_cast<char*>(mmap(NULL, re, PROT_READ, MAP_PRIVATE, fd, 0));
  close(fd);
  return re;
}
}
class mmap_handle {
  char *inp_;
  std::size_t size_;
public:
  mmap_handle(const char* fpath) noexcept {
    this->size_ = impl::get_filecontent(&inp_, fpath);
  }
  ~mmap_handle() noexcept {
    munmap(inp_, size_);
  }
  char* get() noexcept;
  const char* get() const noexcept;
  std::size_t size() const noexcept;
  char* begin() noexcept;
  const char* begin() const noexcept;
  const char* cbegin() const noexcept;
  char* end() noexcept;
  const char* end() const noexcept;
  const char* cend() const noexcept;
};
#endif
inline char* mmap_handle::get() noexcept { return this->inp_; }
inline const char* mmap_handle::get() const noexcept { return this->inp_; }
inline std::size_t mmap_handle::size() const noexcept { return this->size_; }
inline char* mmap_handle::begin() noexcept { return this->inp_; }
inline const char* mmap_handle::begin() const noexcept { return this->inp_; }
inline const char* mmap_handle::cbegin() const noexcept { return this->inp_; }
inline char* mmap_handle::end() noexcept { return this->inp_ + this->size_; }
inline const char* mmap_handle::end() const noexcept { return this->inp_ + this->size_; }
inline const char* mmap_handle::cend() const noexcept { return this->inp_ + this->size_; }
#endif //YUMETODO_MMAP_HPP_

使用例は

wandbox.org