annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/units.h @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
jpayne@69 2 // Licensed under the MIT License:
jpayne@69 3 //
jpayne@69 4 // Permission is hereby granted, free of charge, to any person obtaining a copy
jpayne@69 5 // of this software and associated documentation files (the "Software"), to deal
jpayne@69 6 // in the Software without restriction, including without limitation the rights
jpayne@69 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
jpayne@69 8 // copies of the Software, and to permit persons to whom the Software is
jpayne@69 9 // furnished to do so, subject to the following conditions:
jpayne@69 10 //
jpayne@69 11 // The above copyright notice and this permission notice shall be included in
jpayne@69 12 // all copies or substantial portions of the Software.
jpayne@69 13 //
jpayne@69 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
jpayne@69 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
jpayne@69 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
jpayne@69 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
jpayne@69 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
jpayne@69 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
jpayne@69 20 // THE SOFTWARE.
jpayne@69 21
jpayne@69 22 // This file contains types which are intended to help detect incorrect usage at compile
jpayne@69 23 // time, but should then be optimized down to basic primitives (usually, integers) by the
jpayne@69 24 // compiler.
jpayne@69 25
jpayne@69 26 #pragma once
jpayne@69 27
jpayne@69 28 #include "common.h"
jpayne@69 29 #include <inttypes.h>
jpayne@69 30
jpayne@69 31 KJ_BEGIN_HEADER
jpayne@69 32
jpayne@69 33 namespace kj {
jpayne@69 34
jpayne@69 35 // =======================================================================================
jpayne@69 36 // IDs
jpayne@69 37
jpayne@69 38 template <typename UnderlyingType, typename Label>
jpayne@69 39 struct Id {
jpayne@69 40 // A type-safe numeric ID. `UnderlyingType` is the underlying integer representation. `Label`
jpayne@69 41 // distinguishes this Id from other Id types. Sample usage:
jpayne@69 42 //
jpayne@69 43 // class Foo;
jpayne@69 44 // typedef Id<uint, Foo> FooId;
jpayne@69 45 //
jpayne@69 46 // class Bar;
jpayne@69 47 // typedef Id<uint, Bar> BarId;
jpayne@69 48 //
jpayne@69 49 // You can now use the FooId and BarId types without any possibility of accidentally using a
jpayne@69 50 // FooId when you really wanted a BarId or vice-versa.
jpayne@69 51
jpayne@69 52 UnderlyingType value;
jpayne@69 53
jpayne@69 54 inline constexpr Id(): value(0) {}
jpayne@69 55 inline constexpr explicit Id(int value): value(value) {}
jpayne@69 56
jpayne@69 57 inline constexpr bool operator==(const Id& other) const { return value == other.value; }
jpayne@69 58 inline constexpr bool operator!=(const Id& other) const { return value != other.value; }
jpayne@69 59 inline constexpr bool operator<=(const Id& other) const { return value <= other.value; }
jpayne@69 60 inline constexpr bool operator>=(const Id& other) const { return value >= other.value; }
jpayne@69 61 inline constexpr bool operator< (const Id& other) const { return value < other.value; }
jpayne@69 62 inline constexpr bool operator> (const Id& other) const { return value > other.value; }
jpayne@69 63 };
jpayne@69 64
jpayne@69 65 // =======================================================================================
jpayne@69 66 // Quantity and UnitRatio -- implement unit analysis via the type system
jpayne@69 67
jpayne@69 68 struct Unsafe_ {};
jpayne@69 69 constexpr Unsafe_ unsafe = Unsafe_();
jpayne@69 70 // Use as a parameter to constructors that are unsafe to indicate that you really do mean it.
jpayne@69 71
jpayne@69 72 template <uint64_t maxN, typename T>
jpayne@69 73 class Bounded;
jpayne@69 74 template <uint value>
jpayne@69 75 class BoundedConst;
jpayne@69 76
jpayne@69 77 template <typename T>
jpayne@69 78 struct IsIntegralOrBounded_ { static constexpr bool value = isIntegral<T>(); };
jpayne@69 79 template <uint64_t m, typename T>
jpayne@69 80 struct IsIntegralOrBounded_<Bounded<m, T>> { static constexpr bool value = true; };
jpayne@69 81 template <uint v>
jpayne@69 82 struct IsIntegralOrBounded_<BoundedConst<v>> { static constexpr bool value = true; };
jpayne@69 83
jpayne@69 84 template <typename T>
jpayne@69 85 inline constexpr bool isIntegralOrBounded() { return IsIntegralOrBounded_<T>::value; }
jpayne@69 86
jpayne@69 87 template <typename Number, typename Unit1, typename Unit2>
jpayne@69 88 class UnitRatio {
jpayne@69 89 // A multiplier used to convert Quantities of one unit to Quantities of another unit. See
jpayne@69 90 // Quantity, below.
jpayne@69 91 //
jpayne@69 92 // Construct this type by dividing one Quantity by another of a different unit. Use this type
jpayne@69 93 // by multiplying it by a Quantity, or dividing a Quantity by it.
jpayne@69 94
jpayne@69 95 static_assert(isIntegralOrBounded<Number>(),
jpayne@69 96 "Underlying type for UnitRatio must be integer.");
jpayne@69 97
jpayne@69 98 public:
jpayne@69 99 inline UnitRatio() {}
jpayne@69 100
jpayne@69 101 constexpr UnitRatio(Number unit1PerUnit2, decltype(unsafe)): unit1PerUnit2(unit1PerUnit2) {}
jpayne@69 102 // This constructor was intended to be private, but GCC complains about it being private in a
jpayne@69 103 // bunch of places that don't appear to even call it, so I made it public. Oh well.
jpayne@69 104
jpayne@69 105 template <typename OtherNumber>
jpayne@69 106 inline constexpr UnitRatio(const UnitRatio<OtherNumber, Unit1, Unit2>& other)
jpayne@69 107 : unit1PerUnit2(other.unit1PerUnit2) {}
jpayne@69 108
jpayne@69 109 template <typename OtherNumber>
jpayne@69 110 inline constexpr UnitRatio<decltype(Number()+OtherNumber()), Unit1, Unit2>
jpayne@69 111 operator+(UnitRatio<OtherNumber, Unit1, Unit2> other) const {
jpayne@69 112 return UnitRatio<decltype(Number()+OtherNumber()), Unit1, Unit2>(
jpayne@69 113 unit1PerUnit2 + other.unit1PerUnit2, unsafe);
jpayne@69 114 }
jpayne@69 115 template <typename OtherNumber>
jpayne@69 116 inline constexpr UnitRatio<decltype(Number()-OtherNumber()), Unit1, Unit2>
jpayne@69 117 operator-(UnitRatio<OtherNumber, Unit1, Unit2> other) const {
jpayne@69 118 return UnitRatio<decltype(Number()-OtherNumber()), Unit1, Unit2>(
jpayne@69 119 unit1PerUnit2 - other.unit1PerUnit2, unsafe);
jpayne@69 120 }
jpayne@69 121
jpayne@69 122 template <typename OtherNumber, typename Unit3>
jpayne@69 123 inline constexpr UnitRatio<decltype(Number()*OtherNumber()), Unit3, Unit2>
jpayne@69 124 operator*(UnitRatio<OtherNumber, Unit3, Unit1> other) const {
jpayne@69 125 // U1 / U2 * U3 / U1 = U3 / U2
jpayne@69 126 return UnitRatio<decltype(Number()*OtherNumber()), Unit3, Unit2>(
jpayne@69 127 unit1PerUnit2 * other.unit1PerUnit2, unsafe);
jpayne@69 128 }
jpayne@69 129 template <typename OtherNumber, typename Unit3>
jpayne@69 130 inline constexpr UnitRatio<decltype(Number()*OtherNumber()), Unit1, Unit3>
jpayne@69 131 operator*(UnitRatio<OtherNumber, Unit2, Unit3> other) const {
jpayne@69 132 // U1 / U2 * U2 / U3 = U1 / U3
jpayne@69 133 return UnitRatio<decltype(Number()*OtherNumber()), Unit1, Unit3>(
jpayne@69 134 unit1PerUnit2 * other.unit1PerUnit2, unsafe);
jpayne@69 135 }
jpayne@69 136
jpayne@69 137 template <typename OtherNumber, typename Unit3>
jpayne@69 138 inline constexpr UnitRatio<decltype(Number()*OtherNumber()), Unit3, Unit2>
jpayne@69 139 operator/(UnitRatio<OtherNumber, Unit1, Unit3> other) const {
jpayne@69 140 // (U1 / U2) / (U1 / U3) = U3 / U2
jpayne@69 141 return UnitRatio<decltype(Number()*OtherNumber()), Unit3, Unit2>(
jpayne@69 142 unit1PerUnit2 / other.unit1PerUnit2, unsafe);
jpayne@69 143 }
jpayne@69 144 template <typename OtherNumber, typename Unit3>
jpayne@69 145 inline constexpr UnitRatio<decltype(Number()*OtherNumber()), Unit1, Unit3>
jpayne@69 146 operator/(UnitRatio<OtherNumber, Unit3, Unit2> other) const {
jpayne@69 147 // (U1 / U2) / (U3 / U2) = U1 / U3
jpayne@69 148 return UnitRatio<decltype(Number()*OtherNumber()), Unit1, Unit3>(
jpayne@69 149 unit1PerUnit2 / other.unit1PerUnit2, unsafe);
jpayne@69 150 }
jpayne@69 151
jpayne@69 152 template <typename OtherNumber>
jpayne@69 153 inline decltype(Number() / OtherNumber())
jpayne@69 154 operator/(UnitRatio<OtherNumber, Unit1, Unit2> other) const {
jpayne@69 155 return unit1PerUnit2 / other.unit1PerUnit2;
jpayne@69 156 }
jpayne@69 157
jpayne@69 158 template <typename OtherNumber>
jpayne@69 159 inline constexpr bool operator==(const UnitRatio<OtherNumber, Unit1, Unit2>& other) const {
jpayne@69 160 return unit1PerUnit2 == other.unit1PerUnit2;
jpayne@69 161 }
jpayne@69 162 template <typename OtherNumber>
jpayne@69 163 inline constexpr bool operator!=(const UnitRatio<OtherNumber, Unit1, Unit2>& other) const {
jpayne@69 164 return unit1PerUnit2 != other.unit1PerUnit2;
jpayne@69 165 }
jpayne@69 166
jpayne@69 167 private:
jpayne@69 168 Number unit1PerUnit2;
jpayne@69 169
jpayne@69 170 template <typename OtherNumber, typename OtherUnit>
jpayne@69 171 friend class Quantity;
jpayne@69 172 template <typename OtherNumber, typename OtherUnit1, typename OtherUnit2>
jpayne@69 173 friend class UnitRatio;
jpayne@69 174
jpayne@69 175 template <typename N1, typename N2, typename U1, typename U2, typename>
jpayne@69 176 friend inline constexpr UnitRatio<decltype(N1() * N2()), U1, U2>
jpayne@69 177 operator*(N1, UnitRatio<N2, U1, U2>);
jpayne@69 178 };
jpayne@69 179
jpayne@69 180 template <typename N1, typename N2, typename U1, typename U2,
jpayne@69 181 typename = EnableIf<isIntegralOrBounded<N1>() && isIntegralOrBounded<N2>()>>
jpayne@69 182 inline constexpr UnitRatio<decltype(N1() * N2()), U1, U2>
jpayne@69 183 operator*(N1 n, UnitRatio<N2, U1, U2> r) {
jpayne@69 184 return UnitRatio<decltype(N1() * N2()), U1, U2>(n * r.unit1PerUnit2, unsafe);
jpayne@69 185 }
jpayne@69 186
jpayne@69 187 template <typename Number, typename Unit>
jpayne@69 188 class Quantity {
jpayne@69 189 // A type-safe numeric quantity, specified in terms of some unit. Two Quantities cannot be used
jpayne@69 190 // in arithmetic unless they use the same unit. The `Unit` type parameter is only used to prevent
jpayne@69 191 // accidental mixing of units; this type is never instantiated and can very well be incomplete.
jpayne@69 192 // `Number` is the underlying primitive numeric type.
jpayne@69 193 //
jpayne@69 194 // Quantities support most basic arithmetic operators, intelligently handling units, and
jpayne@69 195 // automatically casting the underlying type in the same way that the compiler would.
jpayne@69 196 //
jpayne@69 197 // To convert a primitive number to a Quantity, multiply it by unit<Quantity<N, U>>().
jpayne@69 198 // To convert a Quantity to a primitive number, divide it by unit<Quantity<N, U>>().
jpayne@69 199 // To convert a Quantity of one unit to another unit, multiply or divide by a UnitRatio.
jpayne@69 200 //
jpayne@69 201 // The Quantity class is not well-suited to hardcore physics as it does not allow multiplying
jpayne@69 202 // one quantity by another. For example, multiplying meters by meters won't get you square
jpayne@69 203 // meters; it will get you a compiler error. It would be interesting to see if template
jpayne@69 204 // metaprogramming could properly deal with such things but this isn't needed for the present
jpayne@69 205 // use case.
jpayne@69 206 //
jpayne@69 207 // Sample usage:
jpayne@69 208 //
jpayne@69 209 // class SecondsLabel;
jpayne@69 210 // typedef Quantity<double, SecondsLabel> Seconds;
jpayne@69 211 // constexpr Seconds SECONDS = unit<Seconds>();
jpayne@69 212 //
jpayne@69 213 // class MinutesLabel;
jpayne@69 214 // typedef Quantity<double, MinutesLabel> Minutes;
jpayne@69 215 // constexpr Minutes MINUTES = unit<Minutes>();
jpayne@69 216 //
jpayne@69 217 // constexpr UnitRatio<double, SecondsLabel, MinutesLabel> SECONDS_PER_MINUTE =
jpayne@69 218 // 60 * SECONDS / MINUTES;
jpayne@69 219 //
jpayne@69 220 // void waitFor(Seconds seconds) {
jpayne@69 221 // sleep(seconds / SECONDS);
jpayne@69 222 // }
jpayne@69 223 // void waitFor(Minutes minutes) {
jpayne@69 224 // waitFor(minutes * SECONDS_PER_MINUTE);
jpayne@69 225 // }
jpayne@69 226 //
jpayne@69 227 // void waitThreeMinutes() {
jpayne@69 228 // waitFor(3 * MINUTES);
jpayne@69 229 // }
jpayne@69 230
jpayne@69 231 static_assert(isIntegralOrBounded<Number>(),
jpayne@69 232 "Underlying type for Quantity must be integer.");
jpayne@69 233
jpayne@69 234 public:
jpayne@69 235 inline constexpr Quantity() = default;
jpayne@69 236
jpayne@69 237 inline constexpr Quantity(MaxValue_): value(maxValue) {}
jpayne@69 238 inline constexpr Quantity(MinValue_): value(minValue) {}
jpayne@69 239 // Allow initialization from maxValue and minValue.
jpayne@69 240 // TODO(msvc): decltype(maxValue) and decltype(minValue) deduce unknown-type for these function
jpayne@69 241 // parameters, causing the compiler to complain of a duplicate constructor definition, so we
jpayne@69 242 // specify MaxValue_ and MinValue_ types explicitly.
jpayne@69 243
jpayne@69 244 inline constexpr Quantity(Number value, decltype(unsafe)): value(value) {}
jpayne@69 245 // This constructor was intended to be private, but GCC complains about it being private in a
jpayne@69 246 // bunch of places that don't appear to even call it, so I made it public. Oh well.
jpayne@69 247
jpayne@69 248 template <typename OtherNumber>
jpayne@69 249 inline constexpr Quantity(const Quantity<OtherNumber, Unit>& other)
jpayne@69 250 : value(other.value) {}
jpayne@69 251
jpayne@69 252 template <typename OtherNumber>
jpayne@69 253 inline Quantity& operator=(const Quantity<OtherNumber, Unit>& other) {
jpayne@69 254 value = other.value;
jpayne@69 255 return *this;
jpayne@69 256 }
jpayne@69 257
jpayne@69 258 template <typename OtherNumber>
jpayne@69 259 inline constexpr Quantity<decltype(Number() + OtherNumber()), Unit>
jpayne@69 260 operator+(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 261 return Quantity<decltype(Number() + OtherNumber()), Unit>(value + other.value, unsafe);
jpayne@69 262 }
jpayne@69 263 template <typename OtherNumber>
jpayne@69 264 inline constexpr Quantity<decltype(Number() - OtherNumber()), Unit>
jpayne@69 265 operator-(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 266 return Quantity<decltype(Number() - OtherNumber()), Unit>(value - other.value, unsafe);
jpayne@69 267 }
jpayne@69 268 template <typename OtherNumber, typename = EnableIf<isIntegralOrBounded<OtherNumber>()>>
jpayne@69 269 inline constexpr Quantity<decltype(Number() * OtherNumber()), Unit>
jpayne@69 270 operator*(OtherNumber other) const {
jpayne@69 271 return Quantity<decltype(Number() * other), Unit>(value * other, unsafe);
jpayne@69 272 }
jpayne@69 273 template <typename OtherNumber, typename = EnableIf<isIntegralOrBounded<OtherNumber>()>>
jpayne@69 274 inline constexpr Quantity<decltype(Number() / OtherNumber()), Unit>
jpayne@69 275 operator/(OtherNumber other) const {
jpayne@69 276 return Quantity<decltype(Number() / other), Unit>(value / other, unsafe);
jpayne@69 277 }
jpayne@69 278 template <typename OtherNumber>
jpayne@69 279 inline constexpr decltype(Number() / OtherNumber())
jpayne@69 280 operator/(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 281 return value / other.value;
jpayne@69 282 }
jpayne@69 283 template <typename OtherNumber>
jpayne@69 284 inline constexpr Quantity<decltype(Number() % OtherNumber()), Unit>
jpayne@69 285 operator%(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 286 return Quantity<decltype(Number() % OtherNumber()), Unit>(value % other.value, unsafe);
jpayne@69 287 }
jpayne@69 288
jpayne@69 289 template <typename OtherNumber, typename OtherUnit>
jpayne@69 290 inline constexpr Quantity<decltype(Number() * OtherNumber()), OtherUnit>
jpayne@69 291 operator*(UnitRatio<OtherNumber, OtherUnit, Unit> ratio) const {
jpayne@69 292 return Quantity<decltype(Number() * OtherNumber()), OtherUnit>(
jpayne@69 293 value * ratio.unit1PerUnit2, unsafe);
jpayne@69 294 }
jpayne@69 295 template <typename OtherNumber, typename OtherUnit>
jpayne@69 296 inline constexpr Quantity<decltype(Number() / OtherNumber()), OtherUnit>
jpayne@69 297 operator/(UnitRatio<OtherNumber, Unit, OtherUnit> ratio) const {
jpayne@69 298 return Quantity<decltype(Number() / OtherNumber()), OtherUnit>(
jpayne@69 299 value / ratio.unit1PerUnit2, unsafe);
jpayne@69 300 }
jpayne@69 301 template <typename OtherNumber, typename OtherUnit>
jpayne@69 302 inline constexpr Quantity<decltype(Number() % OtherNumber()), Unit>
jpayne@69 303 operator%(UnitRatio<OtherNumber, Unit, OtherUnit> ratio) const {
jpayne@69 304 return Quantity<decltype(Number() % OtherNumber()), Unit>(
jpayne@69 305 value % ratio.unit1PerUnit2, unsafe);
jpayne@69 306 }
jpayne@69 307 template <typename OtherNumber, typename OtherUnit>
jpayne@69 308 inline constexpr UnitRatio<decltype(Number() / OtherNumber()), Unit, OtherUnit>
jpayne@69 309 operator/(Quantity<OtherNumber, OtherUnit> other) const {
jpayne@69 310 return UnitRatio<decltype(Number() / OtherNumber()), Unit, OtherUnit>(
jpayne@69 311 value / other.value, unsafe);
jpayne@69 312 }
jpayne@69 313
jpayne@69 314 template <typename OtherNumber>
jpayne@69 315 inline constexpr bool operator==(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 316 return value == other.value;
jpayne@69 317 }
jpayne@69 318 template <typename OtherNumber>
jpayne@69 319 inline constexpr bool operator!=(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 320 return value != other.value;
jpayne@69 321 }
jpayne@69 322 template <typename OtherNumber>
jpayne@69 323 inline constexpr bool operator<=(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 324 return value <= other.value;
jpayne@69 325 }
jpayne@69 326 template <typename OtherNumber>
jpayne@69 327 inline constexpr bool operator>=(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 328 return value >= other.value;
jpayne@69 329 }
jpayne@69 330 template <typename OtherNumber>
jpayne@69 331 inline constexpr bool operator<(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 332 return value < other.value;
jpayne@69 333 }
jpayne@69 334 template <typename OtherNumber>
jpayne@69 335 inline constexpr bool operator>(const Quantity<OtherNumber, Unit>& other) const {
jpayne@69 336 return value > other.value;
jpayne@69 337 }
jpayne@69 338
jpayne@69 339 template <typename OtherNumber>
jpayne@69 340 inline Quantity& operator+=(const Quantity<OtherNumber, Unit>& other) {
jpayne@69 341 value += other.value;
jpayne@69 342 return *this;
jpayne@69 343 }
jpayne@69 344 template <typename OtherNumber>
jpayne@69 345 inline Quantity& operator-=(const Quantity<OtherNumber, Unit>& other) {
jpayne@69 346 value -= other.value;
jpayne@69 347 return *this;
jpayne@69 348 }
jpayne@69 349 template <typename OtherNumber>
jpayne@69 350 inline Quantity& operator*=(OtherNumber other) {
jpayne@69 351 value *= other;
jpayne@69 352 return *this;
jpayne@69 353 }
jpayne@69 354 template <typename OtherNumber>
jpayne@69 355 inline Quantity& operator/=(OtherNumber other) {
jpayne@69 356 value /= other.value;
jpayne@69 357 return *this;
jpayne@69 358 }
jpayne@69 359
jpayne@69 360 private:
jpayne@69 361 Number value;
jpayne@69 362
jpayne@69 363 template <typename OtherNumber, typename OtherUnit>
jpayne@69 364 friend class Quantity;
jpayne@69 365
jpayne@69 366 template <typename Number1, typename Number2, typename Unit2, typename>
jpayne@69 367 friend inline constexpr auto operator*(Number1 a, Quantity<Number2, Unit2> b)
jpayne@69 368 -> Quantity<decltype(Number1() * Number2()), Unit2>;
jpayne@69 369 };
jpayne@69 370
jpayne@69 371 template <typename T> struct Unit_ {
jpayne@69 372 static inline constexpr T get() { return T(1); }
jpayne@69 373 };
jpayne@69 374 template <typename T, typename U>
jpayne@69 375 struct Unit_<Quantity<T, U>> {
jpayne@69 376 static inline constexpr Quantity<decltype(Unit_<T>::get()), U> get() {
jpayne@69 377 return Quantity<decltype(Unit_<T>::get()), U>(Unit_<T>::get(), unsafe);
jpayne@69 378 }
jpayne@69 379 };
jpayne@69 380
jpayne@69 381 template <typename T>
jpayne@69 382 inline constexpr auto unit() -> decltype(Unit_<T>::get()) { return Unit_<T>::get(); }
jpayne@69 383 // unit<Quantity<T, U>>() returns a Quantity of value 1. It also, intentionally, works on basic
jpayne@69 384 // numeric types.
jpayne@69 385
jpayne@69 386 template <typename Number1, typename Number2, typename Unit,
jpayne@69 387 typename = EnableIf<isIntegralOrBounded<Number1>()>>
jpayne@69 388 inline constexpr auto operator*(Number1 a, Quantity<Number2, Unit> b)
jpayne@69 389 -> Quantity<decltype(Number1() * Number2()), Unit> {
jpayne@69 390 return Quantity<decltype(Number1() * Number2()), Unit>(a * b.value, unsafe);
jpayne@69 391 }
jpayne@69 392
jpayne@69 393 template <typename Number1, typename Number2, typename Unit, typename Unit2>
jpayne@69 394 inline constexpr auto operator*(UnitRatio<Number1, Unit2, Unit> ratio,
jpayne@69 395 Quantity<Number2, Unit> measure)
jpayne@69 396 -> decltype(measure * ratio) {
jpayne@69 397 return measure * ratio;
jpayne@69 398 }
jpayne@69 399
jpayne@69 400 // =======================================================================================
jpayne@69 401 // Absolute measures
jpayne@69 402
jpayne@69 403 template <typename T, typename Label>
jpayne@69 404 class Absolute {
jpayne@69 405 // Wraps some other value -- typically a Quantity -- but represents a value measured based on
jpayne@69 406 // some absolute origin. For example, if `Duration` is a type representing a time duration,
jpayne@69 407 // Absolute<Duration, UnixEpoch> might be a calendar date.
jpayne@69 408 //
jpayne@69 409 // Since Absolute represents measurements relative to some arbitrary origin, the only sensible
jpayne@69 410 // arithmetic to perform on them is addition and subtraction.
jpayne@69 411
jpayne@69 412 // TODO(someday): Do the same automatic expansion of integer width that Quantity does? Doesn't
jpayne@69 413 // matter for our time use case, where we always use 64-bit anyway. Note that fixing this
jpayne@69 414 // would implicitly allow things like multiplying an Absolute by a UnitRatio to change its
jpayne@69 415 // units, which is actually totally logical and kind of neat.
jpayne@69 416
jpayne@69 417 public:
jpayne@69 418 inline constexpr Absolute(MaxValue_): value(maxValue) {}
jpayne@69 419 inline constexpr Absolute(MinValue_): value(minValue) {}
jpayne@69 420 // Allow initialization from maxValue and minValue.
jpayne@69 421 // TODO(msvc): decltype(maxValue) and decltype(minValue) deduce unknown-type for these function
jpayne@69 422 // parameters, causing the compiler to complain of a duplicate constructor definition, so we
jpayne@69 423 // specify MaxValue_ and MinValue_ types explicitly.
jpayne@69 424
jpayne@69 425 inline constexpr Absolute operator+(const T& other) const { return Absolute(value + other); }
jpayne@69 426 inline constexpr Absolute operator-(const T& other) const { return Absolute(value - other); }
jpayne@69 427 inline constexpr T operator-(const Absolute& other) const { return value - other.value; }
jpayne@69 428
jpayne@69 429 inline Absolute& operator+=(const T& other) { value += other; return *this; }
jpayne@69 430 inline Absolute& operator-=(const T& other) { value -= other; return *this; }
jpayne@69 431
jpayne@69 432 inline constexpr bool operator==(const Absolute& other) const { return value == other.value; }
jpayne@69 433 inline constexpr bool operator!=(const Absolute& other) const { return value != other.value; }
jpayne@69 434 inline constexpr bool operator<=(const Absolute& other) const { return value <= other.value; }
jpayne@69 435 inline constexpr bool operator>=(const Absolute& other) const { return value >= other.value; }
jpayne@69 436 inline constexpr bool operator< (const Absolute& other) const { return value < other.value; }
jpayne@69 437 inline constexpr bool operator> (const Absolute& other) const { return value > other.value; }
jpayne@69 438
jpayne@69 439 private:
jpayne@69 440 T value;
jpayne@69 441
jpayne@69 442 explicit constexpr Absolute(T value): value(value) {}
jpayne@69 443
jpayne@69 444 template <typename U>
jpayne@69 445 friend inline constexpr U origin();
jpayne@69 446 };
jpayne@69 447
jpayne@69 448 template <typename T, typename Label>
jpayne@69 449 inline constexpr Absolute<T, Label> operator+(const T& a, const Absolute<T, Label>& b) {
jpayne@69 450 return b + a;
jpayne@69 451 }
jpayne@69 452
jpayne@69 453 template <typename T> struct UnitOf_ { typedef T Type; };
jpayne@69 454 template <typename T, typename Label> struct UnitOf_<Absolute<T, Label>> { typedef T Type; };
jpayne@69 455 template <typename T>
jpayne@69 456 using UnitOf = typename UnitOf_<T>::Type;
jpayne@69 457 // UnitOf<Absolute<T, U>> is T. UnitOf<AnythingElse> is AnythingElse.
jpayne@69 458
jpayne@69 459 template <typename T>
jpayne@69 460 inline constexpr T origin() { return T(0 * unit<UnitOf<T>>()); }
jpayne@69 461 // origin<Absolute<T, U>>() returns an Absolute of value 0. It also, intentionally, works on basic
jpayne@69 462 // numeric types.
jpayne@69 463
jpayne@69 464 // =======================================================================================
jpayne@69 465 // Overflow avoidance
jpayne@69 466
jpayne@69 467 template <uint64_t n, uint accum = 0>
jpayne@69 468 struct BitCount_ {
jpayne@69 469 static constexpr uint value = BitCount_<(n >> 1), accum + 1>::value;
jpayne@69 470 };
jpayne@69 471 template <uint accum>
jpayne@69 472 struct BitCount_<0, accum> {
jpayne@69 473 static constexpr uint value = accum;
jpayne@69 474 };
jpayne@69 475
jpayne@69 476 template <uint64_t n>
jpayne@69 477 inline constexpr uint bitCount() { return BitCount_<n>::value; }
jpayne@69 478 // Number of bits required to represent the number `n`.
jpayne@69 479
jpayne@69 480 template <uint bitCountBitCount> struct AtLeastUInt_ {
jpayne@69 481 static_assert(bitCountBitCount < 7, "don't know how to represent integers over 64 bits");
jpayne@69 482 };
jpayne@69 483 template <> struct AtLeastUInt_<0> { typedef uint8_t Type; };
jpayne@69 484 template <> struct AtLeastUInt_<1> { typedef uint8_t Type; };
jpayne@69 485 template <> struct AtLeastUInt_<2> { typedef uint8_t Type; };
jpayne@69 486 template <> struct AtLeastUInt_<3> { typedef uint8_t Type; };
jpayne@69 487 template <> struct AtLeastUInt_<4> { typedef uint16_t Type; };
jpayne@69 488 template <> struct AtLeastUInt_<5> { typedef uint32_t Type; };
jpayne@69 489 template <> struct AtLeastUInt_<6> { typedef uint64_t Type; };
jpayne@69 490
jpayne@69 491 template <uint bits>
jpayne@69 492 using AtLeastUInt = typename AtLeastUInt_<bitCount<max(bits, 1) - 1>()>::Type;
jpayne@69 493 // AtLeastUInt<n> is an unsigned integer of at least n bits. E.g. AtLeastUInt<12> is uint16_t.
jpayne@69 494
jpayne@69 495 // -------------------------------------------------------------------
jpayne@69 496
jpayne@69 497 template <uint value>
jpayne@69 498 class BoundedConst {
jpayne@69 499 // A constant integer value on which we can do bit size analysis.
jpayne@69 500
jpayne@69 501 public:
jpayne@69 502 BoundedConst() = default;
jpayne@69 503
jpayne@69 504 inline constexpr uint unwrap() const { return value; }
jpayne@69 505
jpayne@69 506 #define OP(op, check) \
jpayne@69 507 template <uint other> \
jpayne@69 508 inline constexpr BoundedConst<(value op other)> \
jpayne@69 509 operator op(BoundedConst<other>) const { \
jpayne@69 510 static_assert(check, "overflow in BoundedConst arithmetic"); \
jpayne@69 511 return BoundedConst<(value op other)>(); \
jpayne@69 512 }
jpayne@69 513 #define COMPARE_OP(op) \
jpayne@69 514 template <uint other> \
jpayne@69 515 inline constexpr bool operator op(BoundedConst<other>) const { \
jpayne@69 516 return value op other; \
jpayne@69 517 }
jpayne@69 518
jpayne@69 519 OP(+, value + other >= value)
jpayne@69 520 OP(-, value - other <= value)
jpayne@69 521 OP(*, value * other / other == value)
jpayne@69 522 OP(/, true) // div by zero already errors out; no other division ever overflows
jpayne@69 523 OP(%, true) // mod by zero already errors out; no other modulus ever overflows
jpayne@69 524 OP(<<, value << other >= value)
jpayne@69 525 OP(>>, true) // right shift can't overflow
jpayne@69 526 OP(&, true) // bitwise ops can't overflow
jpayne@69 527 OP(|, true) // bitwise ops can't overflow
jpayne@69 528
jpayne@69 529 COMPARE_OP(==)
jpayne@69 530 COMPARE_OP(!=)
jpayne@69 531 COMPARE_OP(< )
jpayne@69 532 COMPARE_OP(> )
jpayne@69 533 COMPARE_OP(<=)
jpayne@69 534 COMPARE_OP(>=)
jpayne@69 535 #undef OP
jpayne@69 536 #undef COMPARE_OP
jpayne@69 537 };
jpayne@69 538
jpayne@69 539 template <uint64_t m, typename T>
jpayne@69 540 struct Unit_<Bounded<m, T>> {
jpayne@69 541 static inline constexpr BoundedConst<1> get() { return BoundedConst<1>(); }
jpayne@69 542 };
jpayne@69 543
jpayne@69 544 template <uint value>
jpayne@69 545 struct Unit_<BoundedConst<value>> {
jpayne@69 546 static inline constexpr BoundedConst<1> get() { return BoundedConst<1>(); }
jpayne@69 547 };
jpayne@69 548
jpayne@69 549 template <uint value>
jpayne@69 550 inline constexpr BoundedConst<value> bounded() {
jpayne@69 551 return BoundedConst<value>();
jpayne@69 552 }
jpayne@69 553
jpayne@69 554 template <uint64_t a, uint64_t b>
jpayne@69 555 static constexpr uint64_t boundedAdd() {
jpayne@69 556 static_assert(a + b >= a, "possible overflow detected");
jpayne@69 557 return a + b;
jpayne@69 558 }
jpayne@69 559 template <uint64_t a, uint64_t b>
jpayne@69 560 static constexpr uint64_t boundedSub() {
jpayne@69 561 static_assert(a - b <= a, "possible underflow detected");
jpayne@69 562 return a - b;
jpayne@69 563 }
jpayne@69 564 template <uint64_t a, uint64_t b>
jpayne@69 565 static constexpr uint64_t boundedMul() {
jpayne@69 566 static_assert(a * b / b == a, "possible overflow detected");
jpayne@69 567 return a * b;
jpayne@69 568 }
jpayne@69 569 template <uint64_t a, uint64_t b>
jpayne@69 570 static constexpr uint64_t boundedLShift() {
jpayne@69 571 static_assert(a << b >= a, "possible overflow detected");
jpayne@69 572 return a << b;
jpayne@69 573 }
jpayne@69 574
jpayne@69 575 template <uint a, uint b>
jpayne@69 576 inline constexpr BoundedConst<kj::min(a, b)> min(BoundedConst<a>, BoundedConst<b>) {
jpayne@69 577 return bounded<kj::min(a, b)>();
jpayne@69 578 }
jpayne@69 579 template <uint a, uint b>
jpayne@69 580 inline constexpr BoundedConst<kj::max(a, b)> max(BoundedConst<a>, BoundedConst<b>) {
jpayne@69 581 return bounded<kj::max(a, b)>();
jpayne@69 582 }
jpayne@69 583 // We need to override min() and max() between constants because the ternary operator in the
jpayne@69 584 // default implementation would complain.
jpayne@69 585
jpayne@69 586 // -------------------------------------------------------------------
jpayne@69 587
jpayne@69 588 template <uint64_t maxN, typename T>
jpayne@69 589 class Bounded {
jpayne@69 590 public:
jpayne@69 591 static_assert(maxN <= T(kj::maxValue), "possible overflow detected");
jpayne@69 592
jpayne@69 593 Bounded() = default;
jpayne@69 594
jpayne@69 595 Bounded(const Bounded& other) = default;
jpayne@69 596 template <typename OtherInt, typename = EnableIf<isIntegral<OtherInt>()>>
jpayne@69 597 inline constexpr Bounded(OtherInt value): value(value) {
jpayne@69 598 static_assert(OtherInt(maxValue) <= maxN, "possible overflow detected");
jpayne@69 599 }
jpayne@69 600 template <uint64_t otherMax, typename OtherT>
jpayne@69 601 inline constexpr Bounded(const Bounded<otherMax, OtherT>& other)
jpayne@69 602 : value(other.value) {
jpayne@69 603 static_assert(otherMax <= maxN, "possible overflow detected");
jpayne@69 604 }
jpayne@69 605 template <uint otherValue>
jpayne@69 606 inline constexpr Bounded(BoundedConst<otherValue>)
jpayne@69 607 : value(otherValue) {
jpayne@69 608 static_assert(otherValue <= maxN, "overflow detected");
jpayne@69 609 }
jpayne@69 610
jpayne@69 611 Bounded& operator=(const Bounded& other) = default;
jpayne@69 612 template <typename OtherInt, typename = EnableIf<isIntegral<OtherInt>()>>
jpayne@69 613 Bounded& operator=(OtherInt other) {
jpayne@69 614 static_assert(OtherInt(maxValue) <= maxN, "possible overflow detected");
jpayne@69 615 value = other;
jpayne@69 616 return *this;
jpayne@69 617 }
jpayne@69 618 template <uint64_t otherMax, typename OtherT>
jpayne@69 619 inline Bounded& operator=(const Bounded<otherMax, OtherT>& other) {
jpayne@69 620 static_assert(otherMax <= maxN, "possible overflow detected");
jpayne@69 621 value = other.value;
jpayne@69 622 return *this;
jpayne@69 623 }
jpayne@69 624 template <uint otherValue>
jpayne@69 625 inline Bounded& operator=(BoundedConst<otherValue>) {
jpayne@69 626 static_assert(otherValue <= maxN, "overflow detected");
jpayne@69 627 value = otherValue;
jpayne@69 628 return *this;
jpayne@69 629 }
jpayne@69 630
jpayne@69 631 inline constexpr T unwrap() const { return value; }
jpayne@69 632
jpayne@69 633 #define OP(op, newMax) \
jpayne@69 634 template <uint64_t otherMax, typename otherT> \
jpayne@69 635 inline constexpr Bounded<newMax, decltype(T() op otherT())> \
jpayne@69 636 operator op(const Bounded<otherMax, otherT>& other) const { \
jpayne@69 637 return Bounded<newMax, decltype(T() op otherT())>(value op other.value, unsafe); \
jpayne@69 638 }
jpayne@69 639 #define COMPARE_OP(op) \
jpayne@69 640 template <uint64_t otherMax, typename OtherT> \
jpayne@69 641 inline constexpr bool operator op(const Bounded<otherMax, OtherT>& other) const { \
jpayne@69 642 return value op other.value; \
jpayne@69 643 }
jpayne@69 644
jpayne@69 645 OP(+, (boundedAdd<maxN, otherMax>()))
jpayne@69 646 OP(*, (boundedMul<maxN, otherMax>()))
jpayne@69 647 OP(/, maxN)
jpayne@69 648 OP(%, otherMax - 1)
jpayne@69 649
jpayne@69 650 // operator- is intentionally omitted because we mostly use this with unsigned types, and
jpayne@69 651 // subtraction requires proof that subtrahend is not greater than the minuend.
jpayne@69 652
jpayne@69 653 COMPARE_OP(==)
jpayne@69 654 COMPARE_OP(!=)
jpayne@69 655 COMPARE_OP(< )
jpayne@69 656 COMPARE_OP(> )
jpayne@69 657 COMPARE_OP(<=)
jpayne@69 658 COMPARE_OP(>=)
jpayne@69 659
jpayne@69 660 #undef OP
jpayne@69 661 #undef COMPARE_OP
jpayne@69 662
jpayne@69 663 template <uint64_t newMax, typename ErrorFunc>
jpayne@69 664 inline Bounded<newMax, T> assertMax(ErrorFunc&& func) const {
jpayne@69 665 // Assert that the number is no more than `newMax`. Otherwise, call `func`.
jpayne@69 666 static_assert(newMax < maxN, "this bounded size assertion is redundant");
jpayne@69 667 if (KJ_UNLIKELY(value > newMax)) func();
jpayne@69 668 return Bounded<newMax, T>(value, unsafe);
jpayne@69 669 }
jpayne@69 670
jpayne@69 671 template <uint64_t otherMax, typename OtherT, typename ErrorFunc>
jpayne@69 672 inline Bounded<maxN, decltype(T() - OtherT())> subtractChecked(
jpayne@69 673 const Bounded<otherMax, OtherT>& other, ErrorFunc&& func) const {
jpayne@69 674 // Subtract a number, calling func() if the result would underflow.
jpayne@69 675 if (KJ_UNLIKELY(value < other.value)) func();
jpayne@69 676 return Bounded<maxN, decltype(T() - OtherT())>(value - other.value, unsafe);
jpayne@69 677 }
jpayne@69 678
jpayne@69 679 template <uint otherValue, typename ErrorFunc>
jpayne@69 680 inline Bounded<maxN - otherValue, T> subtractChecked(
jpayne@69 681 BoundedConst<otherValue>, ErrorFunc&& func) const {
jpayne@69 682 // Subtract a number, calling func() if the result would underflow.
jpayne@69 683 static_assert(otherValue <= maxN, "underflow detected");
jpayne@69 684 if (KJ_UNLIKELY(value < otherValue)) func();
jpayne@69 685 return Bounded<maxN - otherValue, T>(value - otherValue, unsafe);
jpayne@69 686 }
jpayne@69 687
jpayne@69 688 template <uint64_t otherMax, typename OtherT>
jpayne@69 689 inline Maybe<Bounded<maxN, decltype(T() - OtherT())>> trySubtract(
jpayne@69 690 const Bounded<otherMax, OtherT>& other) const {
jpayne@69 691 // Subtract a number, calling func() if the result would underflow.
jpayne@69 692 if (value < other.value) {
jpayne@69 693 return nullptr;
jpayne@69 694 } else {
jpayne@69 695 return Bounded<maxN, decltype(T() - OtherT())>(value - other.value, unsafe);
jpayne@69 696 }
jpayne@69 697 }
jpayne@69 698
jpayne@69 699 template <uint otherValue>
jpayne@69 700 inline Maybe<Bounded<maxN - otherValue, T>> trySubtract(BoundedConst<otherValue>) const {
jpayne@69 701 // Subtract a number, calling func() if the result would underflow.
jpayne@69 702 if (value < otherValue) {
jpayne@69 703 return nullptr;
jpayne@69 704 } else {
jpayne@69 705 return Bounded<maxN - otherValue, T>(value - otherValue, unsafe);
jpayne@69 706 }
jpayne@69 707 }
jpayne@69 708
jpayne@69 709 inline constexpr Bounded(T value, decltype(unsafe)): value(value) {}
jpayne@69 710 template <uint64_t otherMax, typename OtherT>
jpayne@69 711 inline constexpr Bounded(Bounded<otherMax, OtherT> value, decltype(unsafe))
jpayne@69 712 : value(value.value) {}
jpayne@69 713 // Mainly for internal use.
jpayne@69 714 //
jpayne@69 715 // Only use these as a last resort, with ample commentary on why you think it's safe.
jpayne@69 716
jpayne@69 717 private:
jpayne@69 718 T value;
jpayne@69 719
jpayne@69 720 template <uint64_t, typename>
jpayne@69 721 friend class Bounded;
jpayne@69 722 };
jpayne@69 723
jpayne@69 724 template <typename Number>
jpayne@69 725 inline constexpr Bounded<Number(kj::maxValue), Number> bounded(Number value) {
jpayne@69 726 return Bounded<Number(kj::maxValue), Number>(value, unsafe);
jpayne@69 727 }
jpayne@69 728
jpayne@69 729 inline constexpr Bounded<1, uint8_t> bounded(bool value) {
jpayne@69 730 return Bounded<1, uint8_t>(value, unsafe);
jpayne@69 731 }
jpayne@69 732
jpayne@69 733 template <uint bits, typename Number>
jpayne@69 734 inline constexpr Bounded<maxValueForBits<bits>(), Number> assumeBits(Number value) {
jpayne@69 735 return Bounded<maxValueForBits<bits>(), Number>(value, unsafe);
jpayne@69 736 }
jpayne@69 737
jpayne@69 738 template <uint bits, uint64_t maxN, typename T>
jpayne@69 739 inline constexpr Bounded<maxValueForBits<bits>(), T> assumeBits(Bounded<maxN, T> value) {
jpayne@69 740 return Bounded<maxValueForBits<bits>(), T>(value, unsafe);
jpayne@69 741 }
jpayne@69 742
jpayne@69 743 template <uint bits, typename Number, typename Unit>
jpayne@69 744 inline constexpr auto assumeBits(Quantity<Number, Unit> value)
jpayne@69 745 -> Quantity<decltype(assumeBits<bits>(value / unit<Quantity<Number, Unit>>())), Unit> {
jpayne@69 746 return Quantity<decltype(assumeBits<bits>(value / unit<Quantity<Number, Unit>>())), Unit>(
jpayne@69 747 assumeBits<bits>(value / unit<Quantity<Number, Unit>>()), unsafe);
jpayne@69 748 }
jpayne@69 749
jpayne@69 750 template <uint64_t maxN, typename Number>
jpayne@69 751 inline constexpr Bounded<maxN, Number> assumeMax(Number value) {
jpayne@69 752 return Bounded<maxN, Number>(value, unsafe);
jpayne@69 753 }
jpayne@69 754
jpayne@69 755 template <uint64_t newMaxN, uint64_t maxN, typename T>
jpayne@69 756 inline constexpr Bounded<newMaxN, T> assumeMax(Bounded<maxN, T> value) {
jpayne@69 757 return Bounded<newMaxN, T>(value, unsafe);
jpayne@69 758 }
jpayne@69 759
jpayne@69 760 template <uint64_t maxN, typename Number, typename Unit>
jpayne@69 761 inline constexpr auto assumeMax(Quantity<Number, Unit> value)
jpayne@69 762 -> Quantity<decltype(assumeMax<maxN>(value / unit<Quantity<Number, Unit>>())), Unit> {
jpayne@69 763 return Quantity<decltype(assumeMax<maxN>(value / unit<Quantity<Number, Unit>>())), Unit>(
jpayne@69 764 assumeMax<maxN>(value / unit<Quantity<Number, Unit>>()), unsafe);
jpayne@69 765 }
jpayne@69 766
jpayne@69 767 template <uint maxN, typename Number>
jpayne@69 768 inline constexpr Bounded<maxN, Number> assumeMax(BoundedConst<maxN>, Number value) {
jpayne@69 769 return assumeMax<maxN>(value);
jpayne@69 770 }
jpayne@69 771
jpayne@69 772 template <uint newMaxN, uint64_t maxN, typename T>
jpayne@69 773 inline constexpr Bounded<newMaxN, T> assumeMax(BoundedConst<maxN>, Bounded<maxN, T> value) {
jpayne@69 774 return assumeMax<maxN>(value);
jpayne@69 775 }
jpayne@69 776
jpayne@69 777 template <uint maxN, typename Number, typename Unit>
jpayne@69 778 inline constexpr auto assumeMax(Quantity<BoundedConst<maxN>, Unit>, Quantity<Number, Unit> value)
jpayne@69 779 -> decltype(assumeMax<maxN>(value)) {
jpayne@69 780 return assumeMax<maxN>(value);
jpayne@69 781 }
jpayne@69 782
jpayne@69 783 template <uint64_t newMax, uint64_t maxN, typename T, typename ErrorFunc>
jpayne@69 784 inline Bounded<newMax, T> assertMax(Bounded<maxN, T> value, ErrorFunc&& errorFunc) {
jpayne@69 785 // Assert that the bounded value is less than or equal to the given maximum, calling errorFunc()
jpayne@69 786 // if not.
jpayne@69 787 static_assert(newMax < maxN, "this bounded size assertion is redundant");
jpayne@69 788 return value.template assertMax<newMax>(kj::fwd<ErrorFunc>(errorFunc));
jpayne@69 789 }
jpayne@69 790
jpayne@69 791 template <uint64_t newMax, uint64_t maxN, typename T, typename Unit, typename ErrorFunc>
jpayne@69 792 inline Quantity<Bounded<newMax, T>, Unit> assertMax(
jpayne@69 793 Quantity<Bounded<maxN, T>, Unit> value, ErrorFunc&& errorFunc) {
jpayne@69 794 // Assert that the bounded value is less than or equal to the given maximum, calling errorFunc()
jpayne@69 795 // if not.
jpayne@69 796 static_assert(newMax < maxN, "this bounded size assertion is redundant");
jpayne@69 797 return (value / unit<decltype(value)>()).template assertMax<newMax>(
jpayne@69 798 kj::fwd<ErrorFunc>(errorFunc)) * unit<decltype(value)>();
jpayne@69 799 }
jpayne@69 800
jpayne@69 801 template <uint newMax, uint64_t maxN, typename T, typename ErrorFunc>
jpayne@69 802 inline Bounded<newMax, T> assertMax(
jpayne@69 803 BoundedConst<newMax>, Bounded<maxN, T> value, ErrorFunc&& errorFunc) {
jpayne@69 804 return assertMax<newMax>(value, kj::mv(errorFunc));
jpayne@69 805 }
jpayne@69 806
jpayne@69 807 template <uint newMax, uint64_t maxN, typename T, typename Unit, typename ErrorFunc>
jpayne@69 808 inline Quantity<Bounded<newMax, T>, Unit> assertMax(
jpayne@69 809 Quantity<BoundedConst<newMax>, Unit>,
jpayne@69 810 Quantity<Bounded<maxN, T>, Unit> value, ErrorFunc&& errorFunc) {
jpayne@69 811 return assertMax<newMax>(value, kj::mv(errorFunc));
jpayne@69 812 }
jpayne@69 813
jpayne@69 814 template <uint64_t newBits, uint64_t maxN, typename T, typename ErrorFunc = ThrowOverflow>
jpayne@69 815 inline Bounded<maxValueForBits<newBits>(), T> assertMaxBits(
jpayne@69 816 Bounded<maxN, T> value, ErrorFunc&& errorFunc = ErrorFunc()) {
jpayne@69 817 // Assert that the bounded value requires no more than the given number of bits, calling
jpayne@69 818 // errorFunc() if not.
jpayne@69 819 return assertMax<maxValueForBits<newBits>()>(value, kj::fwd<ErrorFunc>(errorFunc));
jpayne@69 820 }
jpayne@69 821
jpayne@69 822 template <uint64_t newBits, uint64_t maxN, typename T, typename Unit,
jpayne@69 823 typename ErrorFunc = ThrowOverflow>
jpayne@69 824 inline Quantity<Bounded<maxValueForBits<newBits>(), T>, Unit> assertMaxBits(
jpayne@69 825 Quantity<Bounded<maxN, T>, Unit> value, ErrorFunc&& errorFunc = ErrorFunc()) {
jpayne@69 826 // Assert that the bounded value requires no more than the given number of bits, calling
jpayne@69 827 // errorFunc() if not.
jpayne@69 828 return assertMax<maxValueForBits<newBits>()>(value, kj::fwd<ErrorFunc>(errorFunc));
jpayne@69 829 }
jpayne@69 830
jpayne@69 831 template <typename newT, uint64_t maxN, typename T>
jpayne@69 832 inline constexpr Bounded<maxN, newT> upgradeBound(Bounded<maxN, T> value) {
jpayne@69 833 return value;
jpayne@69 834 }
jpayne@69 835
jpayne@69 836 template <typename newT, uint64_t maxN, typename T, typename Unit>
jpayne@69 837 inline constexpr Quantity<Bounded<maxN, newT>, Unit> upgradeBound(
jpayne@69 838 Quantity<Bounded<maxN, T>, Unit> value) {
jpayne@69 839 return value;
jpayne@69 840 }
jpayne@69 841
jpayne@69 842 template <uint64_t maxN, typename T, typename Other, typename ErrorFunc>
jpayne@69 843 inline auto subtractChecked(Bounded<maxN, T> value, Other other, ErrorFunc&& errorFunc)
jpayne@69 844 -> decltype(value.subtractChecked(other, kj::fwd<ErrorFunc>(errorFunc))) {
jpayne@69 845 return value.subtractChecked(other, kj::fwd<ErrorFunc>(errorFunc));
jpayne@69 846 }
jpayne@69 847
jpayne@69 848 template <typename T, typename U, typename Unit, typename ErrorFunc>
jpayne@69 849 inline auto subtractChecked(Quantity<T, Unit> value, Quantity<U, Unit> other, ErrorFunc&& errorFunc)
jpayne@69 850 -> Quantity<decltype(subtractChecked(T(), U(), kj::fwd<ErrorFunc>(errorFunc))), Unit> {
jpayne@69 851 return subtractChecked(value / unit<Quantity<T, Unit>>(),
jpayne@69 852 other / unit<Quantity<U, Unit>>(),
jpayne@69 853 kj::fwd<ErrorFunc>(errorFunc))
jpayne@69 854 * unit<Quantity<T, Unit>>();
jpayne@69 855 }
jpayne@69 856
jpayne@69 857 template <uint64_t maxN, typename T, typename Other>
jpayne@69 858 inline auto trySubtract(Bounded<maxN, T> value, Other other)
jpayne@69 859 -> decltype(value.trySubtract(other)) {
jpayne@69 860 return value.trySubtract(other);
jpayne@69 861 }
jpayne@69 862
jpayne@69 863 template <typename T, typename U, typename Unit>
jpayne@69 864 inline auto trySubtract(Quantity<T, Unit> value, Quantity<U, Unit> other)
jpayne@69 865 -> Maybe<Quantity<decltype(subtractChecked(T(), U(), int())), Unit>> {
jpayne@69 866 return trySubtract(value / unit<Quantity<T, Unit>>(),
jpayne@69 867 other / unit<Quantity<U, Unit>>())
jpayne@69 868 .map([](decltype(subtractChecked(T(), U(), int())) x) {
jpayne@69 869 return x * unit<Quantity<T, Unit>>();
jpayne@69 870 });
jpayne@69 871 }
jpayne@69 872
jpayne@69 873 template <uint64_t aN, uint64_t bN, typename A, typename B>
jpayne@69 874 inline constexpr Bounded<kj::min(aN, bN), WiderType<A, B>>
jpayne@69 875 min(Bounded<aN, A> a, Bounded<bN, B> b) {
jpayne@69 876 return Bounded<kj::min(aN, bN), WiderType<A, B>>(kj::min(a.unwrap(), b.unwrap()), unsafe);
jpayne@69 877 }
jpayne@69 878 template <uint64_t aN, uint64_t bN, typename A, typename B>
jpayne@69 879 inline constexpr Bounded<kj::max(aN, bN), WiderType<A, B>>
jpayne@69 880 max(Bounded<aN, A> a, Bounded<bN, B> b) {
jpayne@69 881 return Bounded<kj::max(aN, bN), WiderType<A, B>>(kj::max(a.unwrap(), b.unwrap()), unsafe);
jpayne@69 882 }
jpayne@69 883 // We need to override min() and max() because:
jpayne@69 884 // 1) WiderType<> might not choose the correct bounds.
jpayne@69 885 // 2) One of the two sides of the ternary operator in the default implementation would fail to
jpayne@69 886 // typecheck even though it is OK in practice.
jpayne@69 887
jpayne@69 888 // -------------------------------------------------------------------
jpayne@69 889 // Operators between Bounded and BoundedConst
jpayne@69 890
jpayne@69 891 #define OP(op, newMax) \
jpayne@69 892 template <uint64_t maxN, uint cvalue, typename T> \
jpayne@69 893 inline constexpr Bounded<(newMax), decltype(T() op uint())> operator op( \
jpayne@69 894 Bounded<maxN, T> value, BoundedConst<cvalue>) { \
jpayne@69 895 return Bounded<(newMax), decltype(T() op uint())>(value.unwrap() op cvalue, unsafe); \
jpayne@69 896 }
jpayne@69 897
jpayne@69 898 #define REVERSE_OP(op, newMax) \
jpayne@69 899 template <uint64_t maxN, uint cvalue, typename T> \
jpayne@69 900 inline constexpr Bounded<(newMax), decltype(uint() op T())> operator op( \
jpayne@69 901 BoundedConst<cvalue>, Bounded<maxN, T> value) { \
jpayne@69 902 return Bounded<(newMax), decltype(uint() op T())>(cvalue op value.unwrap(), unsafe); \
jpayne@69 903 }
jpayne@69 904
jpayne@69 905 #define COMPARE_OP(op) \
jpayne@69 906 template <uint64_t maxN, uint cvalue, typename T> \
jpayne@69 907 inline constexpr bool operator op(Bounded<maxN, T> value, BoundedConst<cvalue>) { \
jpayne@69 908 return value.unwrap() op cvalue; \
jpayne@69 909 } \
jpayne@69 910 template <uint64_t maxN, uint cvalue, typename T> \
jpayne@69 911 inline constexpr bool operator op(BoundedConst<cvalue>, Bounded<maxN, T> value) { \
jpayne@69 912 return cvalue op value.unwrap(); \
jpayne@69 913 }
jpayne@69 914
jpayne@69 915 OP(+, (boundedAdd<maxN, cvalue>()))
jpayne@69 916 REVERSE_OP(+, (boundedAdd<maxN, cvalue>()))
jpayne@69 917
jpayne@69 918 OP(*, (boundedMul<maxN, cvalue>()))
jpayne@69 919 REVERSE_OP(*, (boundedAdd<maxN, cvalue>()))
jpayne@69 920
jpayne@69 921 OP(/, maxN / cvalue)
jpayne@69 922 REVERSE_OP(/, cvalue) // denominator could be 1
jpayne@69 923
jpayne@69 924 OP(%, cvalue - 1)
jpayne@69 925 REVERSE_OP(%, maxN - 1)
jpayne@69 926
jpayne@69 927 OP(<<, (boundedLShift<maxN, cvalue>()))
jpayne@69 928 REVERSE_OP(<<, (boundedLShift<cvalue, maxN>()))
jpayne@69 929
jpayne@69 930 OP(>>, maxN >> cvalue)
jpayne@69 931 REVERSE_OP(>>, cvalue >> maxN)
jpayne@69 932
jpayne@69 933 OP(&, maxValueForBits<bitCount<maxN>()>() & cvalue)
jpayne@69 934 REVERSE_OP(&, maxValueForBits<bitCount<maxN>()>() & cvalue)
jpayne@69 935
jpayne@69 936 OP(|, maxN | cvalue)
jpayne@69 937 REVERSE_OP(|, maxN | cvalue)
jpayne@69 938
jpayne@69 939 COMPARE_OP(==)
jpayne@69 940 COMPARE_OP(!=)
jpayne@69 941 COMPARE_OP(< )
jpayne@69 942 COMPARE_OP(> )
jpayne@69 943 COMPARE_OP(<=)
jpayne@69 944 COMPARE_OP(>=)
jpayne@69 945
jpayne@69 946 #undef OP
jpayne@69 947 #undef REVERSE_OP
jpayne@69 948 #undef COMPARE_OP
jpayne@69 949
jpayne@69 950 template <uint64_t maxN, uint cvalue, typename T>
jpayne@69 951 inline constexpr Bounded<cvalue, decltype(uint() - T())>
jpayne@69 952 operator-(BoundedConst<cvalue>, Bounded<maxN, T> value) {
jpayne@69 953 // We allow subtraction of a variable from a constant only if the constant is greater than or
jpayne@69 954 // equal to the maximum possible value of the variable. Since the variable could be zero, the
jpayne@69 955 // result can be as large as the constant.
jpayne@69 956 //
jpayne@69 957 // We do not allow subtraction of a constant from a variable because there's never a guarantee it
jpayne@69 958 // won't underflow (unless the constant is zero, which is silly).
jpayne@69 959 static_assert(cvalue >= maxN, "possible underflow detected");
jpayne@69 960 return Bounded<cvalue, decltype(uint() - T())>(cvalue - value.unwrap(), unsafe);
jpayne@69 961 }
jpayne@69 962
jpayne@69 963 template <uint64_t aN, uint b, typename A>
jpayne@69 964 inline constexpr Bounded<kj::min(aN, b), A> min(Bounded<aN, A> a, BoundedConst<b>) {
jpayne@69 965 return Bounded<kj::min(aN, b), A>(kj::min(b, a.unwrap()), unsafe);
jpayne@69 966 }
jpayne@69 967 template <uint64_t aN, uint b, typename A>
jpayne@69 968 inline constexpr Bounded<kj::min(aN, b), A> min(BoundedConst<b>, Bounded<aN, A> a) {
jpayne@69 969 return Bounded<kj::min(aN, b), A>(kj::min(a.unwrap(), b), unsafe);
jpayne@69 970 }
jpayne@69 971 template <uint64_t aN, uint b, typename A>
jpayne@69 972 inline constexpr Bounded<kj::max(aN, b), A> max(Bounded<aN, A> a, BoundedConst<b>) {
jpayne@69 973 return Bounded<kj::max(aN, b), A>(kj::max(b, a.unwrap()), unsafe);
jpayne@69 974 }
jpayne@69 975 template <uint64_t aN, uint b, typename A>
jpayne@69 976 inline constexpr Bounded<kj::max(aN, b), A> max(BoundedConst<b>, Bounded<aN, A> a) {
jpayne@69 977 return Bounded<kj::max(aN, b), A>(kj::max(a.unwrap(), b), unsafe);
jpayne@69 978 }
jpayne@69 979 // We need to override min() between a Bounded and a constant since:
jpayne@69 980 // 1) WiderType<> might choose BoundedConst over a 1-byte Bounded, which is wrong.
jpayne@69 981 // 2) To clamp the bounds of the output type.
jpayne@69 982 // 3) Same ternary operator typechecking issues.
jpayne@69 983
jpayne@69 984 // -------------------------------------------------------------------
jpayne@69 985
jpayne@69 986 template <uint64_t maxN, typename T>
jpayne@69 987 class SafeUnwrapper {
jpayne@69 988 public:
jpayne@69 989 inline explicit constexpr SafeUnwrapper(Bounded<maxN, T> value): value(value.unwrap()) {}
jpayne@69 990
jpayne@69 991 template <typename U, typename = EnableIf<isIntegral<U>()>>
jpayne@69 992 inline constexpr operator U() const {
jpayne@69 993 static_assert(maxN <= U(maxValue), "possible truncation detected");
jpayne@69 994 return value;
jpayne@69 995 }
jpayne@69 996
jpayne@69 997 inline constexpr operator bool() const {
jpayne@69 998 static_assert(maxN <= 1, "possible truncation detected");
jpayne@69 999 return value;
jpayne@69 1000 }
jpayne@69 1001
jpayne@69 1002 private:
jpayne@69 1003 T value;
jpayne@69 1004 };
jpayne@69 1005
jpayne@69 1006 template <uint64_t maxN, typename T>
jpayne@69 1007 inline constexpr SafeUnwrapper<maxN, T> unbound(Bounded<maxN, T> bounded) {
jpayne@69 1008 // Unwraps the bounded value, returning a value that can be implicitly cast to any integer type.
jpayne@69 1009 // If this implicit cast could truncate, a compile-time error will be raised.
jpayne@69 1010 return SafeUnwrapper<maxN, T>(bounded);
jpayne@69 1011 }
jpayne@69 1012
jpayne@69 1013 template <uint64_t value>
jpayne@69 1014 class SafeConstUnwrapper {
jpayne@69 1015 public:
jpayne@69 1016 template <typename T, typename = EnableIf<isIntegral<T>()>>
jpayne@69 1017 inline constexpr operator T() const {
jpayne@69 1018 static_assert(value <= T(maxValue), "this operation will truncate");
jpayne@69 1019 return value;
jpayne@69 1020 }
jpayne@69 1021
jpayne@69 1022 inline constexpr operator bool() const {
jpayne@69 1023 static_assert(value <= 1, "this operation will truncate");
jpayne@69 1024 return value;
jpayne@69 1025 }
jpayne@69 1026 };
jpayne@69 1027
jpayne@69 1028 template <uint value>
jpayne@69 1029 inline constexpr SafeConstUnwrapper<value> unbound(BoundedConst<value>) {
jpayne@69 1030 return SafeConstUnwrapper<value>();
jpayne@69 1031 }
jpayne@69 1032
jpayne@69 1033 template <typename T, typename U>
jpayne@69 1034 inline constexpr T unboundAs(U value) {
jpayne@69 1035 return unbound(value);
jpayne@69 1036 }
jpayne@69 1037
jpayne@69 1038 template <uint64_t requestedMax, uint64_t maxN, typename T>
jpayne@69 1039 inline constexpr T unboundMax(Bounded<maxN, T> value) {
jpayne@69 1040 // Explicitly unguard expecting a value that is at most `maxN`.
jpayne@69 1041 static_assert(maxN <= requestedMax, "possible overflow detected");
jpayne@69 1042 return value.unwrap();
jpayne@69 1043 }
jpayne@69 1044
jpayne@69 1045 template <uint64_t requestedMax, uint value>
jpayne@69 1046 inline constexpr uint unboundMax(BoundedConst<value>) {
jpayne@69 1047 // Explicitly unguard expecting a value that is at most `maxN`.
jpayne@69 1048 static_assert(value <= requestedMax, "overflow detected");
jpayne@69 1049 return value;
jpayne@69 1050 }
jpayne@69 1051
jpayne@69 1052 template <uint bits, typename T>
jpayne@69 1053 inline constexpr auto unboundMaxBits(T value) ->
jpayne@69 1054 decltype(unboundMax<maxValueForBits<bits>()>(value)) {
jpayne@69 1055 // Explicitly unguard expecting a value that fits into `bits` bits.
jpayne@69 1056 return unboundMax<maxValueForBits<bits>()>(value);
jpayne@69 1057 }
jpayne@69 1058
jpayne@69 1059 #define OP(op) \
jpayne@69 1060 template <uint64_t maxN, typename T, typename U> \
jpayne@69 1061 inline constexpr auto operator op(T a, SafeUnwrapper<maxN, U> b) -> decltype(a op (T)b) { \
jpayne@69 1062 return a op (AtLeastUInt<sizeof(T)*8>)b; \
jpayne@69 1063 } \
jpayne@69 1064 template <uint64_t maxN, typename T, typename U> \
jpayne@69 1065 inline constexpr auto operator op(SafeUnwrapper<maxN, U> b, T a) -> decltype((T)b op a) { \
jpayne@69 1066 return (AtLeastUInt<sizeof(T)*8>)b op a; \
jpayne@69 1067 } \
jpayne@69 1068 template <uint64_t value, typename T> \
jpayne@69 1069 inline constexpr auto operator op(T a, SafeConstUnwrapper<value> b) -> decltype(a op (T)b) { \
jpayne@69 1070 return a op (AtLeastUInt<sizeof(T)*8>)b; \
jpayne@69 1071 } \
jpayne@69 1072 template <uint64_t value, typename T> \
jpayne@69 1073 inline constexpr auto operator op(SafeConstUnwrapper<value> b, T a) -> decltype((T)b op a) { \
jpayne@69 1074 return (AtLeastUInt<sizeof(T)*8>)b op a; \
jpayne@69 1075 }
jpayne@69 1076
jpayne@69 1077 OP(+)
jpayne@69 1078 OP(-)
jpayne@69 1079 OP(*)
jpayne@69 1080 OP(/)
jpayne@69 1081 OP(%)
jpayne@69 1082 OP(<<)
jpayne@69 1083 OP(>>)
jpayne@69 1084 OP(&)
jpayne@69 1085 OP(|)
jpayne@69 1086 OP(==)
jpayne@69 1087 OP(!=)
jpayne@69 1088 OP(<=)
jpayne@69 1089 OP(>=)
jpayne@69 1090 OP(<)
jpayne@69 1091 OP(>)
jpayne@69 1092
jpayne@69 1093 #undef OP
jpayne@69 1094
jpayne@69 1095 // -------------------------------------------------------------------
jpayne@69 1096
jpayne@69 1097 template <uint64_t maxN, typename T>
jpayne@69 1098 class Range<Bounded<maxN, T>> {
jpayne@69 1099 public:
jpayne@69 1100 inline constexpr Range(Bounded<maxN, T> begin, Bounded<maxN, T> end)
jpayne@69 1101 : inner(unbound(begin), unbound(end)) {}
jpayne@69 1102 inline explicit constexpr Range(Bounded<maxN, T> end)
jpayne@69 1103 : inner(unbound(end)) {}
jpayne@69 1104
jpayne@69 1105 class Iterator {
jpayne@69 1106 public:
jpayne@69 1107 Iterator() = default;
jpayne@69 1108 inline explicit Iterator(typename Range<T>::Iterator inner): inner(inner) {}
jpayne@69 1109
jpayne@69 1110 inline Bounded<maxN, T> operator* () const { return Bounded<maxN, T>(*inner, unsafe); }
jpayne@69 1111 inline Iterator& operator++() { ++inner; return *this; }
jpayne@69 1112
jpayne@69 1113 inline bool operator==(const Iterator& other) const { return inner == other.inner; }
jpayne@69 1114 inline bool operator!=(const Iterator& other) const { return inner != other.inner; }
jpayne@69 1115
jpayne@69 1116 private:
jpayne@69 1117 typename Range<T>::Iterator inner;
jpayne@69 1118 };
jpayne@69 1119
jpayne@69 1120 inline Iterator begin() const { return Iterator(inner.begin()); }
jpayne@69 1121 inline Iterator end() const { return Iterator(inner.end()); }
jpayne@69 1122
jpayne@69 1123 private:
jpayne@69 1124 Range<T> inner;
jpayne@69 1125 };
jpayne@69 1126
jpayne@69 1127 template <typename T, typename U>
jpayne@69 1128 class Range<Quantity<T, U>> {
jpayne@69 1129 public:
jpayne@69 1130 inline constexpr Range(Quantity<T, U> begin, Quantity<T, U> end)
jpayne@69 1131 : inner(begin / unit<Quantity<T, U>>(), end / unit<Quantity<T, U>>()) {}
jpayne@69 1132 inline explicit constexpr Range(Quantity<T, U> end)
jpayne@69 1133 : inner(end / unit<Quantity<T, U>>()) {}
jpayne@69 1134
jpayne@69 1135 class Iterator {
jpayne@69 1136 public:
jpayne@69 1137 Iterator() = default;
jpayne@69 1138 inline explicit Iterator(typename Range<T>::Iterator inner): inner(inner) {}
jpayne@69 1139
jpayne@69 1140 inline Quantity<T, U> operator* () const { return *inner * unit<Quantity<T, U>>(); }
jpayne@69 1141 inline Iterator& operator++() { ++inner; return *this; }
jpayne@69 1142
jpayne@69 1143 inline bool operator==(const Iterator& other) const { return inner == other.inner; }
jpayne@69 1144 inline bool operator!=(const Iterator& other) const { return inner != other.inner; }
jpayne@69 1145
jpayne@69 1146 private:
jpayne@69 1147 typename Range<T>::Iterator inner;
jpayne@69 1148 };
jpayne@69 1149
jpayne@69 1150 inline Iterator begin() const { return Iterator(inner.begin()); }
jpayne@69 1151 inline Iterator end() const { return Iterator(inner.end()); }
jpayne@69 1152
jpayne@69 1153 private:
jpayne@69 1154 Range<T> inner;
jpayne@69 1155 };
jpayne@69 1156
jpayne@69 1157 template <uint value>
jpayne@69 1158 inline constexpr Range<Bounded<value, uint>> zeroTo(BoundedConst<value> end) {
jpayne@69 1159 return Range<Bounded<value, uint>>(end);
jpayne@69 1160 }
jpayne@69 1161
jpayne@69 1162 template <uint value, typename Unit>
jpayne@69 1163 inline constexpr Range<Quantity<Bounded<value, uint>, Unit>>
jpayne@69 1164 zeroTo(Quantity<BoundedConst<value>, Unit> end) {
jpayne@69 1165 return Range<Quantity<Bounded<value, uint>, Unit>>(end);
jpayne@69 1166 }
jpayne@69 1167
jpayne@69 1168 } // namespace kj
jpayne@69 1169
jpayne@69 1170 KJ_END_HEADER