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>'].