Skip to content

Commit e7b8c54

Browse files
committed
Updated to use C++20
1 parent dd9048b commit e7b8c54

File tree

3 files changed

+39
-97
lines changed

3 files changed

+39
-97
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ add_library(binary-cpp-library INTERFACE)
88
# Include header files
99
target_include_directories(binary-cpp-library INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
1010

11-
target_compile_features(binary-cpp-library INTERFACE cxx_std_11)
11+
set_target_properties(binary-cpp-library PROPERTIES CXX_STANDARD 23)

README.md

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# [binary++](https://github.com/KojoBailey/binary-cpp-library)
2-
This library for **C++11 and newer** assists reading from and writing to **binary data**, making use of my own experience as a reverse engineer.
2+
This library for **C++20 and newer** assists reading from and writing to **binary data**, making use of my own experience as a reverse engineer.
33

44
It aims to:
5-
- Support as far back as **C++11** for those still using older standards.
5+
- Make use of modern C++ features where **useful** (i.e. `std::endian` and co.).
66
- Be as **open-purposed** as possible for a wide range of use cases.
77
- Mirror the **standard library's style** for interface.
88
- Receive updates as necessary.
@@ -26,19 +26,21 @@ It aims to:
2626
- [`dump_file()`](#dump_file)
2727

2828
## Dependencies
29-
I'm not one to do strenuous testing on various different platforms and compilers, but this should work as far back as **C++11**. Earlier support will likely never be implemented by myself.
30-
31-
Here is the full list of includes used by this library, and the C++ standards they require.
29+
Here is the full list of includes used by this library:
3230
```cpp
33-
#include <cstring>
31+
#include <bit>
32+
#include <cstring>
3433
#include <fstream>
35-
#include <cstdint> // C++11
36-
#include <type_traits> // C++11
37-
#include <vector> // C++11
34+
#include <cstdint>
35+
#include <string_view>
36+
#include <type_traits>
37+
#include <vector>
3838
```
3939

40+
Otherwise, no external dependencies are used.
41+
4042
## Usage
41-
With a single header file at only ~9KB, it's very easy to start using this library. Support for build systems like **CMake** may be added in the future.
43+
With a single header file at only ~10KB, it's very easy to start using this library. Support for build systems like **CMake** may be added in the future.
4244

4345
```cpp
4446
#include <kojo/binary.hpp> // or something along those lines.
@@ -160,27 +162,27 @@ size_t size();
160162
```
161163
```cpp
162164
kojo::binary foo;
163-
foo.write<std::uint64_t>(23, kojo::endian::big);
164-
foo.write<std::uint32_t>(420, kojo::endian::big);
165+
foo.write<std::uint64_t>(23, std::endian::big);
166+
foo.write<std::uint32_t>(420, std::endian::big);
165167
std::cout << foo.size(); // 12
166168
```
167169

168170
### `set_endian()`
169171
Sets the endianness of an **integer** to big or little. Mostly exists for internal use.
170172
```cpp
171-
template <typename T> T set_endian(T value, kojo::endian endianness);
173+
template <typename T> T set_endian(T value, std::endian endianness);
172174
static_assert(std::is_integral<T>::value, "T must be an integral type.");
173175
```
174176
```cpp
175177
kojo::binary foo;
176-
std::uint32_t number = foo.set_endian(1, kojo::endian::big); // 00 00 00 01
177-
number = foo.set_endian(number, kojo::endian::little); // 01 00 00 00
178+
std::uint32_t number = foo.set_endian(1, std::endian::big); // 00 00 00 01
179+
number = foo.set_endian(number, std::endian::little); // 01 00 00 00
178180
```
179181

180182
### `read()`
181183
Reads from data into a specified type, that being an integer, `char`, or `std::string`.
182184
```cpp
183-
template <typename T> T read(kojo::endian endianness, size_t offset = 0);
185+
template <typename T> T read(std::endian endianness, size_t offset = 0);
184186
static_assert(std::is_integral<T>::value, "T must be an integral type.");
185187
template <typename T> typename std::enable_if<std::is_same<T, char>::value, char>::type read(size_t offset = 0);
186188
template <typename T> typename std::enable_if<std::is_same<T, std::string>::value, std::string>::type read(size_t size = 0, size_t offset = 0);
@@ -189,7 +191,7 @@ template <typename T> typename std::enable_if<std::is_same<T, std::string>::valu
189191
std::vector<char> vec{17, 1, 0, 0, 'h', 'P', 'N', 'G', 'J', 'o', 'h', 'n', '\0', 'B'};
190192
kojo::binary foo{vec.data(), 0, vec.size()};
191193
192-
std::cout << foo.read<std::uint32_t>(kojo::endian::little); // 273
194+
std::cout << foo.read<std::uint32_t>(std::endian::little); // 273
193195
std::cout << foo.read<char>(); // 'h'
194196
std::cout << foo.read<std::string>(3); // "PNG"
195197
std::cout << foo.read<std::string>(); // "John"
@@ -206,8 +208,8 @@ template <typename T> void write(typename std::enable_if<std::is_same<T, std::ve
206208
```
207209
```cpp
208210
kojo::binary foo;
209-
foo.write<std::int64_t>(-281029, kojo::endian::big);
210-
foo.write<std::uint16_t>(934, kojo::endian::little);
211+
foo.write<std::int64_t>(-281029, std::endian::big);
212+
foo.write<std::uint16_t>(934, std::endian::little);
211213
foo.write<char>('E');
212214
foo.write<std::string>("Die Speisekarte, bitte."); // Null-terminated.
213215
foo.write<std::string>("NUCC", 4); // Not null-terminated.
@@ -223,7 +225,7 @@ size_t get_pos();
223225
```cpp
224226
kojo::binary foo;
225227
std::cout << foo.get_pos(); // 0
226-
foo.write<std::uint32_t>(5, kojo::endian::big);
228+
foo.write<std::uint32_t>(5, std::endian::big);
227229
std::cout << foo.get_pos(); // 4
228230
foo.write<std::string>("YouGotCAGEd");
229231
std::cout << foo.get_pos(); // 16

include/kojo/binary.hpp

Lines changed: 16 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#ifndef KOJO_BINARY_LIB
22
#define KOJO_BINARY_LIB
33

4+
#include <bit>
45
#include <cstring> // std::memcpy
56
#include <fstream>
6-
#include <cstdint> // C++11
7-
#include <type_traits> // C++11
8-
#include <vector> // C++11
7+
#include <cstdint>
8+
#include <string_view>
9+
#include <type_traits>
10+
#include <vector>
911

1012
// #include <iostream>
1113
// #include <format>
@@ -25,14 +27,9 @@
2527
/** @note Not `KojoBailey` like on GitHub since that's a bit tedious. */
2628
namespace kojo {
2729

28-
enum class endian {
29-
big = 4321,
30-
little = 1234
31-
};
32-
33-
inline endian system_endian() {
30+
inline std::endian system_endian() {
3431
std::uint32_t num = 0x01020304;
35-
return (reinterpret_cast<char*>(&num)[0] == 1) ? endian::big : endian::little;
32+
return (reinterpret_cast<char*>(&num)[0] == 1) ? std::endian::big : std::endian::little;
3633
}
3734

3835
inline void charswap(unsigned char& a, unsigned char& b) {
@@ -51,42 +48,6 @@ template<typename T> T byteswap(T t) {
5148
return t;
5249
}
5350

54-
template< class classT = void >
55-
class ptr {
56-
private:
57-
const classT* address;
58-
59-
public:
60-
ptr() = default;
61-
template<typename T> ptr(T* init) : address( (decltype(address))init ) {}
62-
63-
/* Get address as certain type. */
64-
decltype(address) addr() { return address; } // char* is default type.
65-
template<typename T = classT> T* addr() { return (T*)address; }
66-
67-
/* Get dereferenced value as certain type. */
68-
template<typename T = classT> T val() { return *(T*)address; }
69-
70-
/* Get dereferenced value line an array. */
71-
template<typename T = classT> T array(int offset) { return *((T*)address + offset); }
72-
73-
template<typename T> friend ptr operator+(ptr left, T right) {
74-
static_assert(std::is_integral<T>::value, "T must be an integral type.");
75-
return ptr((char*)left.addr() + right);
76-
}
77-
template<typename T> friend ptr operator+(T left, ptr right) {
78-
static_assert(std::is_integral<T>::value, "T must be an integral type.");
79-
return ptr((char*)right.addr() + left);
80-
}
81-
template<typename T> friend ptr operator-(ptr left, T right) {
82-
static_assert(std::is_integral<T>::value, "T must be an integral type.");
83-
return ptr((char*)left.addr() - left);
84-
}
85-
std::ptrdiff_t friend operator-(ptr left, ptr right) {
86-
return left.addr() - right.addr();
87-
}
88-
};
89-
9051
class binary {
9152
public:
9253
/** Initialise binary data from filepath. */
@@ -128,6 +89,7 @@ class binary {
12889
internal_storage.push_back(buffer);
12990
}
13091
} else {
92+
internal_storage.reserve(size);
13193
for (int i = 0; i < size; i++) {
13294
internal_storage.push_back(file_input.get());
13395
}
@@ -141,6 +103,7 @@ class binary {
141103
if (size == -1) {
142104
internal_address = (decltype(internal_address))pointer + start;
143105
} else {
106+
internal_storage.reserve(size);
144107
for (int i = start; i < size + start; i++) {
145108
internal_storage.push_back(((decltype(internal_address))pointer)[i]);
146109
}
@@ -168,15 +131,15 @@ class binary {
168131
}
169132

170133
/** Changes the endianness of an integer depending on your system. */
171-
template <typename T> T set_endian(T value, endian endianness) {
134+
template <typename T> T set_endian(T value, std::endian endianness) {
172135
static_assert(std::is_integral<T>::value, "T must be an integral type.");
173136
return (system_endian() != endianness)
174137
? byteswap(value)
175138
: value;
176139
}
177140

178141
/** Reads integer of select size from current position in file. */
179-
template <typename T> T read(endian endianness, size_t offset = 0) {
142+
template <typename T> T read(std::endian endianness, size_t offset = 0) {
180143
static_assert(std::is_integral<T>::value, "T must be an integral type.");
181144
T buffer;
182145
std::memcpy(&buffer, &internal_address[cursor + offset], sizeof(buffer));
@@ -205,27 +168,22 @@ class binary {
205168
return buffer;
206169
}
207170

208-
template <typename T> void write(T value, endian endianness) {
209-
static_assert(std::is_integral<T>::value, "T must be an integral type.");
171+
template <std::integral T> void write(T value, std::endian endianness) {
210172
value = set_endian(value, endianness);
211173
cursor = internal_storage.size();
212174
internal_storage.resize(cursor + sizeof(T));
213175
std::memcpy(&internal_storage[cursor], &value, sizeof(T));
214176
cursor += sizeof(T);
215177
update_pointer();
216178
}
217-
template <typename T> void write(typename std::enable_if<std::is_same<T, char>::value, char>::type value) {
218-
static_assert(std::is_same<T, char>::value, "T must be of the char type.");
179+
template <std::same_as<char> T> void write(const char& value) {
219180
cursor = internal_storage.size();
220181
internal_storage.resize(cursor + 1);
221182
std::memcpy(&internal_storage[cursor], &value, 1);
222183
cursor++;
223184
update_pointer();
224185
}
225-
template <typename T> void write(
226-
typename std::enable_if<std::is_same<T, std::string>::value, std::string>::type value,
227-
size_t length = 0 ) {
228-
static_assert(std::is_same<T, std::string>::value, "T must be of the std::string type.");
186+
template <std::same_as<std::string>> void write(std::string_view value, size_t length = 0 ) {
229187
if (value.size() == 0) return;
230188
size_t padding{1};
231189
if (length == 0) {
@@ -248,16 +206,14 @@ class binary {
248206

249207
update_pointer();
250208
}
251-
template <typename T> void write(typename std::enable_if<std::is_same<T, std::vector<unsigned char>>::value, std::vector<unsigned char>>::type& value) {
252-
static_assert(std::is_same<T, std::vector<unsigned char>>::value, "T must be of the std::vector<unsigned char> type.");
209+
template <std::same_as<std::vector<unsigned char>>> void write(const std::vector<unsigned char>& value) {
253210
cursor = internal_storage.size();
254211
internal_storage.resize(cursor + value.size());
255212
std::memcpy(&internal_storage[cursor], value.data(), value.size());
256213
cursor += value.size();
257214
update_pointer();
258215
}
259-
template <typename T> void write(typename std::enable_if<std::is_same<T, binary>::value, binary>::type& value) {
260-
static_assert(std::is_same<T, binary>::value, "T must be of the kojo::binary type.");
216+
template <std::same_as<binary>> void write(binary& value) {
261217
cursor = internal_storage.size();
262218
if (value.size() == -1 || value.data() == nullptr) return;
263219
internal_storage.resize(cursor + value.size());
@@ -305,22 +261,6 @@ class binary {
305261
if (internal_storage.data() != nullptr)
306262
internal_address = internal_storage.data();
307263
}
308-
309-
void charswap(unsigned char& a, unsigned char& b) {
310-
a ^= b;
311-
b ^= a;
312-
a ^= b;
313-
}
314-
315-
template<typename T> T byteswap(T t) {
316-
static_assert(std::is_integral<T>::value, "T must be an integral type.");
317-
unsigned char* first = (unsigned char*)&t;
318-
unsigned char* last = first + sizeof(T) - 1;
319-
while (first < last) {
320-
charswap(*first++, *last--);
321-
}
322-
return t;
323-
}
324264
};
325265

326266
} // namespace

0 commit comments

Comments
 (0)