1 //
2 // Copyright RIME Developers
3 // Distributed under the BSD License
4 //
5 // register components
6 //
7 // 2011-06-30 GONG Chen <chen.sst@gmail.com>
8 //
9 #include <fstream>
10 #include <boost/filesystem.hpp>
11 #include <boost/interprocess/file_mapping.hpp>
12 #include <boost/interprocess/mapped_region.hpp>
13 #include <rime/dict/mapped_file.h>
14 
15 #ifdef BOOST_RESIZE_FILE
16 
17 #define RESIZE_FILE boost::filesystem::resize_file
18 
19 #else
20 
21 #ifdef _WIN32
22 #include <windows.h>
23 #define RESIZE_FILE(P,SZ) (resize_file_api(P, SZ) != 0)
resize_file_api(const char * p,boost::uintmax_t size)24 static BOOL resize_file_api(const char* p, boost::uintmax_t size) {
25   HANDLE handle = CreateFileA(p, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
26                               FILE_ATTRIBUTE_NORMAL, 0);
27   LARGE_INTEGER sz;
28   sz.QuadPart = size;
29   return handle != INVALID_HANDLE_VALUE
30     && ::SetFilePointerEx(handle, sz, 0, FILE_BEGIN)
31     && ::SetEndOfFile(handle)
32     && ::CloseHandle(handle);
33 }
34 #else
35 #include <unistd.h>
36 #define RESIZE_FILE(P,SZ) (::truncate(P, SZ) == 0)
37 #endif  // _WIN32
38 
39 #endif  // BOOST_RESIZE_FILE
40 
41 namespace rime {
42 
43 class MappedFileImpl {
44  public:
45   enum OpenMode {
46     kOpenReadOnly,
47     kOpenReadWrite,
48   };
49 
MappedFileImpl(const string & file_name,OpenMode mode)50   MappedFileImpl(const string& file_name, OpenMode mode) {
51     boost::interprocess::mode_t file_mapping_mode =
52         (mode == kOpenReadOnly) ? boost::interprocess::read_only
53                                 : boost::interprocess::read_write;
54     file_.reset(new boost::interprocess::file_mapping(file_name.c_str(), file_mapping_mode));
55     region_.reset(new boost::interprocess::mapped_region(*file_, file_mapping_mode));
56   }
~MappedFileImpl()57   ~MappedFileImpl() {
58     region_.reset();
59     file_.reset();
60   }
Flush()61   bool Flush() {
62     return region_->flush();
63   }
get_address() const64   void* get_address() const {
65     return region_->get_address();
66   }
get_size() const67   size_t get_size() const {
68     return region_->get_size();
69   }
70 
71  private:
72   the<boost::interprocess::file_mapping> file_;
73   the<boost::interprocess::mapped_region> region_;
74 
75 };
76 
MappedFile(const string & file_name)77 MappedFile::MappedFile(const string& file_name)
78     : file_name_(file_name) {
79 }
80 
~MappedFile()81 MappedFile::~MappedFile() {
82   if (file_) {
83     file_.reset();
84   }
85 }
86 
Create(size_t capacity)87 bool MappedFile::Create(size_t capacity) {
88   if (Exists()) {
89     LOG(INFO) << "overwriting file '" << file_name_ << "'.";
90     Resize(capacity);
91   }
92   else {
93     LOG(INFO) << "creating file '" << file_name_ << "'.";
94     std::filebuf fbuf;
95     fbuf.open(file_name_.c_str(),
96               std::ios_base::in | std::ios_base::out |
97               std::ios_base::trunc | std::ios_base::binary);
98     if (capacity > 0) {
99       fbuf.pubseekoff(capacity - 1, std::ios_base::beg);
100       fbuf.sputc(0);
101     }
102     fbuf.close();
103   }
104   LOG(INFO) << "opening file for read/write access.";
105   file_.reset(new MappedFileImpl(file_name_, MappedFileImpl::kOpenReadWrite));
106   size_ = 0;
107   return bool(file_);
108 }
109 
OpenReadOnly()110 bool MappedFile::OpenReadOnly() {
111   if (!Exists()) {
112     LOG(ERROR) << "attempt to open non-existent file '" << file_name_ << "'.";
113     return false;
114   }
115   file_.reset(new MappedFileImpl(file_name_, MappedFileImpl::kOpenReadOnly));
116   size_ = file_->get_size();
117   return bool(file_);
118 }
119 
OpenReadWrite()120 bool MappedFile::OpenReadWrite() {
121   if (!Exists()) {
122     LOG(ERROR) << "attempt to open non-existent file '" << file_name_ << "'.";
123     return false;
124   }
125   file_.reset(new MappedFileImpl(file_name_, MappedFileImpl::kOpenReadWrite));
126   size_ = 0;
127   return bool(file_);
128 }
129 
Close()130 void MappedFile::Close() {
131   if (file_) {
132     file_.reset();
133     size_ = 0;
134   }
135 }
136 
Exists() const137 bool MappedFile::Exists() const {
138   return boost::filesystem::exists(file_name_);
139 }
140 
IsOpen() const141 bool MappedFile::IsOpen() const {
142   return bool(file_);
143 }
144 
Flush()145 bool MappedFile::Flush() {
146   if (!file_)
147     return false;
148   return file_->Flush();
149 }
150 
ShrinkToFit()151 bool MappedFile::ShrinkToFit() {
152   LOG(INFO) << "shrinking file to fit data size. capacity: " << capacity();
153   return Resize(size_);
154 }
155 
Remove()156 bool MappedFile::Remove() {
157   if (IsOpen())
158     Close();
159   return boost::interprocess::file_mapping::remove(file_name_.c_str());
160 }
161 
Resize(size_t capacity)162 bool MappedFile::Resize(size_t capacity) {
163   LOG(INFO) << "resize file to: " << capacity;
164   if (IsOpen())
165     Close();
166   try {
167     RESIZE_FILE(file_name_.c_str(), capacity);
168   }
169   catch (...) {
170     return false;
171   }
172   return true;
173 }
174 
CreateString(const string & str)175 String* MappedFile::CreateString(const string& str) {
176   String* ret = Allocate<String>();
177   if (ret && !str.empty()) {
178     CopyString(str, ret);
179   }
180   return ret;
181 }
182 
CopyString(const string & src,String * dest)183 bool MappedFile::CopyString(const string& src, String* dest) {
184   if (!dest)
185     return false;
186   size_t size = src.length() + 1;
187   char* ptr = Allocate<char>(size);
188   if (!ptr)
189     return false;
190   std::strncpy(ptr, src.c_str(), size);
191   dest->data = ptr;
192   return true;
193 }
194 
capacity() const195 size_t MappedFile::capacity() const {
196   return file_->get_size();
197 }
198 
address() const199 char* MappedFile::address() const {
200   return reinterpret_cast<char*>(file_->get_address());
201 }
202 
203 }  // namespace rime
204