QtBase  v6.3.1
qversionnumber.h
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Copyright (C) 2016 Intel Corporation.
5 ** Copyright (C) 2014 Keith Gardner <kreios4004@gmail.com>
6 ** Contact: https://www.qt.io/licensing/
7 **
8 ** This file is part of the QtCore module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Commercial License Usage
12 ** Licensees holding valid commercial Qt licenses may use this file in
13 ** accordance with the commercial license agreement provided with the
14 ** Software or, alternatively, in accordance with the terms contained in
15 ** a written agreement between you and The Qt Company. For licensing terms
16 ** and conditions see https://www.qt.io/terms-conditions. For further
17 ** information use the contact form at https://www.qt.io/contact-us.
18 **
19 ** GNU Lesser General Public License Usage
20 ** Alternatively, this file may be used under the terms of the GNU Lesser
21 ** General Public License version 3 as published by the Free Software
22 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
23 ** packaging of this file. Please review the following information to
24 ** ensure the GNU Lesser General Public License version 3 requirements
25 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26 **
27 ** GNU General Public License Usage
28 ** Alternatively, this file may be used under the terms of the GNU
29 ** General Public License version 2.0 or (at your option) the GNU General
30 ** Public license version 3 or any later version approved by the KDE Free
31 ** Qt Foundation. The licenses are as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33 ** included in the packaging of this file. Please review the following
34 ** information to ensure the GNU General Public License requirements will
35 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36 ** https://www.gnu.org/licenses/gpl-3.0.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #ifndef QVERSIONNUMBER_H
43 #define QVERSIONNUMBER_H
44 
45 #include <QtCore/qlist.h>
46 #include <QtCore/qmetatype.h>
47 #include <QtCore/qnamespace.h>
48 #include <QtCore/qstring.h>
49 #include <QtCore/qtypeinfo.h>
50 #include <limits>
51 
53 
54 class QVersionNumber;
55 Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed = 0);
56 
57 #ifndef QT_NO_DATASTREAM
60 #endif
61 
63 {
64  /*
65  * QVersionNumber stores small values inline, without memory allocation.
66  * We do that by setting the LSB in the pointer that would otherwise hold
67  * the longer form of the segments.
68  * The constants below help us deal with the permutations for 32- and 64-bit,
69  * little- and big-endian architectures.
70  */
71  enum {
72  // in little-endian, inline_segments[0] is shared with the pointer's LSB, while
73  // in big-endian, it's inline_segments[7]
74  InlineSegmentMarker = Q_BYTE_ORDER == Q_LITTLE_ENDIAN ? 0 : sizeof(void *) - 1,
75  InlineSegmentStartIdx = !InlineSegmentMarker, // 0 for BE, 1 for LE
76  InlineSegmentCount = sizeof(void *) - 1
77  };
78  static_assert(InlineSegmentCount >= 3); // at least major, minor, micro
79 
80  struct SegmentStorage
81  {
82  // Note: we alias the use of dummy and inline_segments in the use of the
83  // union below. This is undefined behavior in C++98, but most compilers implement
84  // the C++11 behavior. The one known exception is older versions of Sun Studio.
85  union {
87  qint8 inline_segments[sizeof(void *)];
88  QList<int> *pointer_segments;
89  };
90 
91  // set the InlineSegmentMarker and set length to zero
92  SegmentStorage() noexcept : dummy(1) {}
93 
94  SegmentStorage(const QList<int> &seg)
95  {
96  if (dataFitsInline(seg.data(), seg.size()))
97  setInlineData(seg.data(), seg.size());
98  else
99  pointer_segments = new QList<int>(seg);
100  }
101 
102  SegmentStorage(const SegmentStorage &other)
103  {
104  if (other.isUsingPointer())
105  pointer_segments = new QList<int>(*other.pointer_segments);
106  else
107  dummy = other.dummy;
108  }
109 
110  SegmentStorage &operator=(const SegmentStorage &other)
111  {
112  if (isUsingPointer() && other.isUsingPointer()) {
113  *pointer_segments = *other.pointer_segments;
114  } else if (other.isUsingPointer()) {
115  pointer_segments = new QList<int>(*other.pointer_segments);
116  } else {
117  if (isUsingPointer())
118  delete pointer_segments;
119  dummy = other.dummy;
120  }
121  return *this;
122  }
123 
124  SegmentStorage(SegmentStorage &&other) noexcept
125  : dummy(other.dummy)
126  {
127  other.dummy = 1;
128  }
129 
131 
132  void swap(SegmentStorage &other) noexcept
133  {
134  std::swap(dummy, other.dummy);
135  }
136 
137  explicit SegmentStorage(QList<int> &&seg)
138  {
139  if (dataFitsInline(std::as_const(seg).data(), seg.size()))
140  setInlineData(std::as_const(seg).data(), seg.size());
141  else
142  pointer_segments = new QList<int>(std::move(seg));
143  }
144  SegmentStorage(std::initializer_list<int> args)
145  {
146  if (dataFitsInline(std::data(args), int(args.size()))) {
147  setInlineData(std::data(args), int(args.size()));
148  } else {
149  pointer_segments = new QList<int>(args);
150  }
151  }
152 
153  ~SegmentStorage() { if (isUsingPointer()) delete pointer_segments; }
154 
155  bool isUsingPointer() const noexcept
156  { return (inline_segments[InlineSegmentMarker] & 1) == 0; }
157 
158  int size() const noexcept
159  { return isUsingPointer() ? pointer_segments->size() : (inline_segments[InlineSegmentMarker] >> 1); }
160 
161  void setInlineSize(int len)
162  { inline_segments[InlineSegmentMarker] = qint8(1 + 2 * len); }
163 
164  void resize(int len)
165  {
166  if (isUsingPointer())
167  pointer_segments->resize(len);
168  else
169  setInlineSize(len);
170  }
171 
172  int at(int index) const
173  {
174  return isUsingPointer() ?
175  pointer_segments->at(index) :
176  inline_segments[InlineSegmentStartIdx + index];
177  }
178 
179  void setSegments(int len, int maj, int min = 0, int mic = 0)
180  {
181  if (maj == qint8(maj) && min == qint8(min) && mic == qint8(mic)) {
182  int data[] = { maj, min, mic };
183  setInlineData(data, len);
184  } else {
185  setVector(len, maj, min, mic);
186  }
187  }
188 
189  private:
190  static bool dataFitsInline(const int *data, int len)
191  {
192  if (len > InlineSegmentCount)
193  return false;
194  for (int i = 0; i < len; ++i)
195  if (data[i] != qint8(data[i]))
196  return false;
197  return true;
198  }
199  void setInlineData(const int *data, int len)
200  {
201  dummy = 1 + len * 2;
202 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
203  for (int i = 0; i < len; ++i)
204  dummy |= quintptr(data[i] & 0xFF) << (8 * (i + 1));
205 #elif Q_BYTE_ORDER == Q_BIG_ENDIAN
206  for (int i = 0; i < len; ++i)
207  dummy |= quintptr(data[i] & 0xFF) << (8 * (sizeof(void *) - i - 1));
208 #else
209  // the code above is equivalent to:
210  setInlineSize(len);
211  for (int i = 0; i < len; ++i)
212  inline_segments[InlineSegmentStartIdx + i] = data[i] & 0xFF;
213 #endif
214  }
215 
216  Q_CORE_EXPORT void setVector(int len, int maj, int min, int mic);
217  } m_segments;
218 
219 public:
220  inline QVersionNumber() noexcept
221  : m_segments()
222  {}
223  inline explicit QVersionNumber(const QList<int> &seg) : m_segments(seg) { }
224 
225  // compiler-generated copy/move ctor/assignment operators and the destructor are ok
226 
227  explicit QVersionNumber(QList<int> &&seg) : m_segments(std::move(seg)) { }
228 
229  inline QVersionNumber(std::initializer_list<int> args)
230  : m_segments(args)
231  {}
232 
233  inline explicit QVersionNumber(int maj)
234  { m_segments.setSegments(1, maj); }
235 
236  inline explicit QVersionNumber(int maj, int min)
237  { m_segments.setSegments(2, maj, min); }
238 
239  inline explicit QVersionNumber(int maj, int min, int mic)
240  { m_segments.setSegments(3, maj, min, mic); }
241 
242  [[nodiscard]] inline bool isNull() const noexcept
243  { return segmentCount() == 0; }
244 
245  [[nodiscard]] inline bool isNormalized() const noexcept
246  { return isNull() || segmentAt(segmentCount() - 1) != 0; }
247 
248  [[nodiscard]] inline int majorVersion() const noexcept
249  { return segmentAt(0); }
250 
251  [[nodiscard]] inline int minorVersion() const noexcept
252  { return segmentAt(1); }
253 
254  [[nodiscard]] inline int microVersion() const noexcept
255  { return segmentAt(2); }
256 
257  [[nodiscard]] Q_CORE_EXPORT QVersionNumber normalized() const;
258 
259  [[nodiscard]] Q_CORE_EXPORT QList<int> segments() const;
260 
261  [[nodiscard]] inline int segmentAt(int index) const noexcept
262  { return (m_segments.size() > index) ? m_segments.at(index) : 0; }
263 
264  [[nodiscard]] inline int segmentCount() const noexcept
265  { return m_segments.size(); }
266 
267  [[nodiscard]] Q_CORE_EXPORT bool isPrefixOf(const QVersionNumber &other) const noexcept;
268 
269  [[nodiscard]] Q_CORE_EXPORT static int compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept;
270 
271  [[nodiscard]] Q_CORE_EXPORT static QVersionNumber commonPrefix(const QVersionNumber &v1, const QVersionNumber &v2);
272 
273  [[nodiscard]] Q_CORE_EXPORT QString toString() const;
274 #if QT_STRINGVIEW_LEVEL < 2
275  [[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(const QString &string, int *suffixIndex = nullptr);
276 #endif
277  [[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(QLatin1String string, int *suffixIndex = nullptr);
278  [[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(QStringView string, int *suffixIndex = nullptr);
279 
280  [[nodiscard]] friend bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
281  { return compare(lhs, rhs) > 0; }
282 
283  [[nodiscard]] friend bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
284  { return compare(lhs, rhs) >= 0; }
285 
286  [[nodiscard]] friend bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
287  { return compare(lhs, rhs) < 0; }
288 
289  [[nodiscard]] friend bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
290  { return compare(lhs, rhs) <= 0; }
291 
292  [[nodiscard]] friend bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
293  { return compare(lhs, rhs) == 0; }
294 
295  [[nodiscard]] friend bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
296  { return compare(lhs, rhs) != 0; }
297 
298 
299 private:
300 #ifndef QT_NO_DATASTREAM
301  friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version);
302 #endif
303  friend Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed);
304 };
305 
307 
308 #ifndef QT_NO_DEBUG_STREAM
309 Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version);
310 #endif
311 
312 class QTypeRevision;
313 Q_CORE_EXPORT size_t qHash(const QTypeRevision &key, size_t seed = 0);
314 
315 #ifndef QT_NO_DATASTREAM
316 Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision);
317 Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision);
318 #endif
319 
321 {
322 public:
323  template<typename Integer>
324  using if_valid_segment_type = typename std::enable_if<
326 
327  template<typename Integer>
328  using if_valid_value_type = typename std::enable_if<
330  && (sizeof(Integer) > sizeof(quint16)
331  || (sizeof(Integer) == sizeof(quint16)
333 
334  template<typename Integer, if_valid_segment_type<Integer> = true>
335  static constexpr bool isValidSegment(Integer segment)
336  {
337  // using extra parentheses around max to avoid expanding it if it is a macro
338  return segment >= Integer(0)
339  && ((std::numeric_limits<Integer>::max)() < Integer(SegmentUnknown)
340  || segment < Integer(SegmentUnknown));
341  }
342 
343  template<typename Major, typename Minor,
344  if_valid_segment_type<Major> = true,
345  if_valid_segment_type<Minor> = true>
346  static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
347  {
351  }
352 
353  template<typename Major, if_valid_segment_type<Major> = true>
355  {
357  QTypeRevision(quint8(majorVersion), SegmentUnknown);
358  }
359 
360  template<typename Minor, if_valid_segment_type<Minor> = true>
362  {
364  QTypeRevision(SegmentUnknown, quint8(minorVersion));
365  }
366 
367  template<typename Integer, if_valid_value_type<Integer> = true>
368  static constexpr QTypeRevision fromEncodedVersion(Integer value)
369  {
370  return Q_ASSERT((value & ~Integer(0xffff)) == Integer(0)),
371  QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff));
372  }
373 
374  static constexpr QTypeRevision zero() { return QTypeRevision(0, 0); }
375 
376  constexpr QTypeRevision() = default;
377 
378  constexpr bool hasMajorVersion() const { return m_majorVersion != SegmentUnknown; }
379  constexpr quint8 majorVersion() const { return m_majorVersion; }
380 
381  constexpr bool hasMinorVersion() const { return m_minorVersion != SegmentUnknown; }
382  constexpr quint8 minorVersion() const { return m_minorVersion; }
383 
384  constexpr bool isValid() const { return hasMajorVersion() || hasMinorVersion(); }
385 
386  template<typename Integer, if_valid_value_type<Integer> = true>
387  constexpr Integer toEncodedVersion() const
388  {
389  return Integer(m_majorVersion << 8) | Integer(m_minorVersion);
390  }
391 
392  [[nodiscard]] friend constexpr bool operator==(QTypeRevision lhs, QTypeRevision rhs)
393  {
394  return lhs.toEncodedVersion<quint16>() == rhs.toEncodedVersion<quint16>();
395  }
396 
397  [[nodiscard]] friend constexpr bool operator!=(QTypeRevision lhs, QTypeRevision rhs)
398  {
399  return lhs.toEncodedVersion<quint16>() != rhs.toEncodedVersion<quint16>();
400  }
401 
402  [[nodiscard]] friend constexpr bool operator<(QTypeRevision lhs, QTypeRevision rhs)
403  {
404  return (!lhs.hasMajorVersion() && rhs.hasMajorVersion())
405  // non-0 major > unspecified major > major 0
406  ? rhs.majorVersion() != 0
407  : ((lhs.hasMajorVersion() && !rhs.hasMajorVersion())
408  // major 0 < unspecified major < non-0 major
409  ? lhs.majorVersion() == 0
410  : (lhs.majorVersion() != rhs.majorVersion()
411  // both majors specified and non-0
412  ? lhs.majorVersion() < rhs.majorVersion()
413  : ((!lhs.hasMinorVersion() && rhs.hasMinorVersion())
414  // non-0 minor > unspecified minor > minor 0
415  ? rhs.minorVersion() != 0
416  : ((lhs.hasMinorVersion() && !rhs.hasMinorVersion())
417  // minor 0 < unspecified minor < non-0 minor
418  ? lhs.minorVersion() == 0
419  // both minors specified and non-0
420  : lhs.minorVersion() < rhs.minorVersion()))));
421  }
422 
423  [[nodiscard]] friend constexpr bool operator>(QTypeRevision lhs, QTypeRevision rhs)
424  {
425  return lhs != rhs && !(lhs < rhs);
426  }
427 
428  [[nodiscard]] friend constexpr bool operator<=(QTypeRevision lhs, QTypeRevision rhs)
429  {
430  return lhs == rhs || lhs < rhs;
431  }
432 
433  [[nodiscard]] friend constexpr bool operator>=(QTypeRevision lhs, QTypeRevision rhs)
434  {
435  return lhs == rhs || !(lhs < rhs);
436  }
437 
438 private:
439  enum { SegmentUnknown = 0xff };
440 
441 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
442  constexpr QTypeRevision(quint8 major, quint8 minor)
443  : m_minorVersion(minor), m_majorVersion(major) {}
444 
445  quint8 m_minorVersion = SegmentUnknown;
446  quint8 m_majorVersion = SegmentUnknown;
447 #else
448  constexpr QTypeRevision(quint8 major, quint8 minor)
449  : m_majorVersion(major), m_minorVersion(minor) {}
450 
451  quint8 m_majorVersion = SegmentUnknown;
452  quint8 m_minorVersion = SegmentUnknown;
453 #endif
454 };
455 
456 static_assert(sizeof(QTypeRevision) == 2);
458 
459 #ifndef QT_NO_DEBUG_STREAM
460 Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision);
461 #endif
462 
464 
467 
468 #endif // QVERSIONNUMBER_H
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
#define value
[5]
The QDataStream class provides serialization of binary data to a QIODevice.
Definition: qdatastream.h:66
operator>>(QDataStream &ds, qfloat16 &f)
Definition: qfloat16.cpp:344
operator<<(QDataStream &ds, qfloat16 f)
Definition: qfloat16.cpp:327
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:65
template< typename Enum > size_t qHash(QFlags< Enum > flags, size_t seed=0) noexcept
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
qsizetype size() const noexcept
Definition: qlist.h:414
pointer data()
Definition: qlist.h:442
The QString class provides a Unicode character string.
Definition: qstring.h:388
The QStringView class provides a unified view on UTF-16 strings with a read-only subset of the QStrin...
Definition: qstringview.h:122
The QTypeRevision class contains a lightweight representation of a version number with two 8-bit segm...
constexpr friend bool operator>(QTypeRevision lhs, QTypeRevision rhs)
static constexpr bool isValidSegment(Integer segment)
constexpr friend bool operator>=(QTypeRevision lhs, QTypeRevision rhs)
static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
constexpr friend bool operator!=(QTypeRevision lhs, QTypeRevision rhs)
static constexpr QTypeRevision fromEncodedVersion(Integer value)
constexpr bool hasMinorVersion() const
constexpr friend bool operator<=(QTypeRevision lhs, QTypeRevision rhs)
static constexpr QTypeRevision zero()
typename std::enable_if< std::is_integral< Integer >::value &&(sizeof(Integer) > sizeof(quint16)||(sizeof(Integer)==sizeof(quint16) &&!std::is_signed< Integer >::value)), bool >::type if_valid_value_type
typename std::enable_if< std::is_integral< Integer >::value, bool >::type if_valid_segment_type
static constexpr QTypeRevision fromMinorVersion(Minor minorVersion)
constexpr friend bool operator==(QTypeRevision lhs, QTypeRevision rhs)
constexpr bool hasMajorVersion() const
constexpr Integer toEncodedVersion() const
constexpr quint8 minorVersion() const
static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
constexpr bool isValid() const
constexpr friend bool operator<(QTypeRevision lhs, QTypeRevision rhs)
constexpr quint8 majorVersion() const
constexpr QTypeRevision()=default
The QVersionNumber class contains a version number with an arbitrary number of segments.
QVersionNumber(std::initializer_list< int > args)
Q_CORE_EXPORT bool isPrefixOf(const QVersionNumber &other) const noexcept
friend bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
friend bool operator<(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
friend Q_CORE_EXPORT QDataStream & operator>>(QDataStream &in, QVersionNumber &version)
bool isNull() const noexcept
int minorVersion() const noexcept
static Q_CORE_EXPORT QVersionNumber commonPrefix(const QVersionNumber &v1, const QVersionNumber &v2)
int majorVersion() const noexcept
QVersionNumber(const QList< int > &seg)
QVersionNumber() noexcept
friend bool operator>(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
Q_CORE_EXPORT QList< int > segments() const
friend bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
friend bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
bool isNormalized() const noexcept
static Q_CORE_EXPORT QVersionNumber fromString(const QString &string, int *suffixIndex=nullptr)
Q_CORE_EXPORT QString toString() const
int segmentCount() const noexcept
Q_CORE_EXPORT QVersionNumber normalized() const
int segmentAt(int index) const noexcept
QVersionNumber(int maj, int min, int mic)
QVersionNumber(int maj, int min)
QVersionNumber(int maj)
friend Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed)
int microVersion() const noexcept
friend bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
static Q_CORE_EXPORT int compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept
QVersionNumber(QList< int > &&seg)
a resize(100000)
int const char * version
Definition: zlib.h:814
auto it unsigned count const
Definition: hb-iter.hh:848
Definition: qfloat16.h:381
void swap(SimpleVector< T > &v1, SimpleVector< T > &v2)
Definition: simplevector.h:331
EGLOutputLayerEXT EGLint EGLAttrib value
unsigned short quint16
Definition: qglobal.h:286
size_t quintptr
Definition: qglobal.h:310
QT_BEGIN_NAMESPACE typedef signed char qint8
Definition: qglobal.h:283
unsigned char quint8
Definition: qglobal.h:284
#define QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(Class)
Definition: qglobal.h:563
#define QT_DECL_METATYPE_EXTERN(TYPE, EXPORT)
Definition: qmetatype.h:1285
GLenum type
Definition: qopengl.h:270
GLint GLfloat GLfloat GLfloat v2
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLint GLfloat GLfloat v1
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint segment
Definition: qopenglext.h:9597
GLenum GLsizei len
Definition: qopenglext.h:3292
GLuint in
Definition: qopenglext.h:8870
#define Q_LITTLE_ENDIAN
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
@ Q_RELOCATABLE_TYPE
Definition: qtypeinfo.h:156
Q_DECLARE_TYPEINFO(QVersionNumber, Q_RELOCATABLE_TYPE)
QTextStream out(stdout)
[7]
QSharedPointer< T > other(t)
[5]
this swap(other)
QAction * at
QThreadStorage< int * > dummy[8]
#define rhs