IODA Bundle
DataStream.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 1996-2012 ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  * In applying this licence, ECMWF does not waive the privileges and immunities
7  * granted to it by virtue of its status as an intergovernmental organisation nor
8  * does it submit to any jurisdiction.
9  */
10 
11 /// @author Simon Smart
12 /// @date January 2019
13 
14 #ifndef odc_core_DataStream_H
15 #define odc_core_DataStream_H
16 
17 #include <algorithm>
18 #include <cstddef>
19 #include <cstdint>
20 #include <cstring>
21 #include <map>
22 #include <memory>
23 #include <string>
24 #include <vector>
25 
26 #include "eckit/io/Buffer.h"
27 #include "eckit/io/Offset.h"
28 
29 #include "odc/core/Exceptions.h"
30 
31 
32 namespace odc {
33 namespace core {
34 
35 //----------------------------------------------------------------------------------------------------------------------
36 
37 // Specialise behaviour for big/little endianness
38 
39 struct SameByteOrder {
40  /// With same byte order, we always to nothing!
41  template<typename T> static void swap(T &) {}
42  static void swap(char* addr, size_t size) {}
43 };
44 
45 
47  template<typename T>
48  static void swap(T& o) {
49  std::reverse(reinterpret_cast<char*>(&o), reinterpret_cast<char*>(&o) + sizeof(T));
50  }
51  static void swap(char* addr, size_t size) {
52  std::reverse(addr, addr+size);
53  }
54 };
55 
56 //----------------------------------------------------------------------------------------------------------------------
57 
58 template <typename ByteOrder>
59 class DataStream {
60 
61 public:
62 
63  DataStream(const void* data, size_t size, bool const=true);
64  DataStream(const eckit::Buffer& data);
65  DataStream(void* data, size_t size);
66  DataStream(eckit::Buffer& data);
67  ~DataStream();
68 
69  eckit::Offset position() const;
70 
71  // Reading
72 
73  template <typename T> void read(T& elem);
74  template <typename T> void read(std::vector<T>& vec);
75  template <typename T, typename S> void read(std::map<T,S>& props);
76  void read(std::string& s);
77 
78  void read(void* addr, size_t bytes);
79  void readBytes(void* addr, size_t bytes); // ReadBytes does no endianness checks
80 
81  // Writing
82 
83  template <typename T> void write(const T& elem);
84  template <typename T> void write(const std::vector<T>& vec);
85  template <typename T, typename S> void write(const std::map<T,S>& props);
86  void write(const std::string& s);
87 
88  void write(const void* addr, size_t bytes);
89  void writeBytes(const void* addr, size_t bytes); // ReadBytes does no endianness checks
90 
91  // These are a hack to get WriterBufferingIterator::doWriteRow to work.
92  // TODO: Remove this hack.
93 
94  char* get() { return current_; }
95  void set(char* p) { ASSERT(p >= start_); ASSERT(p <= end_); current_ = p; }
96  void advance(size_t nbytes) { current_ += nbytes; ASSERT(current_ <= end_); }
97 
98 private: // members
99 
100  bool const_;
101  char* start_;
102  char* current_;
103  char* end_;
104 };
105 
106 //----------------------------------------------------------------------------------------------------------------------
107 
108 // GeneralDataStream provides a way to store DataStreams.
109 // This can probably be done better, but I want the read/write functions to be both
110 // templated, and non-virtual, in the specific-data-order cases.
111 
113 
114 public:
115 
117 
118  template <typename ...Args>
119  GeneralDataStream(bool otherByteOrder, Args&&... args) :
120  sameDs_(otherByteOrder ? 0 : new DataStream<SameByteOrder>(std::forward<Args>(args)...)),
121  otherDs_(otherByteOrder ? new DataStream<OtherByteOrder>(std::forward<Args>(args)...) : 0) {}
122 
124 
127 
128  bool isOther() const { return !!otherDs_; }
131 
132  eckit::Offset position() const {
133  ASSERT(sameDs_ || otherDs_);
134  return sameDs_ ? sameDs_->position() : otherDs_->position();
135  }
136 
137  template <typename ...Args>
138  void read(Args&&... args) {
139  ASSERT(sameDs_ || otherDs_);
140  sameDs_ ? sameDs_->read(std::forward<Args>(args)...) : otherDs_->read(std::forward<Args>(args)...);
141  }
142 
143  template <typename ...Args>
144  void readBytes(Args&&... args) {
145  ASSERT(sameDs_ || otherDs_);
146  sameDs_ ? sameDs_->readBytes(std::forward<Args>(args)...) : otherDs_->readBytes(std::forward<Args>(args)...);
147  }
148 
149  template <typename ...Args>
150  void write(Args&&... args) {
151  ASSERT(sameDs_ || otherDs_);
152  sameDs_ ? sameDs_->write(std::forward<Args>(args)...) : otherDs_->write(std::forward<Args>(args)...);
153  }
154 
155  template <typename ...Args>
156  void writeBytes(Args&&... args) {
157  ASSERT(sameDs_ || otherDs_);
158  sameDs_ ? sameDs_->writeBytes(std::forward<Args>(args)...) : otherDs_->writeBytes(std::forward<Args>(args)...);
159  }
160 
161 private: // members
162 
163  std::unique_ptr<DataStream<SameByteOrder>> sameDs_;
164  std::unique_ptr<DataStream<OtherByteOrder>> otherDs_;
165 };
166 
167 
168 //----------------------------------------------------------------------------------------------------------------------
169 
170 template <typename ByteOrder>
171 inline DataStream<ByteOrder>::DataStream(const void* data, size_t size, bool cnst) :
172  const_(cnst),
173  start_(const_cast<char*>(reinterpret_cast<const char*>(data))),
174  current_(start_),
175  end_(start_ + size) {}
176 
177 
178 template <typename ByteOrder>
179 inline DataStream<ByteOrder>::DataStream(const eckit::Buffer& buffer) :
180  DataStream<ByteOrder>(buffer.data(), buffer.size()) {}
181 
182 
183 template <typename ByteOrder>
184 inline DataStream<ByteOrder>::DataStream(void* data, size_t size) :
185  DataStream<ByteOrder>(data, size, false) {}
186 
187 
188 template <typename ByteOrder>
189 inline DataStream<ByteOrder>::DataStream(eckit::Buffer& buffer) :
190  DataStream<ByteOrder>(buffer.data(), buffer.size()) {}
191 
192 
193 template <typename ByteOrder>
195 
196 
197 template <typename ByteOrder>
198 inline eckit::Offset DataStream<ByteOrder>::position() const {
199  return static_cast<size_t>(current_ - start_);
200 }
201 
202 
203 template <typename ByteOrder>
204 template <typename T>
205 inline void DataStream<ByteOrder>::read(T& elem) {
206  read(&elem, sizeof(elem));
207 }
208 
209 
210 template <typename ByteOrder>
211 template <typename T>
212 inline void DataStream<ByteOrder>::read(std::vector<T>& vec) {
213  vec.clear();
214  int32_t count;
215  read(count);
216  vec.resize(count);
217  for (auto& elem : vec) read(elem);
218 }
219 
220 
221 template <typename ByteOrder>
222 template <typename T, typename S>
223 inline void DataStream<ByteOrder>::read(std::map<T, S>& props) {
224  props.clear();
225  int32_t count;
226  read(count);
227  for (int32_t i = 0; i < count; i++) {
228  T first;
229  S second;
230  read(first);
231  read(second);
232  props.emplace(std::move(first), std::move(second));
233  }
234 }
235 
236 
237 template <typename ByteOrder>
238 inline void DataStream<ByteOrder>::read(std::string& s) {
239 
240  int32_t len;
241  read(len);
242 
243  s.resize(len);
244  readBytes(&s[0], len); // n.b. raw read. Bytes are in order.
245 }
246 
247 
248 // read --> takes endianness into account.
249 template <typename ByteOrder>
250 inline void DataStream<ByteOrder>::read(void* addr, size_t bytes) {
251  readBytes(addr, bytes);
252  ByteOrder::swap(reinterpret_cast<char*>(addr), bytes);
253 }
254 
255 
256 // readBytes is a raw read --> do not flip endianness.
257 template <typename ByteOrder>
258 inline void DataStream<ByteOrder>::readBytes(void* addr, size_t bytes) {
259 
260  char* newpos = current_ + bytes;
261 
262  if (newpos > end_) {
263  std::stringstream ss;
264  ss << "Attempting to read " << bytes
265  << " bytes from DataStream with only " << (end_ - current_) << " bytes remaining";
266  throw ODBEndOfDataStream(ss.str(), Here());
267  }
268 
269  ::memcpy(addr, current_, bytes);
270  current_ = newpos;
271 }
272 
273 
274 template <typename ByteOrder>
275 template <typename T>
276 inline void DataStream<ByteOrder>::write(const T& elem) {
277  write(&elem, sizeof(elem));
278 }
279 
280 
281 template <typename ByteOrder>
282 template <typename T>
283 inline void DataStream<ByteOrder>::write(const std::vector<T>& vec) {
284  int32_t len = vec.size();
285  write(len);
286  for (const auto& elem : vec) write(elem);
287 }
288 
289 
290 template <typename ByteOrder>
291 template <typename T, typename S>
292 inline void DataStream<ByteOrder>::write(const std::map<T, S>& props) {
293  int32_t len = props.size();
294  write(len);
295  for (const auto& kv : props) {
296  write(kv.first);
297  write(kv.second);
298  }
299 }
300 
301 
302 template <typename ByteOrder>
303 inline void DataStream<ByteOrder>::write(const std::string& s) {
304 
305  int32_t len = s.length();
306  write(len);
307  writeBytes(&s[0], len); // n.b. raw read. Bytes are in order.
308 }
309 
310 
311 // read --> takes endianness into account.
312 template <typename ByteOrder>
313 inline void DataStream<ByteOrder>::write(const void* addr, size_t bytes) {
314  writeBytes(addr, bytes);
315  ByteOrder::swap(current_ - bytes, bytes); // n.b. swap bytes on mutable target, not unknown source.
316 }
317 
318 
319 // readBytes is a raw read --> do not flip endianness.
320 template <typename ByteOrder>
321 inline void DataStream<ByteOrder>::writeBytes(const void* addr, size_t bytes) {
322 
323  ASSERT(!const_);
324 
325  char* newpos = current_ + bytes;
326 
327  if (newpos > end_) {
328  std::stringstream ss;
329  ss << "Attempting to write " << bytes
330  << " to DataStream with only " << (end_ - current_) << " bytes remaining";
331  throw ODBEndOfDataStream(ss.str(), Here());
332  }
333 
334  ::memcpy(current_, addr, bytes);
335  current_ = newpos;
336 }
337 
338 //----------------------------------------------------------------------------------------------------------------------
339 
340 } // namespace core
341 } // namespace odc
342 
343 #endif
StringTools S
static void count(void *counter, const double *data, size_t n)
Definition: UnitTests.cc:531
void writeBytes(const void *addr, size_t bytes)
Definition: DataStream.h:321
void set(char *p)
Definition: DataStream.h:95
void read(T &elem)
Definition: DataStream.h:205
eckit::Offset position() const
Definition: DataStream.h:198
void readBytes(void *addr, size_t bytes)
Definition: DataStream.h:258
void advance(size_t nbytes)
Definition: DataStream.h:96
DataStream(const void *data, size_t size, bool const=true)
Definition: DataStream.h:171
void write(const T &elem)
Definition: DataStream.h:276
void read(Args &&... args)
Definition: DataStream.h:138
GeneralDataStream & operator=(GeneralDataStream &&rhs)=default
void writeBytes(Args &&... args)
Definition: DataStream.h:156
std::unique_ptr< DataStream< SameByteOrder > > sameDs_
Definition: DataStream.h:163
void readBytes(Args &&... args)
Definition: DataStream.h:144
std::unique_ptr< DataStream< OtherByteOrder > > otherDs_
Definition: DataStream.h:164
GeneralDataStream(bool otherByteOrder, Args &&... args)
Definition: DataStream.h:119
DataStream< SameByteOrder > & same()
Definition: DataStream.h:129
eckit::Offset position() const
Definition: DataStream.h:132
DataStream< OtherByteOrder > & other()
Definition: DataStream.h:130
void write(Args &&... args)
Definition: DataStream.h:150
GeneralDataStream(GeneralDataStream &&rhs)=default
Definition: ColumnInfo.h:23
Definition: encode.cc:30
static void swap(T &o)
Definition: DataStream.h:48
static void swap(char *addr, size_t size)
Definition: DataStream.h:51
static void swap(T &)
With same byte order, we always to nothing!
Definition: DataStream.h:41
static void swap(char *addr, size_t size)
Definition: DataStream.h:42