Skip to content

Commit 9de2d74

Browse files
committed
feat: initial commit
0 parents  commit 9de2d74

File tree

11 files changed

+1713
-0
lines changed

11 files changed

+1713
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.cache
2+
.vscode
3+
build

CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
cmake_minimum_required(VERSION 3.12)
2+
cmake_policy(SET CMP0042 NEW)
3+
set(CMAKE_CXX_STANDARD 11)
4+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
5+
6+
project(LSM)
7+
8+
set(header_files ${CMAKE_CURRENT_SOURCE_DIR}/include/libsharedmemory/libsharedmemory.hpp)
9+
10+
add_library(lsm INTERFACE)
11+
target_sources(lsm INTERFACE "$<BUILD_INTERFACE:${header_files}>")
12+
13+
target_include_directories(lsm INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>)
14+
target_include_directories(lsm SYSTEM INTERFACE $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
15+
16+
if (${CMAKE_GENERATOR} MATCHES "Visual")
17+
target_compile_options(lsm INTERFACE -W3 -EHsc)
18+
else()
19+
target_compile_options(lsm INTERFACE -Wall -Wno-missing-braces -std=c++11 -fPIC)
20+
endif()
21+
22+
option(LSM_BUILD_TEST "build test" ON)
23+
if(${LSM_BUILD_TEST} AND (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR))
24+
enable_testing()
25+
add_subdirectory(test/)
26+
endif()

LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
MIT LICENSE
2+
3+
Copyright (c) 2021 Aron Homberg, https://arons.site
4+
Copyright (c) 2017 itch corp., https://itch.io
5+
6+
Permission is hereby granted, free of charge, to any person obtaining
7+
a copy of this software and associated documentation files (the
8+
"Software"), to deal in the Software without restriction, including
9+
without limitation the rights to use, copy, modify, merge, publish,
10+
distribute, sublicense, and/or sell copies of the Software, and to
11+
permit persons to whom the Software is furnished to do so, subject to
12+
the following conditions:
13+
14+
The above copyright notice and this permission notice shall be
15+
included in all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# libsharedmemory (lsm)
2+
3+
`libsharedmemory` is a small C++11 header-only library for using shared memory on Windows, Linux and macOS. It allows for simple read/write data transfer of `uint8_t*` / `unsigned char*` and `std::string`.
4+
5+
# Build
6+
7+
```sh
8+
./build.sh
9+
```
10+
11+
# Test
12+
13+
```sh
14+
./test.sh
15+
```
16+
17+
## License
18+
19+
`libsharedmemory` is released under the MIT license, see the `LICENSE` file.

build.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
cmake -B build
3+
cmake --build build
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
2+
#ifndef INCLUDE_KYR0_LIBSHAREDMEMORY_HPP_
3+
#define INCLUDE_KYR0_LIBSHAREDMEMORY_HPP_
4+
5+
#define KYR0_LIBSHAREDMEMORY_VERSION_MAJOR 1
6+
#define KYR0_LIBSHAREDMEMORY_VERSION_MINOR 0
7+
#define KYR0_LIBSHAREDMEMORY_VERSION_PATCH 0
8+
#endif
9+
10+
#include <cstdint>
11+
#include <cstring>
12+
#include <string>
13+
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
14+
15+
#if defined(_WIN32)
16+
#define WIN32_LEAN_AND_MEAN
17+
#include <windows.h>
18+
#undef WIN32_LEAN_AND_MEAN
19+
#endif
20+
21+
namespace lsm {
22+
23+
enum Error {
24+
kOK = 0,
25+
kErrorCreationFailed = 100,
26+
kErrorMappingFailed = 110,
27+
kErrorOpeningFailed = 120,
28+
};
29+
30+
enum DataType {
31+
kMemoryChanged = 1,
32+
kMemoryTypeString = 2,
33+
kMemoryFloat32Vector = 4,
34+
kMemoryUInt8Vector = 8,
35+
};
36+
37+
class Memory {
38+
public:
39+
// path should only contain alpha-numeric characters, and is normalized
40+
// on linux/macOS.
41+
explicit Memory(std::string path, size_t size, bool persist);
42+
43+
// create a shared memory area and open it for writing
44+
inline Error create() { return createOrOpen(true); };
45+
46+
// open an existing shared memory for reading
47+
inline Error open() { return createOrOpen(false); };
48+
49+
inline size_t size() { return _size; };
50+
51+
inline const std::string &path() { return _path; }
52+
53+
inline uint8_t *data() { return _data; }
54+
55+
void destroy();
56+
57+
~Memory();
58+
59+
private:
60+
Error createOrOpen(bool create);
61+
62+
std::string _path;
63+
uint8_t *_data = nullptr;
64+
size_t _size = 0;
65+
bool _persist = true;
66+
#if defined(_WIN32)
67+
HANDLE _handle;
68+
#else
69+
int _fd = -1;
70+
#endif
71+
};
72+
73+
// Windows shared memory implementation
74+
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
75+
76+
#include <io.h> // CreateFileMappingA, OpenFileMappingA, etc.
77+
78+
Memory::Memory(std::string path, size_t size, bool persist) : _path(path), _size(size), _persist(persist) {};
79+
80+
Error Memory::createOrOpen(bool create) {
81+
if (create) {
82+
DWORD size_high_order = 0;
83+
DWORD size_low_order = static_cast<DWORD>(size_);
84+
85+
_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, // use paging file
86+
NULL, // default security
87+
PAGE_READWRITE, // read/write access
88+
size_high_order, size_low_order,
89+
_path.c_str() // name of mapping object
90+
);
91+
92+
if (!_handle) {
93+
return kErrorCreationFailed;
94+
}
95+
} else {
96+
_handle = OpenFileMappingA(FILE_MAP_READ, // read access
97+
FALSE, // do not inherit the name
98+
_path.c_str() // name of mapping object
99+
);
100+
101+
if (!_handle) {
102+
return kErrorOpeningFailed;
103+
}
104+
}
105+
106+
DWORD access = create ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ;
107+
_data = static_cast<uint8_t *>(MapViewOfFile(_handle, access, 0, 0, _size));
108+
109+
if (!_data) {
110+
return kErrorMappingFailed;
111+
}
112+
return kOK;
113+
}
114+
115+
void Memory::destroy() {
116+
if (_data) {
117+
UnmapViewOfFile(_data);
118+
_data = nullptr;
119+
}
120+
CloseHandle(_handle);
121+
}
122+
123+
Memory::~Memory() {
124+
destroy()
125+
}
126+
#endif // defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
127+
128+
#if defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION) || defined(__ANDROID__)
129+
130+
#include <fcntl.h> // for O_* constants
131+
#include <sys/mman.h> // mmap, munmap
132+
#include <sys/stat.h> // for mode constants
133+
#include <unistd.h> // unlink
134+
135+
#if defined(__APPLE__)
136+
137+
#include <errno.h>
138+
139+
#endif // __APPLE__
140+
141+
#include <stdexcept>
142+
143+
inline Memory::Memory(std::string path, size_t size, bool persist) : _size(size), _persist(persist) {
144+
_path = "/" + path;
145+
};
146+
147+
inline Error Memory::createOrOpen(bool create) {
148+
if (create) {
149+
// shm segments persist across runs, and macOS will refuse
150+
// to ftruncate an existing shm segment, so to be on the safe
151+
// side, we unlink it beforehand.
152+
int ret = shm_unlink(_path.c_str());
153+
if (ret < 0) {
154+
if (errno != ENOENT) {
155+
return kErrorCreationFailed;
156+
}
157+
}
158+
}
159+
160+
int flags = create ? (O_CREAT | O_RDWR) : O_RDONLY;
161+
162+
_fd = shm_open(_path.c_str(), flags, 0755);
163+
if (_fd < 0) {
164+
if (create) {
165+
return kErrorCreationFailed;
166+
} else {
167+
return kErrorOpeningFailed;
168+
}
169+
}
170+
171+
if (create) {
172+
// this is the only way to specify the size of a
173+
// newly-created POSIX shared memory object
174+
int ret = ftruncate(_fd, _size);
175+
if (ret != 0) {
176+
return kErrorCreationFailed;
177+
}
178+
}
179+
180+
int prot = create ? (PROT_READ | PROT_WRITE) : PROT_READ;
181+
182+
void *memory = mmap(nullptr, // addr
183+
_size, // length
184+
prot, // prot
185+
MAP_SHARED, // flags
186+
_fd, // fd
187+
0 // offset
188+
);
189+
190+
if (memory == MAP_FAILED) {
191+
return kErrorMappingFailed;
192+
}
193+
194+
_data = static_cast<uint8_t *>(memory);
195+
196+
if (!_data) {
197+
return kErrorMappingFailed;
198+
}
199+
return kOK;
200+
}
201+
202+
inline void Memory::destroy() {
203+
munmap(_data, _size);
204+
close(_fd);
205+
shm_unlink(_path.c_str());
206+
}
207+
208+
inline Memory::~Memory() {
209+
if (!_persist) {
210+
destroy();
211+
}
212+
}
213+
214+
#endif // defined(__APPLE__) || defined(__linux__) || defined(__unix__) || defined(_POSIX_VERSION) || defined(__ANDROID__)
215+
216+
class SharedMemoryReadStream {
217+
public:
218+
219+
explicit SharedMemoryReadStream(std::string name, uint32_t bufferSize, bool isPersistent):
220+
_memory(name, bufferSize, isPersistent)/*, _isInOddWriteMode(false)*/ {
221+
222+
if (_memory.open() != kOK) {
223+
throw "Shared memory segment could not be opened.";
224+
}
225+
}
226+
227+
inline std::string read() {
228+
unsigned char* memory = _memory.data();
229+
230+
uint32_t size;
231+
std::memcpy(&size, &memory[1], 4 /*uint32 takes 4 byte*/);
232+
233+
// 3) deserialize the buffer vector data
234+
std::string data(reinterpret_cast<const char*>(&memory[5]), size);
235+
return data;
236+
}
237+
238+
private:
239+
Memory _memory;
240+
//bool _isInOddWriteMode;
241+
};
242+
243+
class SharedMemoryWriteStream {
244+
public:
245+
246+
explicit SharedMemoryWriteStream(std::string name, uint32_t bufferSize, bool isPersistent):
247+
_memory(name, bufferSize, isPersistent), _isInOddWriteMode(false) {
248+
249+
if (_memory.create() != kOK) {
250+
throw "Shared memory segment could not be created.";
251+
}
252+
}
253+
254+
// https://stackoverflow.com/questions/18591924/how-to-use-bitmask
255+
inline uint32_t getWriteFlags(uint8_t type) {
256+
// flip state
257+
_isInOddWriteMode = !_isInOddWriteMode;
258+
unsigned char flags = type;
259+
260+
if (_isInOddWriteMode) {
261+
// enable flag, leave rest untouched
262+
flags ^= DataType::kMemoryChanged;
263+
} else {
264+
// disable flag, leave rest untouched
265+
flags &= ~DataType::kMemoryChanged;
266+
}
267+
return flags;
268+
}
269+
270+
inline void write(std::string dataString) {
271+
unsigned char* memory = _memory.data();
272+
273+
// 1) copy change flag into buffer for change detection
274+
memory[0] = getWriteFlags(DataType::kMemoryTypeString);
275+
276+
// 2) copy buffer size into buffer (meta data for deserializing)
277+
const char *stringDataVector = dataString.data();
278+
uint32_t bufferSize = dataString.size();
279+
std::memcpy(&memory[1], &bufferSize, sizeof(bufferSize) /* should be always 4 */);
280+
281+
// 3) copy data vector into memory buffer
282+
std::memcpy(&memory[5 /* 1b status; 4b buffer size */], stringDataVector, bufferSize);
283+
}
284+
285+
inline void destroy() {
286+
_memory.destroy();
287+
}
288+
289+
private:
290+
Memory _memory;
291+
bool _isInOddWriteMode;
292+
};
293+
294+
295+
}; // namespace lsm
296+

test.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh
2+
./build/test/lsm_test && echo "ALL TESTS SUCCESSFUL!" || echo "TEST(S) FAILED!"

test/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
set(source_files test.cc)
2+
add_executable(lsm_test ${source_files})
3+
target_link_libraries(lsm_test PUBLIC lsm)
4+
target_include_directories(lsm_test PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
5+
set_property(TARGET lsm_test PROPERTY CXX_STANDARD 11)
6+
7+
if(MSVC)
8+
target_compile_definitions(lsm_test PRIVATE TYPE_SAFE_TEST_NO_STATIC_ASSERT)
9+
elseif(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
10+
target_compile_definitions(lsm_test PRIVATE TYPE_SAFE_TEST_NO_STATIC_ASSERT)
11+
endif()
12+
13+
add_test(NAME test COMMAND lsm_test)

0 commit comments

Comments
 (0)