qxLib
file_logger_stream_mapping.inl
Go to the documentation of this file.
1 /**
2 
3  @file file_logger_stream_mapping.inl
4  @author Khrapov
5  @date 15.01.2026
6  @copyright © Nick Khrapov, 2026. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
14  const config& streamConfig,
15  unit<size_t, units::data> initialMapSize) noexcept
16  : base_file_logger_stream(streamConfig)
17 {
18  const std::filesystem::path path =
19  create_folder_and_get_log_file_path(streamConfig.eLogFilePolicy, streamConfig.svFileName);
20 
21 #if QX_WIN
22  SYSTEM_INFO si {};
23  GetSystemInfo(&si);
24  m_nGranularity = si.dwAllocationGranularity ? si.dwAllocationGranularity : 65536;
25 
26  m_hFile = CreateFileW(
27  path.c_str(),
28  GENERIC_READ | GENERIC_WRITE,
29  FILE_SHARE_READ,
30  nullptr,
31  streamConfig.eLogFilePolicy == log_file_policy::clear_then_uppend ? CREATE_ALWAYS : OPEN_ALWAYS,
32  FILE_ATTRIBUTE_NORMAL,
33  nullptr);
34 
35  if (m_hFile == INVALID_HANDLE_VALUE)
36  return;
37 
38  LARGE_INTEGER sz {};
39  if (!GetFileSizeEx(m_hFile, &sz))
40  return;
41 
42  m_nSize = static_cast<size_t>(sz.QuadPart);
43 #else
44  const long ps = ::sysconf(_SC_PAGESIZE);
45  m_nGranularity = ps > 0 ? static_cast<size_t>(ps) : 4096ull;
46 
47  int flags = O_RDWR | O_CREAT;
48  if (streamConfig.eLogFilePolicy == log_file_policy::clear_then_uppend)
49  flags |= O_TRUNC;
50 
51  m_Fd = ::open(path.c_str(), flags, 0644);
52  if (m_Fd < 0)
53  return;
54 
55  struct stat st {};
56  if (::fstat(m_Fd, &st) != 0)
57  return;
58 
59  m_nSize = static_cast<size_t>(st.st_size);
60 #endif
61 
62  const size_t nDefaultStartSize = initialMapSize ? convert(initialMapSize).to(units::data::bytes) : m_nGranularity;
63  size_t nInitialSize = m_nSize < nDefaultStartSize ? nDefaultStartSize : m_nSize;
64  nInitialSize = align_up_u64(nInitialSize, m_nGranularity);
65 
66  remap_to_capacity(nInitialSize);
67 }
68 
70  : base_file_logger_stream(std::move(other))
71 {
72 #if QX_WIN
73  std::swap(m_hFile, other.m_hFile);
74  std::swap(m_hMap, other.m_hMap);
75 #else
76  std::swap(m_Fd, other.m_Fd);
77 #endif
78  std::swap(m_pData, other.m_pData);
79  std::swap(m_nSize, other.m_nSize);
80  std::swap(m_nCapacity, other.m_nCapacity);
81  std::swap(m_nGranularity, other.m_nGranularity);
82 }
83 
84 inline file_logger_stream_mapping::~file_logger_stream_mapping() noexcept
85 {
86 #if QX_WIN
87  if (m_hFile == INVALID_HANDLE_VALUE)
88  return;
89 #else
90  if (m_Fd < 0)
91  return;
92 #endif
93 
95 
96  if (m_pData)
97  {
98 #if QX_WIN
99  UnmapViewOfFile(m_pData);
100 #else
101  ::munmap(m_pData, static_cast<size_t>(m_nCapacity));
102 #endif
103  m_pData = nullptr;
104  }
105 
106 #if QX_WIN
107  if (m_hMap)
108  {
109  CloseHandle(m_hMap);
110  m_hMap = nullptr;
111  }
112 #endif
113 
114 #if QX_WIN
115  LARGE_INTEGER li {};
116  li.QuadPart = static_cast<LONGLONG>(m_nSize);
117 
118  if (!SetFilePointerEx(m_hFile, li, nullptr, FILE_BEGIN))
119  return;
120 
121  if (!SetEndOfFile(m_hFile))
122  return;
123 
124  if (!FlushFileBuffers(m_hFile))
125  return;
126 
127  CloseHandle(m_hFile);
128  m_hFile = INVALID_HANDLE_VALUE;
129 #else
130  ::ftruncate(m_Fd, static_cast<off_t>(m_nSize));
131  ::fsync(m_Fd);
132  ::close(m_Fd);
133  m_Fd = -1;
134 #endif
135 
136  m_nCapacity = 0;
137  m_nSize = 0;
138 }
139 
140 inline void file_logger_stream_mapping::do_log(const category& category, verbosity eVerbosity, string_view svMessage)
141 {
142  const size_t nRequiredSize = svMessage.size() * std::min(sizeof(char_type), sizeof(char16_t));
143  if (!ensure_capacity(nRequiredSize))
144  return;
145 
146 #if QX_WIN || QX_CONF_USE_CHAR
147  std::memcpy(m_pData + m_nSize, svMessage.data(), nRequiredSize);
148  m_nSize += nRequiredSize;
149 #else
150  std::array<char16_t, 2048> chunk;
151 
152  const char_type* pData = svMessage.data();
153  size_t nCharsRemaining = svMessage.size();
154 
155  while (nCharsRemaining > 0)
156  {
157  size_t nCharsToTake = std::min(nCharsRemaining, chunk.size());
158  for (size_t i = 0; i < nCharsToTake; ++i)
159  {
160  chunk[i] = static_cast<char16_t>(static_cast<char16_t>(pData[i]) & 0xFFFFu);
161  }
162 
163  std::memcpy(m_pData + m_nSize, chunk.data(), nCharsToTake * sizeof(char16_t));
164 
165  m_nSize += nCharsToTake * sizeof(char16_t);
166  pData += nCharsToTake;
167  nCharsRemaining -= nCharsToTake;
168  }
169 #endif
170 }
171 
173 {
174  if (!m_pData || m_nSize == 0)
175  return;
176 
177  // flush memory mapped data to the file
178 #if QX_WIN
179  if (!FlushViewOfFile(m_pData, m_nSize))
180  return;
181 #else
182  ::msync(m_pData, static_cast<size_t>(m_nSize), MS_SYNC);
183 #endif
184 
185 #if QX_WIN
186  if (m_hFile != INVALID_HANDLE_VALUE)
187 #else
188  if (m_Fd >= 0)
189 #endif
190  {
191  // update the file on disc
192 #if QX_WIN
193  FlushFileBuffers(m_hFile);
194 #else
195  ::fsync(m_Fd);
196 #endif
197 
198  // notify the OS that file changed (makes editors such as VS Code to reload it)
199 #if QX_WIN
200  FILETIME ft;
201  GetSystemTimeAsFileTime(&ft);
202  SetFileTime(m_hFile, nullptr, nullptr, &ft);
203 #else
204  struct timespec ts[2];
205  clock_gettime(CLOCK_REALTIME, &ts[0]);
206  ts[1] = ts[0];
207 
208  futimens(m_Fd, ts);
209 #endif
210  }
211 }
212 
213 inline bool file_logger_stream_mapping::remap_to_capacity(size_t nNewCapacity) noexcept
214 {
215  if (m_pData)
216  {
217 #if QX_WIN
218  UnmapViewOfFile(m_pData);
219 #else
220  ::munmap(m_pData, static_cast<size_t>(m_nCapacity));
221 #endif
222  m_pData = nullptr;
223  }
224 
225 #if QX_WIN
226  if (m_hMap)
227  {
228  CloseHandle(m_hMap);
229  m_hMap = nullptr;
230  }
231 #endif
232 
233 #if QX_WIN
234  LARGE_INTEGER li {};
235  li.QuadPart = static_cast<LONGLONG>(nNewCapacity);
236  if (!SetFilePointerEx(m_hFile, li, nullptr, FILE_BEGIN))
237  return false;
238  if (!SetEndOfFile(m_hFile))
239  return false;
240 #else
241  if (::ftruncate(m_Fd, static_cast<off_t>(nNewCapacity)) != 0)
242  return false;
243 #endif
244 
245 #if QX_WIN
246  DWORD hi = static_cast<DWORD>((nNewCapacity >> 32) & 0xFFFFFFFFu);
247  DWORD lo = static_cast<DWORD>(nNewCapacity & 0xFFFFFFFFu);
248 
249  m_hMap = CreateFileMappingW(m_hFile, nullptr, PAGE_READWRITE, hi, lo, nullptr);
250  if (!m_hMap)
251  return false;
252 
253  void* p = MapViewOfFile(m_hMap, FILE_MAP_ALL_ACCESS, 0, 0, nNewCapacity);
254  if (!p)
255  return false;
256 #else
257  void* p = ::mmap(nullptr, static_cast<size_t>(nNewCapacity), PROT_READ | PROT_WRITE, MAP_SHARED, m_Fd, 0);
258  if (p == MAP_FAILED)
259  return false;
260 #endif
261 
262  m_pData = static_cast<std::byte*>(p);
263  m_nCapacity = nNewCapacity;
264 
265  return true;
266 }
267 
268 inline bool file_logger_stream_mapping::ensure_capacity(size_t nAdditionalBytes) noexcept
269 {
270  if (m_nSize + nAdditionalBytes <= m_nCapacity)
271  return true;
272 
273  // grow: double until enough (with alignment)
274  size_t nRequiredSize = m_nSize + nAdditionalBytes;
275  size_t nNewCapacity = m_nCapacity ? m_nCapacity : m_nGranularity;
276  while (nNewCapacity < nRequiredSize)
277  nNewCapacity *= 2;
278 
279  nNewCapacity = align_up_u64(nNewCapacity, m_nGranularity);
280  return remap_to_capacity(nNewCapacity);
281 }
282 
283 inline size_t file_logger_stream_mapping::align_up_u64(size_t nValue, size_t nAlignment) noexcept
284 {
285  if (nAlignment == 0)
286  return nValue;
287 
288  const size_t nResult = nValue % nAlignment;
289  return nResult ? (nValue + (nAlignment - nResult)) : nValue;
290 }
291 
292 } // namespace qx
Base class for all file logger streams.
A category is a class that identifies a particular piece of code. This code can be located in differe...
Definition: category.h:59
A conversion class.
Definition: base.h:77
High-performance file logger based on memory-mapped I/O.
virtual void do_log(const category &category, verbosity eVerbosity, string_view svMessage) override
Proceed stream logging.
virtual void do_flush() override
Flush the stream.
file_logger_stream_mapping(const config &streamConfig=config(), unit< size_t, units::data > initialMapSize={ 1, units::data::mebibytes }) noexcept
file_logger_stream_mapping object constructor
Wrapper for enumerations to be used as a list of flags.
Definition: flags.h:62
Definition: base.h:52