PLYwoot
Header-only C++17 library for parsing and writing PLY files
Loading...
Searching...
No Matches
binary_parser_policy.hpp
Go to the documentation of this file.
1/*
2 This file is part of PLYwoot, a header-only PLY parser.
3
4 Copyright (C) 2023-2025, Ton van den Heuvel
5
6 PLYwoot is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#ifndef PLYWOOT_BINARY_PARSER_POLICY_HPP
21#define PLYWOOT_BINARY_PARSER_POLICY_HPP
22
24
25#include "buffered_istream.hpp"
26#include "endian.hpp"
27#include "type_traits.hpp"
28
29#include <cstdint>
30#include <numeric>
31
32namespace plywoot::detail {
33
37template<typename Endianness>
38class BinaryParserPolicy
39{
40public:
44 BinaryParserPolicy(std::istream &is) : is_{is} {}
45
50 void skipElement(const PlyElement &e) const
51 {
52 // In case all element properties are non-list properties, we can calculate
53 // the element size in one go.
54 const std::vector<PlyProperty> &properties = e.properties();
55 if (std::all_of(properties.begin(), properties.end(), [](const PlyProperty &p) { return !p.isList(); }))
56 {
57 is_.skip(
58 e.size() * std::accumulate(
59 properties.begin(), properties.end(), static_cast<std::size_t>(0),
60 [](std::size_t acc, const PlyProperty &p) { return acc + sizeOf(p.type()); }));
61 }
62 else
63 {
64 for (std::size_t i = 0; i < e.size(); ++i)
65 {
66 for (const PlyProperty &p : e.properties()) { skipProperty(p); }
67 }
68 }
69 }
70
75 void skipProperty(const PlyProperty &p) const
76 {
77 if (!p.isList()) { is_.skip(sizeOf(p.type())); }
78 else
79 {
80 std::size_t size = 0;
81 switch (p.sizeType())
82 {
83 case PlyDataType::Char:
84 size = readNumber<char>();
85 break;
86 case PlyDataType::UChar:
87 size = readNumber<unsigned char>();
88 break;
89 case PlyDataType::Short:
90 size = readNumber<short>();
91 break;
92 case PlyDataType::UShort:
93 size = readNumber<unsigned short>();
94 break;
95 case PlyDataType::Int:
96 size = readNumber<int>();
97 break;
98 case PlyDataType::UInt:
99 size = readNumber<unsigned int>();
100 break;
101 case PlyDataType::Float:
102 size = readNumber<float>();
103 break;
104 case PlyDataType::Double:
105 size = readNumber<double>();
106 break;
107 }
108
109 is_.skip(size * sizeOf(p.type()));
110 }
111 }
112
117 template<typename T, typename EndiannessDependent = Endianness>
118 T readNumber() const
119 {
120 if constexpr (std::is_same_v<EndiannessDependent, HostEndian>) { return is_.read<T>(); }
121 else { return byte_swap(is_.read<T>()); }
122 }
123
132 template<typename PlyT, typename DestT, std::size_t N, typename EndiannessDependent = Endianness>
133 std::uint8_t *readNumbers(std::uint8_t *dest) const
134 {
135 if constexpr (std::is_same_v<EndiannessDependent, HostEndian>) { return is_.read<PlyT, DestT, N>(dest); }
136 else
137 {
138 std::uint8_t *result = is_.read<PlyT, DestT, N>(dest);
139
140 // Perform endianess conversion.
141 DestT *to = reinterpret_cast<DestT *>(dest);
142 for (std::size_t i = 0; i < N; ++i, ++to) { *to = byte_swap(static_cast<PlyT>(*to)); }
143
144 return result;
145 }
146 }
147
149 template<typename T>
150 void skipNumber() const
151 {
152 is_.skip(sizeof(T));
153 }
154
158 void skipProperties(std::size_t n) const { is_.skip(n); }
159
167 template<typename... Ts, typename EndiannessDependent = Endianness>
168 void memcpy(std::uint8_t *dest, const PlyElement &element) const
169 {
170 is_.memcpy(dest, element.size() * detail::sizeOf<Ts...>());
171
172 if constexpr (!std::is_same_v<EndiannessDependent, HostEndian>)
173 {
174 for (std::size_t i = 0; i < element.size(); ++i) { dest = byteSwap<Ts...>(dest); }
175 }
176 }
177
178private:
179 template<typename T>
180 std::uint8_t *byteSwap(std::uint8_t *dest) const
181 {
182 if constexpr (!detail::IsPack<T>::value)
183 {
184 auto ptr = reinterpret_cast<T *>(dest);
185 *ptr = byte_swap(*ptr);
186
187 return dest + sizeof(T);
188 }
189 else
190 {
191 auto ptr = reinterpret_cast<typename T::DestT *>(dest);
192 for (std::size_t i = 0; i < T::size; ++i, ++ptr) { *ptr = byte_swap(*ptr); }
193
194 return dest + detail::sizeOf<T>();
195 }
196 }
197
198 template<typename T, typename U, typename... Ts>
199 std::uint8_t *byteSwap(std::uint8_t *dest) const
200 {
201 static_assert(
202 detail::isPacked<T, U, Ts...>(),
203 "converting endianness of possibly padded range of objects not implemented yet");
204
205 return byteSwap<U, Ts...>(byteSwap<T>(dest));
206 }
207
209 mutable detail::BufferedIStream is_;
210};
211
213using BinaryLittleEndianParserPolicy = BinaryParserPolicy<LittleEndian>;
215using BinaryBigEndianParserPolicy = BinaryParserPolicy<BigEndian>;
216
217}
218
219#endif
BinaryParserPolicy< LittleEndian > BinaryLittleEndianParserPolicy
Convenience type alias for the binary little endian parser policy.
BinaryParserPolicy< BigEndian > BinaryBigEndianParserPolicy
Convenience type alias for the binary big endian parser policy.
T byte_swap(T t)
Definition endian.hpp:56
constexpr std::size_t sizeOf()