You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2017/03/11 01:24:18 UTC
[trafficserver] branch master updated: Scalar: Add Scalar (scaled
value) support class.
This is an automated email from the ASF dual-hosted git repository.
amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new d461019 Scalar: Add Scalar (scaled value) support class.
d461019 is described below
commit d4610190652702b4544d26b7fb5ad081bb19f697
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Thu Dec 29 15:39:21 2016 -0600
Scalar: Add Scalar (scaled value) support class.
---
lib/ts/Makefile.am | 4 +-
lib/ts/Scalar.h | 1178 +++++++++++++++++++++++++++++++++++++++++++++++++
lib/ts/test_Scalar.cc | 348 +++++++++++++++
3 files changed, 1529 insertions(+), 1 deletion(-)
diff --git a/lib/ts/Makefile.am b/lib/ts/Makefile.am
index ba0a952..ac7d2ba 100644
--- a/lib/ts/Makefile.am
+++ b/lib/ts/Makefile.am
@@ -23,7 +23,7 @@ library_includedir=$(includedir)/ts
library_include_HEADERS = apidefs.h
noinst_PROGRAMS = mkdfa CompileParseRules
-check_PROGRAMS = test_tsutil test_arena test_atomic test_freelist test_geometry test_List test_Map test_Vec test_X509HostnameValidator test_MemView
+check_PROGRAMS = test_tsutil test_arena test_atomic test_freelist test_geometry test_List test_Map test_Vec test_X509HostnameValidator test_MemView test_Scalar
TESTS_ENVIRONMENT = LSAN_OPTIONS=suppressions=suppression.txt
@@ -240,6 +240,8 @@ test_tsutil_SOURCES = \
test_MemView_SOURCES = test_MemView.cc
test_MemView_LDADD = libtsutil.la
+test_Scalar_SOURCES = test_Scalar.cc Scalar.h
+
CompileParseRules_SOURCES = CompileParseRules.cc
clean-local:
diff --git a/lib/ts/Scalar.h b/lib/ts/Scalar.h
new file mode 100644
index 0000000..ad72b9d
--- /dev/null
+++ b/lib/ts/Scalar.h
@@ -0,0 +1,1178 @@
+/** @file
+
+ Scaled integral values.
+
+ In many situations it is desirable to define scaling factors or base units (a "metric"). This template
+ enables this to be done in a type and scaling safe manner where the defined factors carry their scaling
+ information as part of the type.
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#if !defined(TS_SCALAR_H)
+#define TS_SCALAR_H
+
+#include <cstdint>
+#include <ratio>
+#include <ostream>
+#include <type_traits>
+
+namespace tag
+{
+struct generic;
+}
+
+namespace ApacheTrafficServer
+{
+template <intmax_t N, typename C, typename T> class Scalar;
+
+namespace detail
+{
+ // @internal - althought these conversion methods look bulky, in practice they compile down to
+ // very small amounts of code due to dead code elimination and that all of the conditions are
+ // compile time constants.
+
+ // The general case where neither N nor S are a multiple of the other seems a bit long but this
+ // minimizes the risk of integer overflow. I need to validate that under -O2 the compiler will
+ // only do 1 division to get both the quotient and remainder for (n/N) and (n%N). In cases where
+ // N,S are powers of 2 I have verified recent GNU compilers will optimize to bit operations.
+
+ /// Convert a count @a c that is scale @s S to scale @c N
+ template <intmax_t N, intmax_t S>
+ intmax_t
+ scale_conversion_round_up(intmax_t c)
+ {
+ typedef std::ratio<N, S> R;
+ if (N == S) {
+ return c;
+ } else if (R::den == 1) {
+ return c / R::num + (0 != c % R::num); // N is a multiple of S.
+ } else if (R::num == 1) {
+ return c * R::den; // S is a multiple of N.
+ } else {
+ return (c / R::num) * R::den + ((c % R::num) * R::den) / R::num + (0 != (c % R::num));
+ }
+ }
+
+ /// Convert a count @a c that is scale @s S to scale @c N
+ template <intmax_t N, intmax_t S>
+ intmax_t
+ scale_conversion_round_down(intmax_t c)
+ {
+ typedef std::ratio<N, S> R;
+ if (N == S) {
+ return c;
+ } else if (R::den == 1) {
+ return c / R::num; // N = k S
+ } else if (R::num == 1) {
+ return c * R::den; // S = k N
+ } else {
+ return (c / R::num) * R::den + ((c % R::num) * R::den) / R::num;
+ }
+ }
+
+ /* Helper classes for @c Scalar
+
+ These wrap values to capture extra information for @c Scalar methods. This includes whether to
+ round up or down when converting and, when the wrapped data is also a @c Scalar, the scale.
+
+ These are not intended for direct use but by the @c round_up and @c round_down free functions
+ which capture the information about the argument and construct an instance of one of these
+ classes to pass it on to a @c Scalar method.
+
+ Scale conversions between @c Scalar instances are handled in these classes via the templated
+ methods @c scale_conversion_round_up and @c scale_conversion_round_down.
+
+ Conversions between scales and types for the scalar helpers is done inside the helper classes
+ and a user type conversion operator exists so the helper can be converted by the compiler to
+ the correct type. For the untis bases conversion this is done in @c Scalar because the
+ generality of the needed conversion is too broad to be easily used. It can be done but there is
+ some ugliness due to the fact that in some cases two user conversions which is difficult to
+ deal with. I have tried it both ways and overall this seems a cleaner implementation.
+
+ Much of this is driven by the fact that the assignment operator, in some case, can not be
+ templated and therefore to have a nice interace for assignment this split is needed.
+ */
+
+ // Unit value, to be rounded up.
+ template <typename C> struct scalar_unit_round_up_t {
+ C _n;
+ // template <typename I> constexpr operator scalar_unit_round_up_t<I>() { return {static_cast<I>(_n)}; }
+ template <intmax_t N, typename I>
+ constexpr I
+ scale() const
+ {
+ return static_cast<I>(_n / N + (0 != (_n % N)));
+ }
+ };
+ // Unit value, to be rounded down.
+ template <typename C> struct scalar_unit_round_down_t {
+ C _n;
+ // template <typename I> operator scalar_unit_round_down_t<I>() { return {static_cast<I>(_n)}; }
+ template <intmax_t N, typename I>
+ constexpr I
+ scale() const
+ {
+ return static_cast<I>(_n / N);
+ }
+ };
+ // Scalar value, to be rounded up.
+ template <intmax_t N, typename C, typename T> struct scalar_round_up_t {
+ C _n;
+ template <intmax_t S, typename I> constexpr operator Scalar<S, I, T>() const
+ {
+ return Scalar<S, I, T>(scale_conversion_round_up<S, N>(_n));
+ }
+ };
+ // Scalar value, to be rounded down.
+ template <intmax_t N, typename C, typename T> struct scalar_round_down_t {
+ C _n;
+ template <intmax_t S, typename I> constexpr operator Scalar<S, I, T>() const
+ {
+ return Scalar<S, I, T>(scale_conversion_round_down<S, N>(_n));
+ }
+ };
+}
+
+/// Mark a unit value to be scaled, rounding down.
+template <typename C>
+constexpr detail::scalar_unit_round_up_t<C>
+round_up(C n)
+{
+ return {n};
+}
+/// Mark a @c Scalar value to be scaled, rounding up.
+template <intmax_t N, typename C, typename T>
+constexpr detail::scalar_round_up_t<N, C, T>
+round_up(Scalar<N, C, T> v)
+{
+ return {v.count()};
+}
+/// Mark a unit value to be scaled, rounding down.
+template <typename C>
+constexpr detail::scalar_unit_round_down_t<C>
+round_down(C n)
+{
+ return {n};
+}
+/// Mark a @c Scalar value, to be rounded down.
+template <intmax_t N, typename C, typename T>
+constexpr detail::scalar_round_down_t<N, C, T>
+round_down(Scalar<N, C, T> v)
+{
+ return {v.count()};
+}
+
+/** A class to hold scaled values.
+
+ Instances of this class have a @a count and a @a scale. The "value" of the instance is @a
+ count * @a scale. The scale is stored in the compiler in the class symbol table and so only
+ the count is a run time value. An instance with a large scale can be assign to an instance
+ with a smaller scale and the conversion is done automatically. Conversions from a smaller to
+ larger scale must be explicit using @c round_up and @c round_down. This prevents
+ inadvertent changes in value. Because the scales are not the same these conversions can be
+ lossy and the two conversions determine whether, in such a case, the result should be rounded
+ up or down to the nearest scale value.
+
+ @a N sets the scale. @a C is the type used to hold the count, which is in units of @a N.
+
+ @a T is a "tag" type which is used only to distinguish the base metric for the scale. Scalar
+ types that have different tags are not interoperable although they can be converted manually by
+ converting to units and then explicitly constructing a new Scalar instance. This is by
+ design. This can be ignored - if not specified then it defaults to a "generic" tag. The type can
+ be (and usually is) defined in name only).
+
+ @note This is modeled somewhat on @c std::chrono and serves a similar function for different
+ and simpler cases (where the ratio is always an integer, never a fraction).
+
+ @see round_up
+ @see round_down
+ */
+template <intmax_t N, typename C = int, typename T = tag::generic> class Scalar
+{
+ typedef Scalar self; ///< Self reference type.
+
+public:
+ /// Scaling factor - make it external accessible.
+ constexpr static intmax_t SCALE = N;
+ typedef C Count; ///< Type used to hold the count.
+ typedef T Tag; ///< Make tag accessible.
+
+ static_assert(N > 0, "The scaling factor (1st template argument) must be a positive integer");
+ static_assert(std::is_integral<C>::value, "The counter type (2nd template argument) must be an integral type");
+
+ constexpr Scalar(); ///< Default contructor.
+ ///< Construct to have @a n scaled units.
+ explicit constexpr Scalar(Count n);
+ /// Copy constructor.
+ constexpr Scalar(self const &that); /// Copy constructor.
+ /// Copy constructor for same scale.
+ template <typename I> constexpr Scalar(Scalar<N, I, T> const &that);
+ /// Direct conversion constructor.
+ /// @note Requires that @c S be an integer multiple of @c SCALE.
+ template <intmax_t S, typename I> constexpr Scalar(Scalar<S, I, T> const &that);
+ /// Conversion constructor.
+ constexpr Scalar(detail::scalar_round_up_t<N, C, T> const &that);
+ /// Conversion constructor.
+ constexpr Scalar(detail::scalar_round_down_t<N, C, T> const &that);
+ /// Conversion constructor.
+ template <typename I> constexpr Scalar(detail::scalar_unit_round_up_t<I> v);
+ /// Conversion constructor.
+ template <typename I> constexpr Scalar(detail::scalar_unit_round_down_t<I> v);
+
+ /// Assignment operator.
+ /// The value @a that is scaled appropriately.
+ /// @note Requires the scale of @a that be an integer multiple of the scale of @a this. If this isn't the case then
+ /// the @c round_up or @c round_down must be used to indicate the rounding direction.
+ template <intmax_t S, typename I> self &operator=(Scalar<S, I, T> const &that);
+ /// Assignment from same scale.
+ self &operator=(self const &that);
+ // Conversion assignments.
+ template <typename I> self &operator=(detail::scalar_unit_round_up_t<I> n);
+ template <typename I> self &operator=(detail::scalar_unit_round_down_t<I> n);
+ self &operator =(detail::scalar_round_up_t<N, C, T> v);
+ self &operator =(detail::scalar_round_down_t<N, C, T> v);
+
+ /// Direct assignment.
+ /// The count is set to @a n.
+ self &assign(Count n);
+ /// The value @a that is scaled appropriately.
+ /// @note Requires the scale of @a that be an integer multiple of the scale of @a this. If this isn't the case then
+ /// the @c round_up or @c round_down must be used to indicate the rounding direction.
+ template <intmax_t S, typename I> self &assign(Scalar<S, I, T> const &that);
+ // Conversion assignments.
+ template <typename I> self &assign(detail::scalar_unit_round_up_t<I> n);
+ template <typename I> self &assign(detail::scalar_unit_round_down_t<I> n);
+ self &assign(detail::scalar_round_up_t<N, C, T> v);
+ self &assign(detail::scalar_round_down_t<N, C, T> v);
+
+ /// The number of scale units.
+ constexpr Count count() const;
+ /// The scaled value.
+ constexpr Count units() const;
+ /// User conversion to scaled value.
+ constexpr operator Count() const;
+
+ /// Addition operator.
+ /// The value is scaled from @a that to @a this.
+ /// @note Requires the scale of @a that be an integer multiple of the scale of @a this. If this isn't the case then
+ /// the @c scale_up or @c scale_down casts must be used to indicate the rounding direction.
+ template <intmax_t S, typename I> self &operator+=(Scalar<S, I, T> const &that);
+ /// Addition - add @a n as a number of scaled units.
+ self &operator+=(C n);
+ /// Addition - add @a n as a number of scaled units.
+ self &operator+=(self const &that);
+ template <typename I> self &operator+=(detail::scalar_unit_round_up_t<I> n);
+ template <typename I> self &operator+=(detail::scalar_unit_round_down_t<I> n);
+ self &operator+=(detail::scalar_round_up_t<N, C, T> v);
+ self &operator+=(detail::scalar_round_down_t<N, C, T> v);
+
+ /// Increment - increase count by 1.
+ self &operator++();
+ /// Increment - increase count by 1.
+ self operator++(int);
+ /// Decrement - decrease count by 1.
+ self &operator--();
+ /// Decrement - decrease count by 1.
+ self operator--(int);
+
+ /// Subtraction operator.
+ /// The value is scaled from @a that to @a this.
+ /// @note Requires the scale of @a that be an integer multiple of the scale of @a this. If this isn't the case then
+ /// the @c scale_up or @c scale_down casts must be used to indicate the rounding direction.
+ template <intmax_t S, typename I> self &operator-=(Scalar<S, I, T> const &that);
+ /// Subtraction - subtract @a n as a number of scaled units.
+ self &operator-=(C n);
+ /// Subtraction - subtract @a n as a number of scaled units.
+ self &operator-=(self const &that);
+
+ template <typename I> self &operator-=(detail::scalar_unit_round_up_t<I> n);
+ template <typename I> self &operator-=(detail::scalar_unit_round_down_t<I> n);
+ self &operator-=(detail::scalar_round_up_t<N, C, T> v);
+ self &operator-=(detail::scalar_round_down_t<N, C, T> v);
+
+ /// Multiplication - multiple the count by @a n.
+ self &operator*=(C n);
+
+ /// Division - divide (rounding down) the count by @a n.
+ self &operator/=(C n);
+
+ /// Run time access to the scale (template arg @a N).
+ static constexpr intmax_t scale();
+
+protected:
+ Count _n; ///< Number of scale units.
+};
+
+template <intmax_t N, typename C, typename T> constexpr Scalar<N, C, T>::Scalar() : _n()
+{
+}
+template <intmax_t N, typename C, typename T> constexpr Scalar<N, C, T>::Scalar(Count n) : _n(n)
+{
+}
+template <intmax_t N, typename C, typename T> constexpr Scalar<N, C, T>::Scalar(self const &that) : _n(that._n)
+{
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+constexpr Scalar<N, C, T>::Scalar(Scalar<N, I, T> const &that) : _n(static_cast<C>(that.count()))
+{
+}
+template <intmax_t N, typename C, typename T>
+template <intmax_t S, typename I>
+constexpr Scalar<N, C, T>::Scalar(Scalar<S, I, T> const &that) : _n(std::ratio<S, N>::num * that.count())
+{
+ static_assert(std::ratio<S, N>::den == 1,
+ "Construction not permitted - target scale is not an integral multiple of source scale.");
+}
+template <intmax_t N, typename C, typename T>
+constexpr Scalar<N, C, T>::Scalar(detail::scalar_round_up_t<N, C, T> const &v) : _n(v._n)
+{
+}
+template <intmax_t N, typename C, typename T>
+constexpr Scalar<N, C, T>::Scalar(detail::scalar_round_down_t<N, C, T> const &v) : _n(v._n)
+{
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+constexpr Scalar<N, C, T>::Scalar(detail::scalar_unit_round_up_t<I> v) : _n(v.template scale<N, C>())
+{
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+constexpr Scalar<N, C, T>::Scalar(detail::scalar_unit_round_down_t<I> v) : _n(v.template scale<N, C>())
+{
+}
+
+template <intmax_t N, typename C, typename T>
+constexpr auto
+Scalar<N, C, T>::count() const -> Count
+{
+ return _n;
+}
+template <intmax_t N, typename C, typename T>
+constexpr auto
+Scalar<N, C, T>::units() const -> Count
+{
+ return _n * SCALE;
+}
+template <intmax_t N, typename C, typename T> constexpr Scalar<N, C, T>::operator C() const
+{
+ return _n * SCALE;
+}
+
+template <intmax_t N, typename C, typename T>
+inline auto
+Scalar<N, C, T>::assign(Count n) -> self &
+{
+ _n = n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+inline auto
+Scalar<N, C, T>::operator=(self const &that) -> self &
+{
+ _n = that._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+inline auto
+Scalar<N, C, T>::operator=(detail::scalar_round_up_t<N, C, T> v) -> self &
+{
+ _n = v._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+inline auto
+Scalar<N, C, T>::assign(detail::scalar_round_up_t<N, C, T> v) -> self &
+{
+ _n = v._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+inline auto
+Scalar<N, C, T>::operator=(detail::scalar_round_down_t<N, C, T> v) -> self &
+{
+ _n = v._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+inline auto
+Scalar<N, C, T>::assign(detail::scalar_round_down_t<N, C, T> v) -> self &
+{
+ _n = v._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+inline auto
+Scalar<N, C, T>::operator=(detail::scalar_unit_round_up_t<I> v) -> self &
+{
+ _n = v.template scale<N, C>();
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+inline auto
+Scalar<N, C, T>::assign(detail::scalar_unit_round_up_t<I> v) -> self &
+{
+ _n = v.template scale<N, C>();
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+inline auto
+Scalar<N, C, T>::operator=(detail::scalar_unit_round_down_t<I> v) -> self &
+{
+ _n = v.template scale<N, C>();
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+inline auto
+Scalar<N, C, T>::assign(detail::scalar_unit_round_down_t<I> v) -> self &
+{
+ _n = v.template scale<N, C>();
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <intmax_t S, typename I>
+auto
+Scalar<N, C, T>::operator=(Scalar<S, I, T> const &that) -> self &
+{
+ typedef std::ratio<S, N> R;
+ static_assert(R::den == 1, "Assignment not permitted - target scale is not an integral multiple of source scale.");
+ _n = that.count() * R::num;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <intmax_t S, typename I>
+auto
+Scalar<N, C, T>::assign(Scalar<S, I, T> const &that) -> self &
+{
+ typedef std::ratio<S, N> R;
+ static_assert(R::den == 1, "Assignment not permitted - target scale is not an integral multiple of source scale.");
+ _n = that.count() * R::num;
+ return *this;
+}
+
+template <intmax_t N, typename C, typename T>
+constexpr inline intmax_t
+Scalar<N, C, T>::scale()
+{
+ return SCALE;
+}
+
+// --- Compare operators
+// These optimize nicely due to dead code elimination.
+
+template <intmax_t N, typename C1, intmax_t S, typename I, typename T>
+bool
+operator<(Scalar<N, C1, T> const &lhs, Scalar<S, I, T> const &rhs)
+{
+ typedef std::ratio<N, S> R;
+ if (N == S)
+ return lhs.count() < rhs.count();
+ else if (R::den == 1)
+ return lhs.count() * R::num < rhs.count();
+ else if (R::num == 1)
+ return lhs.count() < rhs.count() * R::den;
+ else
+ return lhs.units() < rhs.units();
+}
+
+template <intmax_t N, typename C1, intmax_t S, typename I, typename T>
+bool
+operator==(Scalar<N, C1, T> const &lhs, Scalar<S, I, T> const &rhs)
+{
+ typedef std::ratio<N, S> R;
+ if (N == S)
+ return lhs.count() == rhs.count();
+ else if (R::den == 1)
+ return lhs.count() * R::num == rhs.count();
+ else if (R::num == 1)
+ return lhs.count() == rhs.count() * R::den;
+ else
+ return lhs.units() == rhs.units();
+}
+
+template <intmax_t N, typename C1, intmax_t S, typename I, typename T>
+bool
+operator<=(Scalar<N, C1, T> const &lhs, Scalar<S, I, T> const &rhs)
+{
+ typedef std::ratio<N, S> R;
+ if (N == S)
+ return lhs.count() <= rhs.count();
+ else if (R::den == 1)
+ return lhs.count() * R::num <= rhs.count();
+ else if (R::num == 1)
+ return lhs.count() <= rhs.count() * R::den;
+ else
+ return lhs.units() <= rhs.units();
+}
+
+// Derived compares.
+
+template <intmax_t N, typename C1, intmax_t S, typename I, typename T>
+bool
+operator>(Scalar<N, C1, T> const &lhs, Scalar<S, I, T> const &rhs)
+{
+ return rhs < lhs;
+}
+
+template <intmax_t N, typename C1, intmax_t S, typename I, typename T>
+bool
+operator>=(Scalar<N, C1, T> const &lhs, Scalar<S, I, T> const &rhs)
+{
+ return rhs <= lhs;
+}
+
+// Do the integer compares.
+// A bit ugly to handle the issue that integers without explicit type are <int>. Therefore suppport
+// must be provided for comparison not just to the counter type C but also explicitly <int>, otherwise
+// function template argument deduction may fail (because it can't figure out what to use for <C>).
+
+template <intmax_t N, typename C, typename T>
+bool
+operator<(Scalar<N, C, T> const &lhs, C n)
+{
+ return lhs.count() < n;
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator<(C n, Scalar<N, C, T> const &rhs)
+{
+ return n < rhs.count();
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator<(Scalar<N, C, T> const &lhs, int n)
+{
+ return lhs.count() < static_cast<C>(n);
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator<(int n, Scalar<N, C, T> const &rhs)
+{
+ return static_cast<C>(n) < rhs.count();
+}
+template <intmax_t N>
+bool
+operator<(Scalar<N, int> const &lhs, int n)
+{
+ return lhs.count() < n;
+}
+template <intmax_t N>
+bool
+operator<(int n, Scalar<N, int> const &rhs)
+{
+ return n < rhs.count();
+}
+
+template <intmax_t N, typename C, typename T>
+bool
+operator==(Scalar<N, C, T> const &lhs, C n)
+{
+ return lhs.count() == n;
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator==(C n, Scalar<N, C, T> const &rhs)
+{
+ return n == rhs.count();
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator==(Scalar<N, C, T> const &lhs, int n)
+{
+ return lhs.count() == static_cast<C>(n);
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator==(int n, Scalar<N, C, T> const &rhs)
+{
+ return static_cast<C>(n) == rhs.count();
+}
+template <intmax_t N>
+bool
+operator==(Scalar<N, int> const &lhs, int n)
+{
+ return lhs.count() == n;
+}
+template <intmax_t N>
+bool
+operator==(int n, Scalar<N, int> const &rhs)
+{
+ return n == rhs.count();
+}
+
+template <intmax_t N, typename C, typename T>
+bool
+operator>(Scalar<N, C, T> const &lhs, C n)
+{
+ return lhs.count() > n;
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator>(C n, Scalar<N, C, T> const &rhs)
+{
+ return n > rhs.count();
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator>(Scalar<N, C, T> const &lhs, int n)
+{
+ return lhs.count() > static_cast<C>(n);
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator>(int n, Scalar<N, C, T> const &rhs)
+{
+ return static_cast<C>(n) > rhs.count();
+}
+template <intmax_t N>
+bool
+operator>(Scalar<N, int> const &lhs, int n)
+{
+ return lhs.count() > n;
+}
+template <intmax_t N>
+bool
+operator>(int n, Scalar<N, int> const &rhs)
+{
+ return n > rhs.count();
+}
+
+template <intmax_t N, typename C, typename T>
+bool
+operator<=(Scalar<N, C, T> const &lhs, C n)
+{
+ return lhs.count() <= n;
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator<=(C n, Scalar<N, C, T> const &rhs)
+{
+ return n <= rhs.count();
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator<=(Scalar<N, C, T> const &lhs, int n)
+{
+ return lhs.count() <= static_cast<C>(n);
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator<=(int n, Scalar<N, C, T> const &rhs)
+{
+ return static_cast<C>(n) <= rhs.count();
+}
+template <intmax_t N>
+bool
+operator<=(Scalar<N, int> const &lhs, int n)
+{
+ return lhs.count() <= n;
+}
+template <intmax_t N>
+bool
+operator<=(int n, Scalar<N, int> const &rhs)
+{
+ return n <= rhs.count();
+}
+
+template <intmax_t N, typename C, typename T>
+bool
+operator>=(Scalar<N, C, T> const &lhs, C n)
+{
+ return lhs.count() >= n;
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator>=(C n, Scalar<N, C, T> const &rhs)
+{
+ return n >= rhs.count();
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator>=(Scalar<N, C, T> const &lhs, int n)
+{
+ return lhs.count() >= static_cast<C>(n);
+}
+template <intmax_t N, typename C, typename T>
+bool
+operator>=(int n, Scalar<N, C, T> const &rhs)
+{
+ return static_cast<C>(n) >= rhs.count();
+}
+template <intmax_t N>
+bool
+operator>=(Scalar<N, int> const &lhs, int n)
+{
+ return lhs.count() >= n;
+}
+template <intmax_t N>
+bool
+operator>=(int n, Scalar<N, int> const &rhs)
+{
+ return n >= rhs.count();
+}
+
+// Arithmetic operators
+template <intmax_t N, typename C, typename T>
+template <intmax_t S, typename I>
+auto
+Scalar<N, C, T>::operator+=(Scalar<S, I, T> const &that) -> self &
+{
+ typedef std::ratio<S, N> R;
+ static_assert(R::den == 1, "Addition not permitted - target scale is not an integral multiple of source scale.");
+ _n += that.count() * R::num;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator+=(self const &that) -> self &
+{
+ _n += that._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator+=(C n) -> self &
+{
+ _n += n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+auto
+Scalar<N, C, T>::operator+=(detail::scalar_unit_round_up_t<I> v) -> self &
+{
+ _n += v.template scale<N, C>();
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+auto
+Scalar<N, C, T>::operator+=(detail::scalar_unit_round_down_t<I> v) -> self &
+{
+ _n += v.template scale<N, C>();
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator+=(detail::scalar_round_up_t<N, C, T> v) -> self &
+{
+ _n += v._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator+=(detail::scalar_round_down_t<N, C, T> v) -> self &
+{
+ _n += v._n;
+ return *this;
+}
+
+template <intmax_t N, typename C, intmax_t S, typename I, typename T>
+auto
+operator+(Scalar<N, C, T> lhs, Scalar<S, I, T> const &rhs) -> typename std::common_type<Scalar<N, C, T>, Scalar<S, I, T>>::type
+{
+ return typename std::common_type<Scalar<N, C, T>, Scalar<S, I, T>>::type(lhs) += rhs;
+}
+
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(Scalar<N, C, T> const &lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(lhs) += rhs;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(Scalar<N, C, T> const &lhs, C n)
+{
+ return Scalar<N, C, T>(lhs) += n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(C n, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(rhs) += n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(Scalar<N, C, T> const &lhs, int n)
+{
+ return Scalar<N, C, T>(lhs) += n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(int n, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(rhs) += n;
+}
+template <intmax_t N, typename T>
+Scalar<N, int>
+operator+(Scalar<N, int, T> const &lhs, int n)
+{
+ return Scalar<N, int, T>(lhs) += n;
+}
+template <intmax_t N>
+Scalar<N, int>
+operator+(int n, Scalar<N, int> const &rhs)
+{
+ return Scalar<N, int>(rhs) += n;
+}
+template <intmax_t N, typename C, typename T, typename I>
+Scalar<N, C, T>
+operator+(detail::scalar_unit_round_up_t<I> lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(rhs) += lhs.template scale<N, C>();
+}
+template <intmax_t N, typename C, typename T, typename I>
+Scalar<N, C, T>
+operator+(Scalar<N, C, T> const &lhs, detail::scalar_unit_round_up_t<I> rhs)
+{
+ return Scalar<N, C, T>(lhs) += rhs.template scale<N, C>();
+}
+template <intmax_t N, typename C, typename T, typename I>
+Scalar<N, C, T>
+operator+(detail::scalar_unit_round_down_t<I> lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(rhs) += lhs.template scale<N, C>();
+}
+template <intmax_t N, typename C, typename T, typename I>
+Scalar<N, C, T>
+operator+(Scalar<N, C, T> const &lhs, detail::scalar_unit_round_down_t<I> rhs)
+{
+ return Scalar<N, C, T>(lhs) += rhs.template scale<N, C>();
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(detail::scalar_round_up_t<N, C, T> lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(rhs) += lhs._n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(Scalar<N, C, T> const &lhs, detail::scalar_round_up_t<N, C, T> rhs)
+{
+ return Scalar<N, C, T>(lhs) += rhs._n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(detail::scalar_round_down_t<N, C, T> lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(rhs) += lhs._n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator+(Scalar<N, C, T> const &lhs, detail::scalar_round_down_t<N, C, T> rhs)
+{
+ return Scalar<N, C, T>(lhs) += rhs._n;
+}
+
+template <intmax_t N, typename C, typename T>
+template <intmax_t S, typename I>
+auto
+Scalar<N, C, T>::operator-=(Scalar<S, I, T> const &that) -> self &
+{
+ typedef std::ratio<S, N> R;
+ static_assert(R::den == 1, "Subtraction not permitted - target scale is not an integral multiple of source scale.");
+ _n -= that.count() * R::num;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator-=(self const &that) -> self &
+{
+ _n -= that._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator-=(C n) -> self &
+{
+ _n -= n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+auto
+Scalar<N, C, T>::operator-=(detail::scalar_unit_round_up_t<I> v) -> self &
+{
+ _n -= v.template scale<N, C>();
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+template <typename I>
+auto
+Scalar<N, C, T>::operator-=(detail::scalar_unit_round_down_t<I> v) -> self &
+{
+ _n -= v.template scale<N, C>();
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator-=(detail::scalar_round_up_t<N, C, T> v) -> self &
+{
+ _n -= v._n;
+ return *this;
+}
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator-=(detail::scalar_round_down_t<N, C, T> v) -> self &
+{
+ _n -= v._n;
+ return *this;
+}
+
+template <intmax_t N, typename C, intmax_t S, typename I, typename T>
+auto
+operator-(Scalar<N, C, T> lhs, Scalar<S, I, T> const &rhs) -> typename std::common_type<Scalar<N, C, T>, Scalar<S, I, T>>::type
+{
+ return typename std::common_type<Scalar<N, C, T>, Scalar<S, I, T>>::type(lhs) -= rhs;
+}
+
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(Scalar<N, C, T> const &lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(lhs) -= rhs;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(Scalar<N, C, T> const &lhs, C n)
+{
+ return Scalar<N, C, T>(lhs) -= n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(C n, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(n) -= rhs;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(Scalar<N, C, T> const &lhs, int n)
+{
+ return Scalar<N, C, T>(lhs) -= n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(int n, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(n) -= rhs;
+}
+template <intmax_t N>
+Scalar<N, int>
+operator-(Scalar<N, int> const &lhs, int n)
+{
+ return Scalar<N, int>(lhs) -= n;
+}
+template <intmax_t N>
+Scalar<N, int>
+operator-(int n, Scalar<N, int> const &rhs)
+{
+ return Scalar<N, int>(n) -= rhs;
+}
+template <intmax_t N, typename C, typename T, typename I>
+Scalar<N, C, T>
+operator-(detail::scalar_unit_round_up_t<I> lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(lhs.template scale<N, C>()) -= rhs;
+}
+template <intmax_t N, typename C, typename T, typename I>
+Scalar<N, C, T>
+operator-(Scalar<N, C, T> const &lhs, detail::scalar_unit_round_up_t<I> rhs)
+{
+ return Scalar<N, C, T>(lhs) -= rhs.template scale<N, C>();
+}
+template <intmax_t N, typename C, typename T, typename I>
+Scalar<N, C, T>
+operator-(detail::scalar_unit_round_down_t<I> lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(lhs.template scale<N, C>()) -= rhs;
+}
+template <intmax_t N, typename C, typename T, typename I>
+Scalar<N, C, T>
+operator-(Scalar<N, C, T> const &lhs, detail::scalar_unit_round_down_t<I> rhs)
+{
+ return Scalar<N, C, T>(lhs) -= rhs.template scale<N, C>();
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(detail::scalar_round_up_t<N, C, T> lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(lhs._n) -= rhs;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(Scalar<N, C, T> const &lhs, detail::scalar_round_up_t<N, C, T> rhs)
+{
+ return Scalar<N, C, T>(lhs) -= rhs._n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(detail::scalar_round_down_t<N, C, T> lhs, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(lhs._n) -= rhs;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator-(Scalar<N, C, T> const &lhs, detail::scalar_round_down_t<N, C, T> rhs)
+{
+ return Scalar<N, C, T>(lhs) -= rhs._n;
+}
+
+template <intmax_t N, typename C, typename T> auto Scalar<N, C, T>::operator++() -> self &
+{
+ ++_n;
+ return *this;
+}
+
+template <intmax_t N, typename C, typename T> auto Scalar<N, C, T>::operator++(int) -> self
+{
+ self zret(*this);
+ ++_n;
+ return zret;
+}
+
+template <intmax_t N, typename C, typename T> auto Scalar<N, C, T>::operator--() -> self &
+{
+ --_n;
+ return *this;
+}
+
+template <intmax_t N, typename C, typename T> auto Scalar<N, C, T>::operator--(int) -> self
+{
+ self zret(*this);
+ --_n;
+ return zret;
+}
+
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator*=(C n) -> self &
+{
+ _n *= n;
+ return *this;
+}
+
+template <intmax_t N, typename C, typename T> Scalar<N, C, T> operator*(Scalar<N, C, T> const &lhs, C n)
+{
+ return Scalar<N, C, T>(lhs) *= n;
+}
+template <intmax_t N, typename C, typename T> Scalar<N, C, T> operator*(C n, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(rhs) *= n;
+}
+template <intmax_t N, typename C, typename T> Scalar<N, C, T> operator*(Scalar<N, C, T> const &lhs, int n)
+{
+ return Scalar<N, C, T>(lhs) *= n;
+}
+template <intmax_t N, typename C, typename T> Scalar<N, C, T> operator*(int n, Scalar<N, C, T> const &rhs)
+{
+ return Scalar<N, C, T>(rhs) *= n;
+}
+template <intmax_t N> Scalar<N, int> operator*(Scalar<N, int> const &lhs, int n)
+{
+ return Scalar<N, int>(lhs) *= n;
+}
+template <intmax_t N> Scalar<N, int> operator*(int n, Scalar<N, int> const &rhs)
+{
+ return Scalar<N, int>(rhs) *= n;
+}
+
+template <intmax_t N, typename C, typename T>
+auto
+Scalar<N, C, T>::operator/=(C n) -> self &
+{
+ _n /= n;
+ return *this;
+}
+
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator/(Scalar<N, C, T> const &lhs, C n)
+{
+ return Scalar<N, C, T>(lhs) /= n;
+}
+template <intmax_t N, typename C, typename T>
+Scalar<N, C, T>
+operator/(Scalar<N, C, T> const &lhs, int n)
+{
+ return Scalar<N, C, T>(lhs) /= n;
+}
+template <intmax_t N>
+Scalar<N, int>
+operator/(Scalar<N, int> const &lhs, int n)
+{
+ return Scalar<N, int>(lhs) /= n;
+}
+
+namespace detail
+{
+ // These classes exist only to create distinguishable overloads.
+ struct tag_label_A {
+ };
+ struct tag_label_B : public tag_label_A {
+ };
+ // The purpose is to print a label for a tagged type only if the tag class defines a member that
+ // is the label. This creates a base function that always works and does nothing. The second
+ // function creates an overload if the tag class has a member named 'label' that has an stream IO
+ // output operator. When invoked with a second argument of B then the second overload exists and
+ // is used, otherwise only the first exists and that is used. The critical technology is the use
+ // of 'auto' and 'decltype' which effectively checks if the code inside 'decltype' compiles.
+ template <typename T>
+ inline std::ostream &
+ tag_label(std::ostream &s, tag_label_A const &)
+ {
+ return s;
+ }
+ template <typename T>
+ inline auto
+ tag_label(std::ostream &s, tag_label_B const &) -> decltype(s << T::label, s)
+ {
+ return s << T::label;
+ }
+} // detail
+
+} // namespace
+
+namespace std
+{
+template <intmax_t N, typename C, typename T>
+ostream &
+operator<<(ostream &s, ApacheTrafficServer::Scalar<N, C, T> const &x)
+{
+ static ApacheTrafficServer::detail::tag_label_B b; // Can't be const or the compiler gets upset.
+ s << x.units();
+ return ApacheTrafficServer::detail::tag_label<T>(s, b);
+}
+
+/// Compute common type of two scalars.
+/// In `std` to overload the base definition. This yields a type that has the common type of the
+/// counter type and a scale that is the GCF of the input scales.
+template <intmax_t N, typename C, intmax_t S, typename I, typename T>
+struct common_type<ApacheTrafficServer::Scalar<N, C, T>, ApacheTrafficServer::Scalar<S, I, T>> {
+ typedef std::ratio<N, S> R;
+ typedef ApacheTrafficServer::Scalar<N / R::num, typename common_type<C, I>::type, T> type;
+};
+}
+#endif // TS_SCALAR_H
diff --git a/lib/ts/test_Scalar.cc b/lib/ts/test_Scalar.cc
new file mode 100644
index 0000000..2c17e8a
--- /dev/null
+++ b/lib/ts/test_Scalar.cc
@@ -0,0 +1,348 @@
+/** @file
+
+ Intrusive pointer test.
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include <ts/Scalar.h>
+#include <string>
+#include <stdarg.h>
+#include <iostream>
+
+namespace ts
+{
+using namespace ApacheTrafficServer;
+}
+
+struct TestBox {
+ typedef TestBox self; ///< Self reference type.
+
+ std::string _name;
+
+ static int _count;
+ static int _fail;
+
+ TestBox(char const *name) : _name(name) {}
+ TestBox(std::string const &name) : _name(name) {}
+ bool check(bool result, char const *fmt, ...) __attribute__((format(printf, 3, 4)));
+
+ static void
+ print_summary()
+ {
+ printf("Tests: %d of %d passed - %s\n", (_count - _fail), _count, _fail ? "FAIL" : "SUCCESS");
+ }
+};
+
+int TestBox::_count = 0;
+int TestBox::_fail = 0;
+
+bool
+TestBox::check(bool result, char const *fmt, ...)
+{
+ ++_count;
+
+ if (!result) {
+ static constexpr size_t N = 1 << 16;
+ size_t n = N;
+ size_t x;
+ char *s;
+ char buffer[N]; // just stack, go big.
+
+ s = buffer;
+ x = snprintf(s, n, "%s: ", _name.c_str());
+ n -= x;
+ s += x;
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(s, n, fmt, ap);
+ va_end(ap);
+ printf("%s\n", buffer);
+ ++_fail;
+ }
+ return result;
+}
+
+// Extremely simple test.
+void
+Test_1()
+{
+ constexpr static int SCALE = 4096;
+ typedef ts::Scalar<SCALE> PageSize;
+
+ TestBox test("TS Scalar basic");
+ PageSize pg1(1);
+
+ test.check(pg1.count() == 1, "Count wrong, got %d expected %d", pg1.count(), 1);
+ test.check(pg1.units() == SCALE, "Units wrong, got %d expected %d", pg1.units(), SCALE);
+}
+
+// Test multiples.
+void
+Test_2()
+{
+ constexpr static int SCALE_1 = 8192;
+ constexpr static int SCALE_2 = 512;
+
+ typedef ts::Scalar<SCALE_1> Size_1;
+ typedef ts::Scalar<SCALE_2> Size_2;
+
+ TestBox test("TS Scalar Conversion of scales of multiples");
+ Size_2 sz_a(2);
+ Size_2 sz_b(57);
+ Size_2 sz_c(SCALE_1 / SCALE_2);
+ Size_2 sz_d(29 * SCALE_1 / SCALE_2);
+
+ Size_1 sz = ts::round_up(sz_a);
+ test.check(sz.count() == 1, "[1] Rounding up, got %d expected %d", sz.count(), 1);
+ sz = ts::round_down(sz_a);
+ test.check(sz.count() == 0, "[2] Rounding down: got %d expected %d", sz.count(), 0);
+
+ sz = ts::round_up(sz_b);
+ test.check(sz.count() == 4, "[3] Rounding up, got %d expected %d", sz.count(), 4);
+ sz = ts::round_down(sz_b);
+ test.check(sz.count() == 3, "[4] Rounding down, got %d expected %d", sz.count(), 3);
+
+ sz = ts::round_up(sz_c);
+ test.check(sz.count() == 1, "[5] Rounding up, got %d expected %d", sz.count(), 1);
+ sz = ts::round_down(sz_c);
+ test.check(sz.count() == 1, "[6] Rounding down, got %d expected %d", sz.count(), 1);
+
+ sz = ts::round_up(sz_d);
+ test.check(sz.count() == 29, "[7] Rounding up, got %d expected %d", sz.count(), 29);
+ sz = ts::round_down(sz_d);
+ test.check(sz.count() == 29, "[8] Rounding down, got %d expected %d", sz.count(), 29);
+
+ sz.assign(119);
+ sz_b = sz; // Should be OK because SCALE_1 is an integer multiple of SCALE_2
+ // sz = sz_b; // Should not compile.
+ test.check(sz_b.count() == 119 * (SCALE_1 / SCALE_2), "Integral conversion, got %d expected %d", sz_b.count(),
+ 119 * (SCALE_1 / SCALE_2));
+}
+
+// Test common factor.
+void
+Test_3()
+{
+ constexpr static int SCALE_1 = 30;
+ constexpr static int SCALE_2 = 20;
+
+ typedef ts::Scalar<SCALE_1> Size_1;
+ typedef ts::Scalar<SCALE_2> Size_2;
+
+ TestBox test("TS Scalar common factor conversions");
+ Size_2 sz_a(2);
+ Size_2 sz_b(97);
+
+ Size_1 sz = round_up(sz_a);
+ test.check(sz.count() == 2, "Rounding up, got %d expected %d", sz.count(), 2);
+ sz = round_down(sz_a);
+ test.check(sz.count() == 1, "Rounding down: got %d expected %d", sz.count(), 0);
+
+ sz = ts::round_up(sz_b);
+ test.check(sz.count() == 65, "Rounding up, got %d expected %d", sz.count(), 65);
+ sz = ts::round_down(sz_b);
+ test.check(sz.count() == 64, "Rounding down, got %d expected %d", sz.count(), 64);
+}
+
+void
+Test_4()
+{
+ TestBox test("TS Scalar: relatively prime tests");
+
+ ts::Scalar<9> m_9;
+ ts::Scalar<4> m_4, m_test;
+
+ m_9.assign(95);
+ // m_4 = m_9; // Should fail to compile with static assert.
+ // m_9 = m_4; // Should fail to compile with static assert.
+
+ m_4 = ts::round_up(m_9);
+ test.check(m_4.count() == 214, "Rounding down, got %d expected %d", m_4.count(), 214);
+ m_4 = ts::round_down(m_9);
+ test.check(m_4.count() == 213, "Rounding down, got %d expected %d", m_4.count(), 213);
+
+ m_4.assign(213);
+ m_9 = ts::round_up(m_4);
+ test.check(m_9.count() == 95, "Rounding down, got %d expected %d", m_9.count(), 95);
+ m_9 = ts::round_down(m_4);
+ test.check(m_9.count() == 94, "Rounding down, got %d expected %d", m_9.count(), 94);
+
+ m_test = m_4; // Verify assignment of identical scale values compiles.
+ test.check(m_test.count() == 213, "Assignment got %d expected %d", m_4.count(), 213);
+}
+
+void
+Test_5()
+{
+ TestBox test("TS Scalar: arithmetic operator tests");
+
+ typedef ts::Scalar<1024> KBytes;
+ typedef ts::Scalar<1024, long int> KiBytes;
+ typedef ts::Scalar<1, int64_t> Bytes;
+ typedef ts::Scalar<1024 * KBytes::SCALE> MBytes;
+
+ Bytes bytes(96);
+ KBytes kbytes(2);
+ MBytes mbytes(5);
+
+ Bytes z1 = bytes + 128;
+ test.check(z1.count() == 224, "[1] Addition got %ld expected %d", z1.count(), 224);
+ KBytes z2 = kbytes + 3;
+ test.check(z2.count() == 5, "[2] Addition got %d expected %d", z2.count(), 5);
+ Bytes z3(bytes);
+ z3 += kbytes;
+ test.check(z3.units() == 2048 + 96, "[3] Addition got %ld expected %d", z3.units(), 2048 + 96);
+ MBytes z4 = mbytes;
+ z4 += 5;
+ z2 += z4;
+ test.check(z2.units() == ((10 << 20) + (5 << 10)), "[4] Addition got %d expected %d", z2.units(), (10 << 20) + (2 << 10));
+
+ z1 += 128;
+ test.check(z1.count() == 352, "[5] Addition got %ld expected %d", z1.count(), 352);
+
+ z2.assign(2);
+ z1 = 3 * z2;
+ test.check(z1.count() == 6144, "[6] Addition got %ld expected %d", z1.count(), 6144);
+ z1 *= 5;
+ test.check(z1.count() == 30720, "[7] Addition got %ld expected %d", z1.count(), 30720);
+ z1 /= 3;
+ test.check(z1.count() == 10240, "[8] Addition got %ld expected %d", z1.count(), 10240);
+
+ z2.assign(3148);
+ auto x = z2 + MBytes(1);
+ test.check(x.scale() == z2.scale(), "[9] Common type addition yielded bad scale %ld - expected %ld", x.scale(), z2.scale());
+ test.check(x.count() == 4172, "[10] Common type addition yielded bad count %d - expected %d", x.count(), 4172);
+
+ z2 = ts::round_down(262150);
+ test.check(z2.count() == 256, "[11] Unit scale down assignment bad count %d - expected %d", z2.count(), 256);
+
+ z2 = ts::round_up(262150);
+ test.check(z2.count() == 257, "[12] Unit scale up assignment bad count %d - expected %d", z2.count(), 257);
+
+ KBytes q(ts::round_down(262150));
+ test.check(q.count() == 256, "[13] Unit scale down constructor bad count %d - expected %d", q.count(), 256);
+
+ z2 += ts::round_up(97384);
+ test.check(z2.count() == 353, "[14] Unit scale down += bad count %d - expected %d", z2.count(), 353);
+
+ decltype(z2) a = z2 + ts::round_down(167229);
+ test.check(a.count() == 516, "[15] Unit scale down += bad count %d - expected %d", a.count(), 516);
+
+ KiBytes k(3148);
+ auto kx = k + MBytes(1);
+ test.check(kx.scale() == k.scale(), "[9] Common type addition yielded bad scale %ld - expected %ld", kx.scale(), k.scale());
+ test.check(kx.count() == 4172, "[10] Common type addition yielded bad count %ld - expected %d", kx.count(), 4172);
+
+ k = ts::round_down(262150);
+ test.check(k.count() == 256, "[11] Unit scale down assignment bad count %ld - expected %d", k.count(), 256);
+
+ k = ts::round_up(262150);
+ test.check(k.count() == 257, "[12] Unit scale up assignment bad count %ld - expected %d", k.count(), 257);
+
+ KBytes kq(ts::round_down(262150));
+ test.check(kq.count() == 256, "[13] Unit scale down constructor bad count %d - expected %d", kq.count(), 256);
+
+ k += ts::round_up(97384);
+ test.check(k.count() == 353, "[14] Unit scale down += bad count %ld - expected %d", k.count(), 353);
+
+ decltype(k) ka = k + ts::round_down(167229);
+ test.check(ka.count() == 516, "[15] Unit scale down += bad count %ld - expected %d", ka.count(), 516);
+}
+
+// test comparisons
+void
+Test_6()
+{
+ using ts::Scalar;
+ typedef Scalar<1024, ssize_t> KB;
+ typedef Scalar<KB::SCALE * 1024, ssize_t> MB;
+ typedef Scalar<8 * KB::SCALE, ssize_t> StoreBlocks;
+ typedef Scalar<127 * MB::SCALE, ssize_t> SpanBlocks;
+
+ TestBox test("TS Scalar: comparison operator tests");
+
+ StoreBlocks a(80759700);
+ SpanBlocks b(4968);
+ SpanBlocks delta(1);
+
+ test.check(a < b, "[1] Less than incorrect %ld < %ld", a.units(), b.units());
+ test.check(b < (a + delta), "[2] Less than incorrect %ld < %ld", b.units(), (a + delta).units());
+}
+
+struct KBytes_tag {
+ static std::string const label;
+};
+std::string const KBytes_tag::label(" bytes");
+
+void
+Test_IO()
+{
+ typedef ts::Scalar<1024, long int, KBytes_tag> KBytes;
+ typedef ts::Scalar<1024, int> KiBytes;
+
+ KBytes x(12);
+ KiBytes y(12);
+
+ std::cout << "Testing" << std::endl;
+ std::cout << "x is " << x << std::endl;
+ std::cout << "y is " << y << std::endl;
+}
+
+void
+test_Compile()
+{
+ // These tests aren't normally run, they exist to detect compiler issues.
+
+ typedef ts::Scalar<1024, short> KBytes;
+ typedef ts::Scalar<1024, int> KiBytes;
+ int delta = 10;
+
+ KBytes x(12);
+ KiBytes y(12);
+
+ if (x > 12)
+ std::cout << "Operator > works" << std::endl;
+ if (y > 12)
+ std::cout << "Operator > works" << std::endl;
+
+ (void)(x += 10);
+ (void)(x += static_cast<int>(10));
+ (void)(x += static_cast<long int>(10));
+ (void)(x += delta);
+ (void)(y += 10);
+ (void)(y += static_cast<int>(10));
+ (void)(y += static_cast<long int>(10));
+ (void)(y += delta);
+}
+
+int
+main(int, char **)
+{
+ Test_1();
+ Test_2();
+ Test_3();
+ Test_4();
+ Test_5();
+ Test_6();
+ Test_IO();
+ TestBox::print_summary();
+ return 0;
+}
--
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].