ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvSerializableObject.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #pragma once
9 
10 // CV_CORE_LIB
11 #include <CVLog.h>
12 #include <CVPlatform.h>
13 #include <CVTypes.h>
14 
15 // System
16 #include <cassert>
17 #include <cstdint>
18 
19 // Qt
20 #include <QDataStream>
21 #include <QFile>
22 #include <QMultiMap>
23 
26 public:
28  virtual ~ccSerializableObject() = default;
29 
31  virtual bool isSerializable() const { return false; }
32 
34 
38  virtual bool toFile(QFile& out, short dataVersion) const { return false; }
39 
41 
48  virtual short minimumFileVersion() const = 0;
49 
52  DF_POINT_COORDS_64_BITS =
53  1,
55  // DGM: inversion is 'historical' ;)
56  DF_SCALAR_VAL_32_BITS = 2,
58  };
59 
61  typedef QMultiMap<unsigned, unsigned> LoadedIDMap;
62 
64 
75  virtual bool fromFile(QFile& in,
76  short dataVersion,
77  int flags,
78  LoadedIDMap& oldToNewIDMap) {
79  return false;
80  }
81 
83 
86  static bool WriteError() {
87  CVLog::Error("Write error (disk full or no access right?)");
88  return false;
89  }
90 
92 
95  static bool ReadError() {
96  CVLog::Error("Read error (corrupted file or no access right?)");
97  return false;
98  }
99 
101 
104  static bool MemoryError() {
105  CVLog::Error("Not enough memory");
106  return false;
107  }
108 
110 
113  static bool CorruptError() {
114  CVLog::Error("File seems to be corrupted");
115  return false;
116  }
117 };
118 
121 public:
123  static short GenericArrayToFileMinVersion() { return 20; }
124 
127  static void CoordsFromDataStream(QDataStream& stream,
128  int flags,
129  PointCoordinateType* out,
130  unsigned count = 1) {
132  for (unsigned i = 0; i < count; ++i, ++out) {
133  double val;
134  stream >> val;
135  *out = static_cast<PointCoordinateType>(val);
136  }
137  } else {
138  for (unsigned i = 0; i < count; ++i, ++out) {
139  float val;
140  stream >> val;
141  *out = static_cast<PointCoordinateType>(val);
142  }
143  }
144  }
145 
148  static void ScalarsFromDataStream(QDataStream& stream,
149  int flags,
150  ScalarType* out,
151  unsigned count = 1) {
153  for (unsigned i = 0; i < count; ++i, ++out) {
154  float val;
155  stream >> val;
156  *out = static_cast<PointCoordinateType>(val);
157  }
158  } else {
159  for (unsigned i = 0; i < count; ++i, ++out) {
160  double val;
161  stream >> val;
162  *out = static_cast<PointCoordinateType>(val);
163  }
164  }
165  }
166 
168 
172  template <class Type, int N, class ComponentType>
173  static bool GenericArrayToFile(const std::vector<Type>& data, QFile& out) {
174  assert(out.isOpen() && (out.openMode() & QIODevice::WriteOnly));
175 
176  // removed to allow saving empty clouds
177  // if (data.empty())
178  //{
179  // return ccSerializableObject::MemoryError();
180  // }
181 
182  // component count (dataVersion>=20)
183  ::uint8_t componentCount = static_cast<::uint8_t>(N);
184  if (out.write((const char*)&componentCount, 1) < 0)
186 
187  // element count = array size (dataVersion>=20)
188  ::uint32_t elementCount = static_cast<::uint32_t>(data.size());
189  if (out.write((const char*)&elementCount, 4) < 0)
191 
192  // array data (dataVersion>=20)
193  {
194  // DGM: do it by chunks, in case it's too big to be processed by the
195  // system
196  const char* _data = (const char*)data.data();
197  qint64 byteCount = static_cast<qint64>(elementCount);
198  byteCount *= sizeof(Type);
199  while (byteCount != 0) {
200  static const qint64 s_maxByteSaveCount =
201  (1 << 26); // 64 Mb each time
202  qint64 saveCount = std::min(byteCount, s_maxByteSaveCount);
203  if (out.write(_data, saveCount) < 0)
205  _data += saveCount;
206  byteCount -= saveCount;
207  }
208  }
209  return true;
210  }
211 
213 
218  template <class Type, int N, class ComponentType>
219  static bool GenericArrayFromFile(std::vector<Type>& data,
220  QFile& in,
221  short dataVersion,
222  const QString& verboseDescription) {
223  ::uint8_t componentCount = 0;
224  ::uint32_t elementCount = 0;
225  if (!ReadArrayHeader(in, dataVersion, componentCount, elementCount)) {
226  return false;
227  }
228  if (componentCount != N) {
230  }
231 
233  QString("Loading %0: %1 elements and %2 dimension(s)")
234  .arg(verboseDescription)
235  .arg(elementCount)
236  .arg(componentCount));
237 
238  if (elementCount) {
239  // try to allocate memory
240  try {
241  data.resize(elementCount);
242  } catch (const std::bad_alloc&) {
244  }
245 
246  // array data (dataVersion>=20)
247  {
248  // Apparently Qt and/or Windows don't like to read too many
249  // bytes in a row...
250  static const qint64 MaxElementPerChunk =
251  (static_cast<qint64>(1) << 24);
252  assert(sizeof(ComponentType) * N == sizeof(Type));
253  qint64 byteCount = static_cast<qint64>(data.size()) *
254  (sizeof(ComponentType) * N);
255  char* dest = (char*)data.data();
256  while (byteCount > 0) {
257  qint64 chunkSize = std::min(MaxElementPerChunk, byteCount);
258  if (in.read(dest, chunkSize) < 0) {
260  }
261  byteCount -= chunkSize;
262  dest += chunkSize;
263  }
264  }
265  }
266 
267  return true;
268  }
269 
272 
277  template <class Type, int N, class ComponentType, class FileComponentType>
279  std::vector<Type>& data,
280  QFile& in,
281  short dataVersion,
282  const QString& verboseDescription,
283  FileComponentType* _autoOffset = nullptr) {
284  ::uint8_t componentCount = 0;
285  ::uint32_t elementCount = 0;
286  if (!ReadArrayHeader(in, dataVersion, componentCount, elementCount)) {
287  return false;
288  }
289  if (componentCount != N) {
291  }
292 
294  QString("Loading %0: %1 elements and %2 dimension(s)")
295  .arg(verboseDescription)
296  .arg(elementCount)
297  .arg(componentCount));
298 
299  if (elementCount) {
300  // try to allocate memory
301  try {
302  data.resize(elementCount);
303  } catch (const std::bad_alloc&) {
305  }
306 
307  // array data (dataVersion>=20)
308  //--> sadly we can't read it as a block...
309  // we must convert each element, value by value!
310  FileComponentType dummyArray[N]{0};
311 
312  ComponentType* _data = (ComponentType*)data.data();
313 
314  size_t elementSize = sizeof(FileComponentType) * N;
315 
316  if (_autoOffset) {
317  // read the first element
318  if (in.read((char*)dummyArray, elementSize) >= 0) {
319  for (unsigned k = 0; k < N; ++k) {
320  _autoOffset[k] = dummyArray[k];
321  *_data++ = 0;
322  }
323  } else {
325  }
326 
327  // read the next elements
328  for (unsigned i = 1; i < elementCount; ++i) {
329  if (in.read((char*)dummyArray, elementSize) >= 0) {
330  for (unsigned k = 0; k < N; ++k) {
331  *_data++ = static_cast<ComponentType>(
332  dummyArray[k] - _autoOffset[k]);
333  }
334  } else {
336  }
337  }
338  } else {
339  // no automatic offset
340  for (unsigned i = 0; i < elementCount; ++i) {
341  if (in.read((char*)dummyArray,
342  sizeof(FileComponentType) * N) >= 0) {
343  for (unsigned k = 0; k < N; ++k) {
344  *_data++ =
345  static_cast<ComponentType>(dummyArray[k]);
346  }
347  } else {
349  }
350  }
351  }
352  }
353 
354  return true;
355  }
356 
357 protected:
358  static bool ReadArrayHeader(QFile& in,
359  short dataVersion,
360  ::uint8_t& componentCount,
361  ::uint32_t& elementCount) {
362  assert(in.isOpen() && (in.openMode() & QIODevice::ReadOnly));
363 
364  if (dataVersion < 20) return ccSerializableObject::CorruptError();
365 
366  // component count (dataVersion>=20)
367  if (in.read((char*)&componentCount, 1) < 0)
369 
370  // element count = array size (dataVersion>=20)
371  if (in.read((char*)&elementCount, 4) < 0)
373 
374  return true;
375  }
376 };
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
#define CV_DB_LIB_API
Definition: CV_db.h:15
int count
static bool PrintVerbose(const char *format,...)
Prints out a verbose formatted message in console.
Definition: CVLog.cpp:103
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
Serializable object interface.
static bool CorruptError()
Sends a custom error message (corrupted file) and returns 'false'.
virtual bool isSerializable() const
Returns whether object is serializable of not.
QMultiMap< unsigned, unsigned > LoadedIDMap
Map of loaded unique IDs (old ID --> new ID)
virtual ~ccSerializableObject()=default
Destructor.
static bool ReadError()
Sends a custom error message (read error) and returns 'false'.
DeserializationFlags
Deserialization flags (bit-field)
virtual short minimumFileVersion() const =0
Returns the minimum file version required to save this instance.
static bool WriteError()
Sends a custom error message (write error) and returns 'false'.
virtual bool toFile(QFile &out, short dataVersion) const
Saves data to binary stream.
virtual bool fromFile(QFile &in, short dataVersion, int flags, LoadedIDMap &oldToNewIDMap)
Loads data from binary stream.
static bool MemoryError()
Sends a custom error message (not enough memory) and returns 'false'.
Serialization helpers.
static bool GenericArrayFromFile(std::vector< Type > &data, QFile &in, short dataVersion, const QString &verboseDescription)
Helper: loads a vector structure from file.
static bool GenericArrayToFile(const std::vector< Type > &data, QFile &out)
Helper: saves a vector to file.
static void ScalarsFromDataStream(QDataStream &stream, int flags, ScalarType *out, unsigned count=1)
static bool ReadArrayHeader(QFile &in, short dataVersion, ::uint8_t &componentCount, ::uint32_t &elementCount)
static bool GenericArrayFromTypedFile(std::vector< Type > &data, QFile &in, short dataVersion, const QString &verboseDescription, FileComponentType *_autoOffset=nullptr)
static void CoordsFromDataStream(QDataStream &stream, int flags, PointCoordinateType *out, unsigned count=1)
static short GenericArrayToFileMinVersion()
Returns the minimum file version to save/load a 'generic array'.
int min(int a, int b)
Definition: cutil_math.h:53