固定長配列データベースの改良
スタックデータベースができたので、固定長データベース(固定長配列データベースではありません。)を実装しましょう、と言いたいところですが、その前にもうちょっと準備が必要です。
固定長データベースを用いて固定長配列データベースを実装しようと思うので、固定長データベースのクラス変数を永続化する必要があります。そこで、固定長配列データベースのヘッダーに固定長データベースのクラスの変数を格納できるような一工夫をしました。
そして、もう一つ、他のプロセスが同一の固定長配列データベースを利用しても問題ないように、他のプロセスによりサイズ変更があったときに、それを検知して再マップする処理も加えました。なお、排他制御を実装していないので、複数のプロセスが書き込む場合には問題が生じますが、一つのプロセスが書き込みだけで、他のプロセスは読み込みだけなら、うまく動作するようです。
以下がそのソースです。"// <<<<"と"// >>>>"で囲まれた部分が変更した部分です。
#include <fcntl.h> #include <errno.h> #include <sys/mman.h> #include <assert.h> #include <iostream> #include <exception> class Exception : public std::exception { public: Exception(const std::string &msg) { message = msg; } virtual ~Exception() throw() {} const char *what() { return message.c_str(); } std::string message; }; template <class T> class Array { public: class Header { public: void initialize() { headerSize = 256; pageSize = sysconf(_SC_PAGE_SIZE); elementSize = sizeof(T); arraySize = 0; validArraySize = 0; size = headerSize; } size_t updateSize(size_t arraysize) { size = ((headerSize + arraysize * elementSize - 1) / pageSize + 1) * pageSize; arraySize = (size - headerSize) / elementSize; return size; } size_t headerSize; size_t pageSize; // page size size_t elementSize; // size of an element size_t arraySize; // number of elements size_t validArraySize; // number of valid elements size_t size; // total size }; Array():fd(-1) {} Array(const std::string &f):fd(-1) { initialize(f); } ~Array() { if (fd != -1) { // <<<< msync(header, initialSize, MS_SYNC); munmap(header, initialSize); // >>>> close(); } } void initialize(const std::string &f) { file = f; if ((fd = open(file.c_str(), O_RDWR, 0666)) == -1) { if (errno == ENOENT) { create(file); } else { throw Exception(strerror(errno)); } } struct stat fs; fstat(fd, &fs); map(fs.st_size); } void map(size_t sz) { if (fd == -1) { throw Exception("Not ready"); } // <<<< initialSize = sz; // >>>> header = (Header*)mmap(0, sz, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0); if ((long long)header == -1){ close(); throw Exception(strerror(errno)); } array = (T*)((unsigned char*)header + header->headerSize); if (sz != header->size) { close(); throw Exception("Sizes are inconsistency"); } } void resize(size_t arraysize) { resizeCapacity(arraysize); if (arraysize != header->validArraySize) { header->validArraySize = arraysize; } } T &at(size_t idx) { if (fd == -1) { throw Exception("Not ready"); } if (idx >= header->validArraySize) { throw Exception("Invalid element"); } return array[idx]; } T &set(size_t idx, T& v) { if (idx >= header->arraySize) { resizeCapacity(idx + 1); } array[idx] = v; if (idx >= header->validArraySize) { header->validArraySize = idx + 1; } return array[idx]; } // <<<< size_t reserved(size_t rs) { return reservedSize += rs; } size_t getReservedSize() { return reservedSize; } void *getReservedVariable(size_t offset) { return (void*)((unsigned char*)(Array<T>::array) - offset); } // >>>> size_t getSize() { return header->validArraySize; } protected: void create(const std::string &file) { if ((fd = open(file.c_str(), O_RDWR|O_CREAT, 0666)) == -1) { throw Exception(strerror(errno)); } Header h; h.initialize(); if (write(fd, &h, sizeof(Header)) == -1) { close(); throw Exception(strerror(errno)); } unsigned char c = 0; for (size_t i = 0; i < h.headerSize - sizeof(Header); i++) { if (write(fd, &c, sizeof(c)) == -1) { close(); throw Exception(strerror(errno)); } } } // <<<< void remap(size_t sz) { msync(header, initialSize, MS_SYNC); munmap(header, initialSize); map(sz); } // >>>> void close() { if (fd != -1) { ::close(fd); } fd = -1; } void resizeCapacity(size_t arraysize) { if (fd == -1) { throw Exception("Not ready"); } // <<<< if (initialSize != header->size) { remap(header->size); } // >>>> Header h; h = *header; size_t s = h.size; h.updateSize(arraysize); if (h.size == s) { return; } if (fd != -1) { msync(header, header->size, MS_SYNC); munmap(header, header->size); close(); } if ((fd = open(file.c_str(), O_RDWR, 0666)) == -1) { throw Exception(strerror(errno)); } if (ftruncate(fd, h.size) == -1) { close(); throw Exception(strerror(errno)); } if (write(fd, &h, sizeof(Header)) == -1) { close(); throw Exception(strerror(errno)); } map(h.size); if (arraysize < header->validArraySize) { header->validArraySize = arraysize; } } protected: Header *header; int fd; T *array; std::string file; // <<<< size_t initialSize; size_t reservedSize; // >>>> };