GALib-Parallel
Header-only C++ library for parallel evolutionary algorithms
Loading...
Searching...
No Matches
BinarySerializer.h
Go to the documentation of this file.
1#ifndef BINARY_SERIALIZER_H
2#define BINARY_SERIALIZER_H
3
4#pragma once
5
7#include <vector>
8#include <cstdint>
9#include <bit>
10#include <algorithm>
11#include <type_traits>
12
13namespace galib::internal {
14 template <typename GeneType>
15 class BinarySerializer : public Serializer<GeneType> {
16 static_assert(std::is_trivially_copyable_v<GeneType>, "GeneType must be trivially copyable for binary serialization");
17
18 public:
19 std::vector<std::uint8_t> serialize(const std::vector<Individual<GeneType>>& individuals) const override {
20 if (individuals.empty()) { return {}; }
21
22 const auto individuals_count = static_cast<std::uint32_t>(individuals.size());
23 const auto individual_genes_count = static_cast<std::uint32_t>(individuals[0].getGenotype().size());
24
25 const auto genotype_size_bytes = sizeof(GeneType) * individual_genes_count;
26
27 const std::size_t total_size_bytes = (sizeof(std::uint32_t) * 2) + (individuals_count * (sizeof(double) + genotype_size_bytes));
28
29 std::vector<std::uint8_t> data_serialized;
30 data_serialized.reserve(total_size_bytes);
31
32 appendSerialize(data_serialized, individuals_count);
33 appendSerialize(data_serialized, individual_genes_count);
34
35 for (const auto& individual : individuals) {
36 appendSerialize(data_serialized, individual.getFitness());
37
38 const auto& genotype = individual.getGenotype();
39
40 if constexpr (std::endian::native == std::endian::little) {
41 const auto* data_ptr = reinterpret_cast<const std::uint8_t*>(genotype.data());
42 data_serialized.insert(data_serialized.end(), data_ptr, data_ptr + genotype_size_bytes);
43 } else {
44 for (const auto& gene : genotype) {
45 appendSerialize(data_serialized, gene);
46 }
47 }
48 }
49
50 return data_serialized;
51 }
52
53 std::vector<std::uint8_t> serialize(const Individual<GeneType>& individual) const override {
54 const auto individual_genes_count = static_cast<std::uint32_t>(individual.getGenotype().size());
55
56 const auto genotype_size_bytes = sizeof(GeneType) * individual_genes_count;
57
58 const std::size_t total_size_bytes = (sizeof(std::uint32_t) * 2) + (sizeof(double) + genotype_size_bytes);
59
60 std::vector<std::uint8_t> data_serialized;
61 data_serialized.reserve(total_size_bytes);
62
63 appendSerialize(data_serialized, static_cast<std::uint32_t>(1));
64 appendSerialize(data_serialized, individual_genes_count);
65
66 appendSerialize(data_serialized, individual.getFitness());
67
68 const auto& genotype = individual.getGenotype();
69
70 if constexpr (std::endian::native == std::endian::little) {
71 const auto* data_ptr = reinterpret_cast<const std::uint8_t*>(genotype.data());
72 data_serialized.insert(data_serialized.end(), data_ptr, data_ptr + genotype_size_bytes);
73 } else {
74 for (const auto& gene : genotype) {
75 appendSerialize(data_serialized, gene);
76 }
77 }
78
79 return data_serialized;
80 }
81
82 std::vector<Individual<GeneType>> deserialize(const std::vector<std::uint8_t>& individuals_serialized) const override {
83 if (individuals_serialized.empty()) {
84 return {};
85 }
86
87 const std::uint8_t* read_ptr = individuals_serialized.data();
88
89 const auto individuals_count = readDeserialize<uint32_t>(read_ptr);
90 const auto individual_genes_count = readDeserialize<uint32_t>(read_ptr);
91
92 std::vector<Individual<GeneType>> individuals;
93 individuals.reserve(individuals_count);
94
95 for (std::size_t i = 0; i < individuals_count; ++i) {
96 const double fitness = readDeserialize<double>(read_ptr);
97
98 std::vector<GeneType> genotype(individual_genes_count);
99
100 if constexpr (std::endian::native == std::endian::little) {
101 const std::size_t genotype_bytes = sizeof(GeneType) * individual_genes_count;
102 std::copy_n(read_ptr, genotype_bytes, reinterpret_cast<std::uint8_t*>(genotype.data()));
103 read_ptr += genotype_bytes;
104 } else {
105 for (std::size_t j = 0; j < individual_genes_count; ++j) {
106 genotype[j] = readDeserialize<GeneType>(read_ptr);
107 }
108 }
109
110 Individual<GeneType> individual;
111 individual.setGenotype(std::move(genotype));
112 individual.setFitness(fitness);
113 individuals.push_back(std::move(individual));
114 }
115
116 return individuals;
117 }
118
119 std::vector<Individual<GeneType>> deserialize(const std::uint8_t* data, std::size_t size) const override {
120 if (data == nullptr || size == 0) {
121 return {};
122 }
123
124 const std::uint8_t* read_ptr = data;
125
126 const auto individuals_count = readDeserialize<uint32_t>(read_ptr);
127 const auto individual_genes_count = readDeserialize<uint32_t>(read_ptr);
128
129 std::vector<Individual<GeneType>> individuals;
130 individuals.reserve(individuals_count);
131
132 for (std::size_t i = 0; i < individuals_count; ++i) {
133 const double fitness = readDeserialize<double>(read_ptr);
134
135 std::vector<GeneType> genotype(individual_genes_count);
136
137 if constexpr (std::endian::native == std::endian::little) {
138 const std::size_t genotype_bytes = sizeof(GeneType) * individual_genes_count;
139 std::copy_n(read_ptr, genotype_bytes, reinterpret_cast<std::uint8_t*>(genotype.data()));
140 read_ptr += genotype_bytes;
141 } else {
142 for (std::size_t j = 0; j < individual_genes_count; ++j) {
143 genotype[j] = readDeserialize<GeneType>(read_ptr);
144 }
145 }
146
147 Individual<GeneType> individual;
148 individual.setGenotype(std::move(genotype));
149 individual.setFitness(fitness);
150 individuals.push_back(std::move(individual));
151 }
152
153 return individuals;
154 }
155
156 [[nodiscard]] std::size_t getSerializedSize(const std::size_t individuals_count, const std::size_t genes_count) const override {
157 if (individuals_count == 0) return 0;
158
159 const auto genotype_size_bytes = sizeof(GeneType) * genes_count;
160 return (sizeof(std::uint32_t) * 2) + (individuals_count * (sizeof(double) + genotype_size_bytes));
161 }
162
163 private:
164 template <typename T>
165 static void appendSerialize(std::vector<uint8_t>& buffer, T value) {
166 if constexpr (std::endian::native == std::endian::big) {
167 auto* data_ptr = reinterpret_cast<std::uint8_t*>(&value);
168 std::reverse(data_ptr, data_ptr + sizeof(T));
169 }
170
171 const auto bytes_ptr = reinterpret_cast<const std::uint8_t*>(&value);
172 buffer.insert(buffer.end(), bytes_ptr, bytes_ptr + sizeof(T));
173 }
174
175 template <typename T>
176 static T readDeserialize(const std::uint8_t*& ptr) {
177 T value;
178 std::copy_n(ptr, sizeof(T), reinterpret_cast<std::uint8_t*>(&value));
179 ptr += sizeof(T);
180
181 if constexpr (std::endian::native == std::endian::big) {
182 auto* const data_ptr = reinterpret_cast<std::uint8_t*>(&value);
183 std::reverse(data_ptr, data_ptr + sizeof(T));
184 }
185
186 return value;
187 }
188 };
189}
190
191#endif // BINARY_SERIALIZER_H
Represents a single candidate solution in the population.
Definition Individual.h:23
double getFitness() const
Gets the current fitness score.
Definition Individual.h:77
void setGenotype(const std::vector< GeneType > &new_genotype)
Sets the genotype by copying.
Definition Individual.h:65
std::vector< GeneType > & getGenotype()
Returns a reference to the genotype vector.
Definition Individual.h:53
void setFitness(double new_fitness)
Sets a new fitness score.
Definition Individual.h:83
std::vector< std::uint8_t > serialize(const Individual< GeneType > &individual) const override
Serializes a single individual.
std::vector< Individual< GeneType > > deserialize(const std::vector< std::uint8_t > &individuals_serialized) const override
Deserializes a byte vector into individuals.
std::vector< Individual< GeneType > > deserialize(const std::uint8_t *data, std::size_t size) const override
Deserializes from a raw pointer.
std::size_t getSerializedSize(const std::size_t individuals_count, const std::size_t genes_count) const override
Calculates the exact size required to serialize a specific number of individuals.
std::vector< std::uint8_t > serialize(const std::vector< Individual< GeneType > > &individuals) const override
Serializes multiple individuals into a byte vector.
Interface for converting individuals to/from byte streams for network transport.
Definition Serializer.h:19