GCS  0.2.3
gu_rset.hpp
1 /* Copyright (C) 2013 Codership Oy <info@codership.com> */
13 #ifndef _GU_RSET_HPP_
14 #define _GU_RSET_HPP_
15 
16 #include "gu_vector.hpp"
17 #include "gu_alloc.hpp"
18 #include "gu_digest.hpp"
19 
20 #ifdef GU_RSET_CHECK_SIZE
21 # include "gu_throw.hpp"
22 #endif
23 
24 #include <string>
25 
26 namespace gu {
27 
28 class RecordSet
29 {
30 public:
31 
32  enum Version
33  {
34  EMPTY = 0,
35  VER1
36  };
37 
38  static Version const MAX_VERSION = VER1;
39 
40  enum CheckType
41  {
42  CHECK_NONE = 0,
43  CHECK_MMH32,
44  CHECK_MMH64,
45  CHECK_MMH128
46  };
47 
49  size_t size() const { return size_; }
50 
52  int count() const { return count_; }
53 
54  typedef gu::Vector<gu::Buf, 16> GatherVector;
55 
56 protected:
57 
58  ssize_t size_;
59  int count_;
60 
61  Version version_;
62  CheckType check_type_;
63 
64  /* ctor for RecordSetOut */
65  RecordSet (Version const version, CheckType const ct);
66 
67  /* ctor for RecordSetIn */
68  RecordSet ()
69  : size_(0), count_(0), version_(EMPTY), check_type_(CHECK_NONE) {}
70 
71  void init (const byte_t* buf, ssize_t size);
72 
73  ~RecordSet() {}
74 };
75 
76 
77 #if defined(__GNUG__)
78 # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4)
79 # pragma GCC diagnostic push
80 # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4)
81 # pragma GCC diagnostic ignored "-Weffc++"
82 #endif
83 
86 {
87 public:
88 
90 
92  ssize_t page_count() const { return bufs_->size(); }
93 
95  ssize_t gather (GatherVector& out);
96 
97 protected:
98 
100 
101  RecordSetOutBase (byte_t* reserved,
102  size_t reserved_size,
103  const BaseName& base_name, /* basename for on-disk
104  * allocator */
105  CheckType ct,
106  Version version = MAX_VERSION
107 #ifdef GU_RSET_CHECK_SIZE
108  ,ssize_t max_size = 0x7fffffff
109 #endif
110  );
111 
112  /* this is to emulate partial specialization of function template through
113  * overloading by parameter */
114  template <bool store> struct HasPtr{};
115 
116  /* variant for classes that don't provide ptr() method and need to be
117  * explicitly serialized to internal storage */
118  template <class R>
119  void process (const R& record,
120  const byte_t*& ptr,
121  bool& new_page,
122  size_t const size,
123  bool,
125  {
126  byte_t* const dst(alloc_.alloc (size, new_page));
127 
128  new_page = (new_page || !prev_stored_);
129  ptr = dst;
130 
131 #ifdef NDEBUG
132  record.serialize_to (dst, size);
133 #else
134  size_t const ssize (record.serialize_to (dst, size));
135  assert (ssize == size);
136 #endif
137  }
138 
139  /* variant for classes that have ptr() method and can be either serialized
140  * or referenced */
141  template <class R>
142  void process (const R& record,
143  const byte_t*& ptr,
144  bool& new_page,
145  size_t const size,
146  bool const store,
147  HasPtr<true>)
148  {
149  if (store)
150  {
151  process (record, ptr, new_page, size, true, HasPtr<false>());
152  }
153  else
154  {
155  ptr = record.ptr();
156  new_page = true;
157  }
158  }
159 
160  template <class R, bool has_ptr>
161  std::pair<const byte_t*, size_t>
162  append_base (const R& record,
163  bool const store = true,
164  bool const new_record = true)
165  {
166  ssize_t const size (record.serial_size());
167 
168 #ifdef GU_RSET_CHECK_SIZE
169  if (gu_unlikely(size > max_size_ - size_)) gu_throw_error(EMSGSIZE);
170 #endif
171 
172  bool new_page;
173  const byte_t* ptr;
174 
175  process (record, ptr, new_page, size, store, HasPtr<has_ptr>());
176 
177  prev_stored_ = store;
178  // make sure there is at least one record
179  count_ += new_record || (0 == count_);
180 
181  post_append (new_page, ptr, size);
182 
183  return std::pair<const byte_t*, size_t>(ptr, size);
184  }
185 
186 private:
187 
188 #ifdef GU_RSET_CHECK_SIZE
189  ssize_t const max_size_;
190 #endif
191 
192  Allocator alloc_;
193  Hash check_;
194  Vector<Buf, Allocator::INITIAL_VECTOR_SIZE> bufs_;
195  bool prev_stored_;
196 
197  void
198  post_alloc (bool const new_page, const byte_t* const ptr,
199  ssize_t const size);
200 
201  void
202  post_append (bool const new_page, const byte_t* const ptr,
203  ssize_t const size);
204 
205  int header_size () const;
206  int header_size_max () const;
207 
208  /* Writes the header to the end of provided buffer, returns header
209  * offset from ptr */
210  ssize_t write_header (byte_t* ptr, ssize_t size);
211 };
212 
213 
216 template <class R>
218 {
219 public:
220 
222 
224 
225  RecordSetOut (byte_t* reserved,
226  size_t reserved_size,
227  const BaseName& base_name,
228  CheckType ct,
229  Version version = MAX_VERSION
230 #ifdef GU_RSET_CHECK_SIZE
231  ,ssize_t max_size = 0x7fffffff
232 #endif
233  )
234  : RecordSetOutBase (reserved, reserved_size, base_name, ct, version
235 #ifdef GU_RSET_CHECK_SIZE
236  ,max_size
237 #endif
238  )
239  {}
240 
241  std::pair<const byte_t*, size_t>
242  append (const R& r)
243  {
244  return append_base<R, false> (r);
245 // return append_base<R> (r); old append_base() method
246  }
247 
248  std::pair<const byte_t*, size_t>
249  append (const void* const src, ssize_t const size,
250  bool const store = true, bool const new_record = true)
251  {
252  assert (src);
253  assert (size);
254 
255  BufWrap bw (src, size);
256  return append_base<BufWrap, true> (bw, store, new_record);
257 // return append_base (src, size, store); - old append_base() method
258  }
259 
260 private:
261 
264  class BufWrap
265  {
266  const byte_t* const ptr_;
267  size_t const size_;
268 
269  public:
270 
271  BufWrap (const void* const ptr, size_t const size)
272  : ptr_(reinterpret_cast<const byte_t*>(ptr)), size_(size)
273  {}
274 
275  size_t serial_size() const { return size_; }
276  const byte_t* ptr() const { return ptr_; }
277 
278  size_t serialize_to (byte_t* const dst, size_t) const
279  {
280  ::memcpy (dst, ptr_, size_);
281  return size_;
282  }
283  };
284 
285  RecordSetOut (const RecordSetOut&);
286  RecordSetOut& operator = (const RecordSetOut&);
287 };
288 
289 
292 {
293 public:
294 
295  RecordSetInBase (const byte_t* buf,/* pointer to the beginning of buffer */
296  size_t size, /* total size of buffer */
297  bool check_now = true); /* checksum now */
298 
299  /* this is a "delayed constructor", for the object created empty */
300  void init (const byte_t* buf, /* pointer to the beginning of buffer */
301  size_t size, /* total size of buffer */
302  bool check_now = true); /* checksum now */
303 
304 
305  void rewind() const { next_ = begin_; }
306 
307  void checksum() const; // throws if checksum fails
308 
309  gu::Buf buf() const
310  {
311  gu::Buf ret = { head_, size_ }; return ret;
312  }
313 
314 protected:
315 
316  template <class R>
317  void next_base (Buf& n) const
318  {
319  if (gu_likely (next_ < size_))
320  {
321  size_t const next_size(R::serial_size(head_ + next_, size_ -next_));
322 
323  /* sanity check */
324  if (gu_likely (next_ + next_size <= size_t(size_)))
325  {
326  n.ptr = head_ + next_;
327  n.size = next_size;
328  next_ += next_size;
329  return;
330  }
331 
332  throw_error (E_FAULT);
333  }
334 
335  assert (next_ == size_);
336 
337  throw_error (E_PERM);
338  }
339 
340  template <class R>
341  R next_base () const
342  {
343  if (gu_likely (next_ < size_))
344  {
345  R const rec(head_ + next_, size_ - next_);
346  size_t const tmp_size(rec.serial_size());
347 
348  /* sanity check */
349  if (gu_likely (next_ + tmp_size <= size_t(size_)))
350  {
351  next_ += tmp_size;
352  return rec;
353  }
354 
355  throw_error (E_FAULT);
356  }
357 
358  assert (next_ == size_);
359 
360  throw_error (E_PERM);
361  }
362 
363 private:
364 
365  const byte_t* head_; /* pointer to header */
366  ssize_t mutable next_; /* offset to next record */
367  short begin_; /* offset to first record */
368  /* size_ from parent class is offset past all records */
369 
370  /* takes total size of the supplied buffer */
371  void parse_header_v1 (size_t size);
372 
373  enum Error
374  {
375  E_PERM,
376  E_FAULT
377  };
378 
379  GU_NORETURN void throw_error (Error code) const;
380 
381  /* shallow copies here - we're not allocating anything */
383  :
384  RecordSet (r),
385  head_ (r.head_),
386  next_ (r.next_),
387  begin_ (r.begin_)
388  {}
389 
390  RecordSetInBase& operator= (const RecordSetInBase r);
391 #if 0
392  {
393  std::swap(head_, r.head_);
394  std::swap(next_, r.next_);
395  std::swap(begin, r.begin_);
396  }
397 #endif
398 }; /* class RecordSetInBase */
399 
400 
403 template <class R>
405 {
406 public:
407 
408  RecordSetIn (const void* buf,/* pointer to the beginning of buffer */
409  size_t size, /* total size of buffer */
410  bool check_first = true) /* checksum now */
411  :
412  RecordSetInBase (reinterpret_cast<const byte_t*>(buf),
413  size, check_first)
414  {}
415 
416  RecordSetIn () : RecordSetInBase (NULL, 0, false) {}
417 
418  void next (Buf& n) const { next_base<R> (n); }
419 
420  R next () const { return next_base<R> (); }
421 
422 }; /* class RecordSetIn */
423 
424 #if defined(__GNUG__)
425 # if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4)
426 # pragma GCC diagnostic pop
427 # endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || (__GNUC__ > 4)
428 #endif
429 
430 } /* namespace gu */
431 
432 #endif /* _GU_RSET_HPP_ */
Definition: gu_buf.h:17
byte_t * alloc(page_size_type const size, bool &new_page)
ssize_t gather(GatherVector &out)
Definition: gu_vector.hpp:61
ssize_t page_count() const
Definition: gu_rset.hpp:92
Definition: gu_rset.hpp:404
Definition: gu_rset.hpp:28
size_t size() const
Definition: gu_rset.hpp:49
Definition: gu_rset.hpp:114
Definition: gu_rset.hpp:217
int count() const
Definition: gu_rset.hpp:52
Definition: gu_rset.hpp:291
Definition: gu_rset.hpp:85
Definition: gu_alloc.hpp:31