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