Skip to content

Commit d2b9b43

Browse files
committed
add .sync() to flush writes to disk
add writable_file::sync() and resizable_file::sync() with optional offset and size arguments drive-by fix for likely outdated NtQuerySection definition removed unused ntifs() dll module singleton renamed SectionPageProtection that used to shadow a createSection() parameter
1 parent 502617a commit d2b9b43

File tree

6 files changed

+202
-37
lines changed

6 files changed

+202
-37
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2024 Pyarelal Knowles, MIT License
1+
# Copyright (c) 2024-2025 Pyarelal Knowles, MIT License
22

33
cmake_minimum_required(VERSION 3.20)
44

README.md

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# decodeless_mappedfile
22

3-
[`decodeless`](https://github.com/decodeless) (previously no-decode) is a
4-
collection of utility libraries for conveniently reading and writing files via
5-
memory mapping. Components can be used individually or combined.
3+
[`decodeless`](https://github.com/decodeless) is a collection of utility
4+
libraries for conveniently reading and writing files via memory mapping.
5+
Components can be used individually or combined.
66

7-
`decodeless_mappedfile` is a small cross platform file mapping abstraction that
8-
supports reserving virtual address space and growing a file mapping into it for
9-
an exciting new way to write binary files. Also includes convenient read-only
10-
and writable file mapping objects.
7+
`decodeless_mappedfile` is a small cross platform file mapping abstraction. It
8+
provides simple objects to read and write to existing files. However, the more
9+
interesting feature is a resizable mapped file. It works by reserving some
10+
virtual address space and growing a file mapping into it for a really convenient
11+
and fast way to write binary files.
1112

1213
[decodeless_writer](https://github.com/decodeless/writer) conbines this and
1314
[decodeless_allocator](https://github.com/decodeless/allocator) to conveniently
@@ -33,16 +34,16 @@ write complex data structures directly as binary data.
3334

3435
## Code Example
3536

36-
```
37+
```cpp
3738
// Memory map a read-only file
3839
decodeless::file mapped(filename);
3940
const int* numbers = reinterpret_cast<const int*>(mapped.data());
4041
...
4142
```
4243
43-
```
44+
```cpp
4445
// Create a file
45-
size_t maxSize = 4096;
46+
size_t maxSize = 1 << 30; // reserved virtual address; can be huge
4647
decodeless::resizable_file file(filename, maxSize);
4748
EXPECT_EQ(file.size(), 0);
4849
EXPECT_EQ(file.data(), nullptr);
@@ -58,6 +59,35 @@ EXPECT_EQ(numbers[9], 9);
5859
numbers[99] = 99;
5960
```
6061

62+
## Building
63+
64+
This is a header-only C++20 library with CMake integration. Use any of:
65+
66+
- ```cmake
67+
add_subdirectory(path/to/mappedfile)
68+
```
69+
70+
- ```cmake
71+
include(FetchContent)
72+
FetchContent_Declare(
73+
decodeless_mappedfile
74+
GIT_REPOSITORY https://github.com/decodeless/mappedfile.git
75+
GIT_TAG release_tag
76+
GIT_SHALLOW TRUE
77+
)
78+
FetchContent_MakeAvailable(decodeless_mappedfile)
79+
```
80+
81+
- ```cmake
82+
find_package(decodeless_mappedfile REQUIRED CONFIG PATHS paths/to/search)
83+
```
84+
85+
Then,
86+
87+
```cmake
88+
target_link_libraries(myproject PRIVATE decodeless::mappedfile)
89+
```
90+
6191
## Notes
6292

6393
- Windows implementation uses unofficial section API for `NtExtendSection` from

include/decodeless/detail/mappedfile_linux.hpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
// Copyright (c) 2024 Pyarelal Knowles, MIT License
1+
// Copyright (c) 2024-2025 Pyarelal Knowles, MIT License
22

33
#pragma once
44

5+
#include <assert.h>
56
#include <decodeless/detail/mappedfile_common.hpp>
67
#include <errno.h>
78
#include <exception>
89
#include <fcntl.h>
910
#include <limits>
11+
#include <optional>
1012
#include <string.h>
1113
#include <string>
12-
#include <optional>
1314
#include <sys/mman.h>
1415
#include <sys/stat.h>
1516
#include <sys/types.h>
@@ -128,14 +129,25 @@ class MemoryMap {
128129
return static_cast<address_type>(static_cast<byte_type*>(m_address) + offset);
129130
}
130131
size_t size() const { return m_size; }
131-
void sync(int flags = MS_SYNC | MS_INVALIDATE)
132+
void sync(size_t offset, size_t size) const
133+
requires Writable
134+
{
135+
assert(offset + size <= m_size);
136+
size_t alignedOffset = offset & ~(pageSize() - 1);
137+
size_t alignedSize = size + offset - alignedOffset;
138+
void* offsetAddress = static_cast<void*>(
139+
static_cast<std::byte*>(const_cast<void*>(m_address)) + alignedOffset);
140+
if (msync(offsetAddress, alignedSize, MS_SYNC | MS_INVALIDATE) == -1)
141+
throw LastError();
142+
}
143+
void sync() const
132144
requires Writable
133145
{
134146
// ENOMEM "Cannot allocate memory" here likely means something remapped
135147
// the range before this object went out of scope. I haven't found a
136148
// good way to avoid this other than the user being careful to delete
137149
// the object before remapping.
138-
if (msync(const_cast<void*>(m_address), m_size, flags) == -1)
150+
if (msync(const_cast<void*>(m_address), m_size, MS_SYNC | MS_INVALIDATE) == -1)
139151
throw LastError();
140152
}
141153
void resize(size_t size) {
@@ -188,6 +200,16 @@ class MappedFile {
188200

189201
data_type data() const { return m_mapped.address(); }
190202
size_t size() const { return m_mapped.size(); }
203+
void sync() const
204+
requires Writable
205+
{
206+
m_mapped.sync();
207+
}
208+
void sync(size_t offset, size_t size) const
209+
requires Writable
210+
{
211+
m_mapped.sync(offset, size);
212+
}
191213

192214
private:
193215
static constexpr int MapMemoryProtection = Writable ? PROT_READ | PROT_WRITE : PROT_READ;
@@ -218,6 +240,14 @@ class ResizableMappedFile {
218240
if (size)
219241
map(size);
220242
}
243+
void sync() const {
244+
if (m_mapped)
245+
m_mapped->sync();
246+
}
247+
void sync(size_t offset, size_t size) const {
248+
if (m_mapped)
249+
m_mapped->sync(offset, size);
250+
}
221251

222252
// Override default move assignment so m_reserved outlives m_mapped
223253
ResizableMappedFile& operator=(ResizableMappedFile&& other) noexcept {

0 commit comments

Comments
 (0)