diff options
Diffstat (limited to 'src/base')
-rw-r--r-- | src/base/BUILD.bazel | 45 | ||||
-rw-r--r-- | src/base/bit_stream.h | 77 | ||||
-rw-r--r-- | src/base/bottom_n.h | 78 | ||||
-rw-r--r-- | src/base/math_utils.h | 80 | ||||
-rw-r--r-- | src/base/optional.h | 520 | ||||
-rw-r--r-- | src/base/string_utils.h | 68 | ||||
-rw-r--r-- | src/base/test/bit_stream_test.cpp | 141 | ||||
-rw-r--r-- | src/base/test/bottom_n_test.cpp | 99 | ||||
-rw-r--r-- | src/base/test/math_utils_test.cpp | 78 | ||||
-rw-r--r-- | src/base/test/optional_test.cpp | 481 | ||||
-rw-r--r-- | src/base/test/string_utils_test.cpp | 110 | ||||
-rw-r--r-- | src/base/test/type_traits_test.cpp | 128 | ||||
-rw-r--r-- | src/base/test/uint128_test.cpp | 140 | ||||
-rw-r--r-- | src/base/type_traits.h | 172 | ||||
-rw-r--r-- | src/base/uint128.h | 175 |
15 files changed, 2392 insertions, 0 deletions
diff --git a/src/base/BUILD.bazel b/src/base/BUILD.bazel new file mode 100644 index 0000000..9d8b9a0 --- /dev/null +++ b/src/base/BUILD.bazel @@ -0,0 +1,45 @@ +# Copyright 2018 Google LLC +# +# Licensed 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 +# +# https://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. + +cc_library( + name = "base", + hdrs = [ + "bit_stream.h", + "bottom_n.h", + "math_utils.h", + "optional.h", + "string_utils.h", + "type_traits.h", + "uint128.h", + ], + visibility = ["//src/decoder:__pkg__"], +) + +cc_test( + name = "base_test", + srcs = [ + "test/bit_stream_test.cpp", + "test/bottom_n_test.cpp", + "test/math_utils_test.cpp", + "test/optional_test.cpp", + "test/string_utils_test.cpp", + "test/type_traits_test.cpp", + "test/uint128_test.cpp", + ], + deps = [ + "@gtest//:gtest_main", + ":base", + ], +) + diff --git a/src/base/bit_stream.h b/src/base/bit_stream.h new file mode 100644 index 0000000..c878197 --- /dev/null +++ b/src/base/bit_stream.h @@ -0,0 +1,77 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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. + +#ifndef ASTC_CODEC_BASE_BIT_STREAM_H_ +#define ASTC_CODEC_BASE_BIT_STREAM_H_ + +#include <cassert> +#include <cstdint> + +namespace astc_codec { +namespace base { + +// Represents a stream of bits that can be read or written in arbitrary-sized +// chunks. +template<typename IntType = uint64_t> +class BitStream { + public: + // Creates an empty BitStream. + BitStream() = default; + BitStream(IntType data, uint32_t data_size) + : data_(data), data_size_(data_size) { + assert(data_size_ <= sizeof(data_) * 8); + } + + // Return the number of bits in the stream. + uint32_t Bits() const { return data_size_; } + + // Put |size| bits into the stream. + // Fails if there is not enough space in the buffer to store the bits. + template<typename ResultType> + void PutBits(ResultType x, uint32_t size) { + assert(data_size_ + size <= sizeof(data_) * 8); + + data_ |= (IntType(x) & MaskFor(size)) << data_size_; + data_size_ += size; + } + + // Get |count| bits from the stream. + // Returns true if |count| bits were successfully retrieved. + template<typename ResultType> + bool GetBits(uint32_t count, ResultType* result) { + if (count <= data_size_) { + *result = static_cast<ResultType>(data_ & MaskFor(count)); + data_ = data_ >> count; + data_size_ -= count; + return true; + } else { + *result = 0; + return false; + } + } + + private: + IntType MaskFor(uint32_t bits) const { + return (bits == sizeof(IntType) * 8) ? ~IntType(0) + : (IntType(1) << bits) - 1; + } + + IntType data_ = 0; + uint32_t data_size_ = 0; +}; + +} // namespace base +} // namespace astc_codec + +#endif // ASTC_CODEC_BASE_BIT_STREAM_H_ diff --git a/src/base/bottom_n.h b/src/base/bottom_n.h new file mode 100644 index 0000000..4edc8ef --- /dev/null +++ b/src/base/bottom_n.h @@ -0,0 +1,78 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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. + +#ifndef ASTC_CODEC_BASE_BOTTOM_N_H_ +#define ASTC_CODEC_BASE_BOTTOM_N_H_ + +#include <algorithm> +#include <functional> +#include <vector> + +namespace astc_codec { +namespace base { + +// Used to aggregate the lowest N values of data supplied. +template<typename T, typename CompareFn = std::less<T>> +class BottomN { + public: + typedef std::vector<T> ContainerType; + + // Creates an empty BottomN with limit |max_size|. + BottomN(size_t max_size) : max_size_(max_size) { } + + bool Empty() const { return data_.empty(); } + size_t Size() const { return data_.size(); } + + const T& Top() const { return data_.front(); } + + void Push(const T& value) { + if (data_.size() < max_size_ || compare_(value, Top())) { + data_.push_back(value); + std::push_heap(data_.begin(), data_.end(), compare_); + + if (Size() > max_size_) { + PopTop(); + } + } + } + + std::vector<T> Pop() { + const size_t len = Size(); + std::vector<T> result(len); + + for (size_t i = 0; i < len; ++i) { + result[len - i - 1] = PopTop(); + } + + return result; + } + + private: + T PopTop() { + std::pop_heap(data_.begin(), data_.end(), compare_); + T result = data_.back(); + data_.pop_back(); + return result; + } + + ContainerType data_; + CompareFn compare_; + + const size_t max_size_; +}; + +} // namespace base +} // namespace astc_codec + +#endif // ASTC_CODEC_BASE_BOTTOM_N_H_ diff --git a/src/base/math_utils.h b/src/base/math_utils.h new file mode 100644 index 0000000..48f1a24 --- /dev/null +++ b/src/base/math_utils.h @@ -0,0 +1,80 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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. + +#ifndef ASTC_CODEC_BASE_MATH_UTILS_H_ +#define ASTC_CODEC_BASE_MATH_UTILS_H_ + +#include "src/base/uint128.h" + +#include <cassert> +#include <cstdint> +#include <type_traits> + +namespace astc_codec { +namespace base { + +inline int Log2Floor(uint32_t n) { + if (n == 0) { + return -1; + } + + int log = 0; + uint32_t value = n; + for (int i = 4; i >= 0; --i) { + int shift = (1 << i); + uint32_t x = value >> shift; + if (x != 0) { + value = x; + log += shift; + } + } + assert(value == 1); + return log; +} + +inline int CountOnes(uint32_t n) { + n -= ((n >> 1) & 0x55555555); + n = ((n >> 2) & 0x33333333) + (n & 0x33333333); + return static_cast<int>((((n + (n >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24); +} + +template<typename T> +inline T ReverseBits(T value) { + uint32_t s = sizeof(value) * 8; + T mask = ~T(0); + while ((s >>= 1) > 0) { + mask ^= (mask << s); + value = ((value >> s) & mask) | ((value << s) & ~mask); + } + + return value; +} + +template<typename T> +inline T GetBits(T source, uint32_t offset, uint32_t count) { + static_assert(std::is_same<T, UInt128>::value || std::is_unsigned<T>::value, + "T must be unsigned."); + + const uint32_t total_bits = sizeof(T) * 8; + assert(count > 0); + assert(offset + count <= total_bits); + + const T mask = count == total_bits ? ~T(0) : ~T(0) >> (total_bits - count); + return (source >> offset) & mask; +} + +} // namespace base +} // namespace astc_codec + +#endif // ASTC_CODEC_BASE_MATH_UTILS_H_ diff --git a/src/base/optional.h b/src/base/optional.h new file mode 100644 index 0000000..5ede4af --- /dev/null +++ b/src/base/optional.h @@ -0,0 +1,520 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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. + +#ifndef ASTC_CODEC_BASE_OPTIONAL_H_ +#define ASTC_CODEC_BASE_OPTIONAL_H_ + +#include "src/base/type_traits.h" + +#include <cassert> +#include <initializer_list> +#include <type_traits> +#include <utility> + +#include <cstddef> + +// Optional<T> - a template class to store an optional value of type T. +// +// Usage examples: +// +// Initialization and construction: +// Optional<Foo> foo; // |foo| doesn't contain a value. +// Optional<Foo> foo(Foo(10)); // |foo| contains a copy-constructed value. +// Optional<Foo> foo2(foo); // |foo2| contains a copy of |foo|'s value. +// Optional<Foo> foo3(std::move(foo2)); // Guess what? +// +// Assignment: +// Foo foo_value(0); +// Optional<Foo> foo; // |foo| is empty. +// Optional<Foo> foo2; // |foo2| is empty. +// foo2 = foo; // |foo2| is still empty. +// foo = foo_value; // set value of |foo| to a copy of |foo_value| +// foo = std::move(foo_value); // move |foo_value| into |foo|. +// foo2 = foo; // now |foo2| has a copy of |foo|'s value. +// foo = kNullopt; // unset |foo|, it has no value. +// +// Checking and accessing value: +// if (foo) { +// // |foo| has a value. +// doStuff(*foo); // |*foo| is the value inside |foo|. +// foo->callMethod(); // Same as (*foo).callMethod(). +// } else { +// // |foo| is empty. +// } +// +// foo.value() // Same as *foo +// foo.valueOr(<default>) // Return <default> is |foo| has no value. +// +// In-place construction: +// +// Optional<Foo> foo; // |foo| is empty. +// foo.emplace(20); // |foo| now contains a value constructed as Foo(20) +// +// Optional<Foo> foo(kInplace, 20); // |foo| is initialized with a value +// // that is constructed in-place as +// // Foo(20). +// +// return makeOptional<Foo>(20); // Takes Foo constructor arguments +// // directly. +// +// Returning values: +// +// Optional<Foo> myFunc(...) { +// if (someCondition) { +// return Foo(10); // call Optional<Foo>(Foo&) constructor. +// } else { +// return {}; // call Optional<Foo>() constructor, which +// // builds an empty value. +// } +// } +// +// Memory layout: +// Optional<Foo> is equivalent to: +// +// struct { +// bool flag; +// Foo value; +// }; +// +// in terms of memory layout. This means it *doubles* the size of integral +// types. Also: +// +// - Optional<Foo> can be constructed from anything that constructs a Foo. +// +// - Same with Optional<Foo>(kInplace, Args...) where Args... matches any +// arguments that can be passed to a Foo constructor. +// +// - Comparison operators are provided. Beware: an empty Optional<Foo> +// is always smaller than any Foo value. + +namespace astc_codec { +namespace base { + +namespace details { + +// Base classes to reduce the number of instantiations of the Optional's +// internal members. +class OptionalFlagBase { + public: + void setConstructed(bool constructed) { mConstructed = constructed; } + constexpr bool constructed() const { return mConstructed; } + constexpr operator bool() const { return constructed(); } + bool hasValue() const { return constructed(); } + + constexpr OptionalFlagBase(bool constructed = false) + : mConstructed(constructed) { } + + private: + bool mConstructed = false; +}; + +template<size_t Size, size_t Align> +class OptionalStorageBase { + protected: + using StoreT = typename std::aligned_storage<Size, Align>::type; + StoreT mStorage = {}; +}; + +} // namespace details + +// A tag type for empty optional construction +struct NulloptT { + constexpr explicit NulloptT(int) { } +}; + +// A tag type for inplace value construction +struct InplaceT { + constexpr explicit InplaceT(int) { } +}; + +// Tag values for null optional and inplace construction +constexpr NulloptT kNullopt{1}; +constexpr InplaceT kInplace{1}; + +// Forward declaration for an early use +template<class T> +class Optional; + +// A type trait for checking if a type is an optional instantiation +// Note: if you want to refer to the template name inside the template, +// you need to declare this alias outside of it - because the +// class name inside of the template stands for an instantiated template +// E.g, for template <T> class Foo if you say 'Foo' inside the class, it +// actually means Foo<T>; +template<class U> +using is_any_optional = + is_template_instantiation_of<typename std::decay<U>::type, Optional>; + +template<class T> +class Optional + : private details::OptionalFlagBase, + private details::OptionalStorageBase<sizeof(T), + std::alignment_of<T>::value> { + // make sure all optionals are buddies - this is needed to implement + // conversion from optionals of other types + template<class U> + friend class Optional; + + template<class U> + using self = Optional<U>; + + using base_flag = details::OptionalFlagBase; + using base_storage = + details::OptionalStorageBase<sizeof(T), std::alignment_of<T>::value>; + + public: + // std::optional will have this, so let's provide it + using value_type = T; + + // make sure we forbid some Optional instantiations where things may get + // really messy + static_assert(!std::is_same<typename std::decay<T>::type, NulloptT>::value, + "Optional of NulloptT is not allowed"); + static_assert(!std::is_same<typename std::decay<T>::type, InplaceT>::value, + "Optional of InplaceT is not allowed"); + static_assert(!std::is_reference<T>::value, + "Optional references are not allowed: use a pointer instead"); + + // constructors + constexpr Optional() { } + constexpr Optional(NulloptT) { } + + Optional(const Optional& other) : base_flag(other.constructed()) { + if (this->constructed()) { + new (&get()) T(other.get()); + } + } + Optional(Optional&& other) : base_flag(other.constructed()) { + if (this->constructed()) { + new (&get()) T(std::move(other.get())); + } + } + + // Conversion constructor from optional of similar type + template<class U, class = enable_if_c<!is_any_optional<U>::value && + std::is_constructible<T, U>::value>> + Optional(const Optional<U>& other) : base_flag(other.constructed()) { + if (this->constructed()) { + new (&get()) T(other.get()); + } + } + + // Move-conversion constructor + template<class U, class = enable_if_c<!is_any_optional<U>::value && + std::is_constructible<T, U>::value>> + Optional(Optional<U>&& other) : base_flag(other.constructed()) { + if (this->constructed()) { + new (&get()) T(std::move(other.get())); + } + } + + // Construction from a raw value + Optional(const T& value) : base_flag(true) { new (&get()) T(value); } + // Move construction from a raw value + Optional(T&& value) : base_flag(true) { new (&get()) T(std::move(value)); } + + // Inplace construction from a list of |T|'s ctor arguments + template<class... Args> + Optional(InplaceT, Args&&... args) : base_flag(true) { + new (&get()) T(std::forward<Args>(args)...); + } + + // Inplace construction from an initializer list passed into |T|'s ctor + template<class U, class = enable_if< + std::is_constructible<T, std::initializer_list<U>>>> + Optional(InplaceT, std::initializer_list<U> il) : base_flag(true) { + new (&get()) T(il); + } + + // direct assignment + Optional& operator=(const Optional& other) { + if (&other == this) { + return *this; + } + + if (this->constructed()) { + if (other.constructed()) { + get() = other.get(); + } else { + destruct(); + this->setConstructed(false); + } + } else { + if (other.constructed()) { + new (&get()) T(other.get()); + this->setConstructed(true); + } else { + ; // we're good + } + } + return *this; + } + + // move assignment + Optional& operator=(Optional&& other) { + if (this->constructed()) { + if (other.constructed()) { + get() = std::move(other.get()); + } else { + destruct(); + this->setConstructed(false); + } + } else { + if (other.constructed()) { + new (&get()) T(std::move(other.get())); + this->setConstructed(true); + } else { + ; // we're good + } + } + return *this; + } + + // conversion assignment + template<class U, + class = enable_if_convertible<typename std::decay<U>::type, T>> + Optional& operator=(const Optional<U>& other) { + if (this->constructed()) { + if (other.constructed()) { + get() = other.get(); + } else { + destruct(); + this->setConstructed(false); + } + } else { + if (other.constructed()) { + new (&get()) T(other.get()); + this->setConstructed(true); + } else { + ; // we're good + } + } + return *this; + } + + // conversion move assignment + template<class U, + class = enable_if_convertible<typename std::decay<U>::type, T>> + Optional& operator=(Optional<U>&& other) { + if (this->constructed()) { + if (other.constructed()) { + get() = std::move(other.get()); + } else { + destruct(); + this->setConstructed(false); + } + } else { + if (other.constructed()) { + new (&get()) T(std::move(other.get())); + this->setConstructed(true); + } else { + ; // we're good + } + } + return *this; + } + + // the most complicated one: forwarding constructor for anything convertible + // to |T|, excluding the stuff implemented above explicitly + template<class U, + class = enable_if_c< + !is_any_optional<typename std::decay<U>::type>::value && + std::is_convertible<typename std::decay<U>::type, T>::value>> + Optional& operator=(U&& other) { + if (this->constructed()) { + get() = std::forward<U>(other); + } else { + new (&get()) T(std::forward<U>(other)); + this->setConstructed(true); + } + return *this; + } + + // Adopt value checkers from the parent + using base_flag::operator bool; + using base_flag::hasValue; + + T& value() { + assert(this->constructed()); + return get(); + } + constexpr const T& value() const { + assert(this->constructed()); + return get(); + } + + T* ptr() { return this->constructed() ? &get() : nullptr; } + constexpr const T* ptr() const { + return this->constructed() ? &get() : nullptr; + } + + // Value getter with fallback + template<class U = T, + class = enable_if_convertible<typename std::decay<U>::type, T>> + constexpr T valueOr(U&& defaultValue) const { + return this->constructed() ? get() : std::move(defaultValue); + } + + // Pointer-like operators + T& operator*() { + assert(this->constructed()); + return get(); + } + constexpr const T& operator*() const { + assert(this->constructed()); + return get(); + } + + T* operator->() { + assert(this->constructed()); + return &get(); + } + constexpr const T* operator->() const { + assert(this->constructed()); + return &get(); + } + + ~Optional() { + if (this->constructed()) { + destruct(); + } + } + + void clear() { + if (this->constructed()) { + destruct(); + this->setConstructed(false); + } + } + + template<class U, + class = enable_if_convertible<typename std::decay<U>::type, T>> + void reset(U&& u) { + *this = std::forward<U>(u); + } + + // In-place construction with possible destruction of the old value + template<class... Args> + void emplace(Args&&... args) { + if (this->constructed()) { + destruct(); + } + new (&get()) T(std::forward<Args>(args)...); + this->setConstructed(true); + } + + // In-place construction with possible destruction of the old value + // initializer-list version + template<class U, class = enable_if< + std::is_constructible<T, std::initializer_list<U>>>> + void emplace(std::initializer_list<U> il) { + if (this->constructed()) { + destruct(); + } + new (&get()) T(il); + this->setConstructed(true); + } + + private: + // A helper function to convert the internal raw storage to T& + constexpr const T& get() const { + return *reinterpret_cast<const T*>( + reinterpret_cast<const char*>(&this->mStorage)); + } + + // Same thing, mutable + T& get() { return const_cast<T&>(const_cast<const Optional*>(this)->get()); } + + // Shortcut for a destructor call for the stored object + void destruct() { get().T::~T(); } +}; + +template<class T> +Optional<typename std::decay<T>::type> makeOptional(T&& t) { + return Optional<typename std::decay<T>::type>(std::forward<T>(t)); +} + +template<class T, class... Args> +Optional<typename std::decay<T>::type> makeOptional(Args&&... args) { + return Optional<typename std::decay<T>::type>(kInplace, + std::forward<Args>(args)...); +} + +template<class T> +bool operator==(const Optional<T>& l, const Optional<T>& r) { + return l.hasValue() ? r.hasValue() && *l == *r : !r.hasValue(); +} +template<class T> +bool operator==(const Optional<T>& l, NulloptT) { + return !l; +} +template<class T> +bool operator==(NulloptT, const Optional<T>& r) { + return !r; +} +template<class T> +bool operator==(const Optional<T>& l, const T& r) { + return bool(l) && *l == r; +} +template<class T> +bool operator==(const T& l, const Optional<T>& r) { + return bool(r) && l == *r; +} + +template<class T> +bool operator!=(const Optional<T>& l, const Optional<T>& r) { + return !(l == r); +} +template<class T> +bool operator!=(const Optional<T>& l, NulloptT) { + return bool(l); +} +template<class T> +bool operator!=(NulloptT, const Optional<T>& r) { + return bool(r); +} +template<class T> +bool operator!=(const Optional<T>& l, const T& r) { + return !l || !(*l == r); +} +template<class T> +bool operator!=(const T& l, const Optional<T>& r) { + return !r || !(l == *r); +} + +template<class T> +bool operator<(const Optional<T>& l, const Optional<T>& r) { + return !r ? false : (!l ? true : *l < *r); +} +template<class T> +bool operator<(const Optional<T>&, NulloptT) { + return false; +} +template<class T> +bool operator<(NulloptT, const Optional<T>& r) { + return bool(r); +} +template<class T> +bool operator<(const Optional<T>& l, const T& r) { + return !l || *l < r; +} +template<class T> +bool operator<(const T& l, const Optional<T>& r) { + return bool(r) && l < *r; +} + +} // namespace base +} // namespace astc_codec + +#endif // ASTC_CODEC_BASE_OPTIONAL_H_ diff --git a/src/base/string_utils.h b/src/base/string_utils.h new file mode 100644 index 0000000..c450b27 --- /dev/null +++ b/src/base/string_utils.h @@ -0,0 +1,68 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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. + +#ifndef ASTC_CODEC_BASE_STRING_UTILS_H_ +#define ASTC_CODEC_BASE_STRING_UTILS_H_ + +#include <limits> +#include <string> + +namespace astc_codec { +namespace base { + +// Iterates over a string's parts using |splitBy| as a delimiter. +// |splitBy| must be a nonempty string well, or it's a no-op. +// Otherwise, |func| is called on each of the splits, excluding the +// characters that are part of |splitBy|. If two |splitBy|'s occur in a row, +// |func| will be called on a StringView("") in between. See +// StringUtils_unittest.cpp for the full story. +template<class Func> +void Split(const std::string& str, const std::string& splitBy, Func func) { + if (splitBy.empty()) { + return; + } + + size_t splitSize = splitBy.size(); + size_t begin = 0; + size_t end = str.find(splitBy); + + while (true) { + func(str.substr(begin, end - begin)); + if (end == std::string::npos) { + return; + } + + begin = end + splitSize; + end = str.find(splitBy, begin); + } +} + +static int32_t ParseInt32(const char* str, int32_t deflt) { + using std::numeric_limits; + + char* error = nullptr; + int64_t value = strtol(str, &error, 0); + // Limit long values to int32 min/max. Needed for lp64; no-op on 32 bits. + if (value > std::numeric_limits<int32_t>::max()) { + value = std::numeric_limits<int32_t>::max(); + } else if (value < std::numeric_limits<int32_t>::min()) { + value = std::numeric_limits<int32_t>::min(); + } + return (error == str) ? deflt : static_cast<int32_t>(value); +} + +} // namespace base +} // namespace astc_codec + +#endif // ASTC_CODEC_BASE_STRING_UTILS_H_ diff --git a/src/base/test/bit_stream_test.cpp b/src/base/test/bit_stream_test.cpp new file mode 100644 index 0000000..0c4b3c9 --- /dev/null +++ b/src/base/test/bit_stream_test.cpp @@ -0,0 +1,141 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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 "src/base/bit_stream.h" + +#include <gtest/gtest.h> + +namespace astc_codec { +namespace base { + +namespace { + static constexpr uint64_t kAllBits = 0xFFFFFFFFFFFFFFFF; + static constexpr uint64_t k40Bits = 0x000000FFFFFFFFFF; +} + +TEST(BitStream, Decode) { + { + BitStream<uint64_t> stream(0, 1); + + uint64_t bits = kAllBits; + EXPECT_TRUE(stream.GetBits(1, &bits)); + EXPECT_EQ(bits, 0); + EXPECT_FALSE(stream.GetBits(1, &bits)); + } + + { + BitStream<uint64_t> stream(0b1010101010101010, 32); + EXPECT_EQ(stream.Bits(), 32); + + uint64_t bits = 0; + EXPECT_TRUE(stream.GetBits(1, &bits)); + EXPECT_EQ(bits, 0); + + EXPECT_TRUE(stream.GetBits(3, &bits)); + EXPECT_EQ(bits, 0b101); + + EXPECT_TRUE(stream.GetBits(8, &bits)); + EXPECT_EQ(bits, 0b10101010); + + EXPECT_EQ(stream.Bits(), 20); + + EXPECT_TRUE(stream.GetBits(20, &bits)); + EXPECT_EQ(bits, 0b1010); + EXPECT_EQ(stream.Bits(), 0); + } + + { + BitStream<uint64_t> stream(kAllBits, 64); + EXPECT_EQ(stream.Bits(), 64); + + uint64_t bits = 0; + EXPECT_TRUE(stream.GetBits(64, &bits)); + EXPECT_EQ(bits, kAllBits); + EXPECT_EQ(stream.Bits(), 0); + } + + { + BitStream<uint64_t> stream(kAllBits, 64); + EXPECT_EQ(stream.Bits(), 64); + + uint64_t bits = 0; + EXPECT_TRUE(stream.GetBits(40, &bits)); + EXPECT_EQ(bits, k40Bits); + EXPECT_EQ(stream.Bits(), 24); + } + + { + BitStream<uint64_t> stream(kAllBits, 32); + + uint64_t bits = 0; + EXPECT_TRUE(stream.GetBits(0, &bits)); + EXPECT_EQ(bits, 0); + EXPECT_TRUE(stream.GetBits(32, &bits)); + EXPECT_EQ(bits, k40Bits & 0xFFFFFFFF); + EXPECT_TRUE(stream.GetBits(0, &bits)); + EXPECT_EQ(bits, 0); + EXPECT_EQ(stream.Bits(), 0); + } +} + +TEST(BitStream, Encode) { + { + BitStream<uint64_t> stream; + + stream.PutBits(0, 1); + stream.PutBits(0b11, 2); + EXPECT_EQ(stream.Bits(), 3); + + uint64_t bits = 0; + EXPECT_TRUE(stream.GetBits(3, &bits)); + EXPECT_EQ(bits, 0b110); + } + + { + BitStream<uint64_t> stream; + + uint64_t bits = 0; + stream.PutBits(kAllBits, 64); + EXPECT_EQ(stream.Bits(), 64); + + EXPECT_TRUE(stream.GetBits(64, &bits)); + EXPECT_EQ(bits, kAllBits); + EXPECT_EQ(stream.Bits(), 0); + } + + { + BitStream<uint64_t> stream; + stream.PutBits(kAllBits, 40); + + uint64_t bits = 0; + EXPECT_TRUE(stream.GetBits(40, &bits)); + EXPECT_EQ(bits, k40Bits); + EXPECT_EQ(stream.Bits(), 0); + } + + { + BitStream<uint64_t> stream; + stream.PutBits(0, 0); + stream.PutBits(kAllBits, 32); + stream.PutBits(0, 0); + + uint64_t bits = 0; + EXPECT_TRUE(stream.GetBits(32, &bits)); + EXPECT_EQ(bits, k40Bits & 0xFFFFFFFF); + EXPECT_EQ(stream.Bits(), 0); + } +} + +} // namespace base +} // namespace astc_codec diff --git a/src/base/test/bottom_n_test.cpp b/src/base/test/bottom_n_test.cpp new file mode 100644 index 0000000..8a48d30 --- /dev/null +++ b/src/base/test/bottom_n_test.cpp @@ -0,0 +1,99 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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 "src/base/bottom_n.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace astc_codec { +namespace base { + +using ::testing::ElementsAre; + +template<typename T, size_t N> +static void pushAll(BottomN<T>& heap, const T (&arr)[N]) { + for (auto i : arr) { + heap.Push(i); + } +} + +TEST(BottomN, Sort) { + { + BottomN<int> heap(10); + EXPECT_TRUE(heap.Empty()); + pushAll(heap, {1, 2}); + + EXPECT_EQ(heap.Size(), 2); + EXPECT_FALSE(heap.Empty()); + EXPECT_THAT(heap.Pop(), ElementsAre(1, 2)); + } + + { + BottomN<int> heap(6); + pushAll(heap, {1, 4, 3, 2, 2, 1}); + + EXPECT_EQ(heap.Size(), 6); + EXPECT_THAT(heap.Pop(), ElementsAre(1, 1, 2, 2, 3, 4)); + } +} + +TEST(BottomN, Bounds) { + { + BottomN<int> heap(4); + pushAll(heap, {1, 2, 3, 4}); + EXPECT_EQ(heap.Size(), 4); + + heap.Push(0); + EXPECT_EQ(heap.Size(), 4); + + EXPECT_THAT(heap.Pop(), ElementsAre(0, 1, 2, 3)); + } + + { + BottomN<int> heap(4); + pushAll(heap, {4, 3, 2, 1}); + EXPECT_EQ(heap.Size(), 4); + + pushAll(heap, {4, 4, 4, 4}); + EXPECT_EQ(heap.Size(), 4); + + EXPECT_THAT(heap.Pop(), ElementsAre(1, 2, 3, 4)); + } + + { + BottomN<int> heap(4); + pushAll(heap, {4, 3, 2, 1}); + EXPECT_EQ(heap.Size(), 4); + + pushAll(heap, {5, 5, 5, 5}); + EXPECT_EQ(heap.Size(), 4); + + EXPECT_THAT(heap.Pop(), ElementsAre(1, 2, 3, 4)); + } + + { + BottomN<int> heap(4); + pushAll(heap, {4, 3, 2, 1}); + EXPECT_EQ(heap.Size(), 4); + + pushAll(heap, {0, 0, 0, 0}); + EXPECT_EQ(heap.Size(), 4); + + EXPECT_THAT(heap.Pop(), ElementsAre(0, 0, 0, 0)); + } +} + +} // namespace base +} // namespace astc_codec diff --git a/src/base/test/math_utils_test.cpp b/src/base/test/math_utils_test.cpp new file mode 100644 index 0000000..0371e11 --- /dev/null +++ b/src/base/test/math_utils_test.cpp @@ -0,0 +1,78 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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 "src/base/math_utils.h" + +#include <gtest/gtest.h> + +namespace astc_codec { +namespace base { + +TEST(MathUtils, Log2Floor) { + EXPECT_EQ(-1, Log2Floor(0)); + + for (int i = 0; i < 32; i++) { + uint32_t n = 1U << i; + EXPECT_EQ(i, Log2Floor(n)); + if (n > 2) { + EXPECT_EQ(i - 1, Log2Floor(n - 1)); + EXPECT_EQ(i, Log2Floor(n + 1)); + } + } +} + +TEST(MathUtils, CountOnes) { + EXPECT_EQ(0, CountOnes(0)); + EXPECT_EQ(1, CountOnes(1)); + EXPECT_EQ(32, CountOnes(static_cast<uint32_t>(~0U))); + EXPECT_EQ(1, CountOnes(0x8000000)); + + for (int i = 0; i < 32; i++) { + EXPECT_EQ(1, CountOnes(1U << i)); + EXPECT_EQ(31, CountOnes(static_cast<uint32_t>(~0U) ^ (1U << i))); + } +} + +TEST(MathUtils, ReverseBits) { + EXPECT_EQ(ReverseBits(0u), 0u); + EXPECT_EQ(ReverseBits(1u), 1u << 31); + EXPECT_EQ(ReverseBits(0xffffffff), 0xffffffff); + EXPECT_EQ(ReverseBits(0x00000001), 0x80000000); + EXPECT_EQ(ReverseBits(0x80000000), 0x00000001); + EXPECT_EQ(ReverseBits(0xaaaaaaaa), 0x55555555); + EXPECT_EQ(ReverseBits(0x55555555), 0xaaaaaaaa); + EXPECT_EQ(ReverseBits(0x7d5d7f53), 0xcafebabe); + EXPECT_EQ(ReverseBits(0xcafebabe), 0x7d5d7f53); +} + +TEST(MathUtils, GetBits) { + EXPECT_EQ(GetBits(0u, 0, 1), 0u); + EXPECT_EQ(GetBits(0u, 0, 32), 0u); + EXPECT_EQ(GetBits(0x00000001u, 0, 1), 0x00000001); + EXPECT_EQ(GetBits(0x00000001u, 0, 32), 0x00000001); + EXPECT_EQ(GetBits(0x00000001u, 1, 31), 0x00000000); + EXPECT_EQ(GetBits(0x00000001u, 31, 1), 0x00000000); + + EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 1, 32), ""); + EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 32, 0), ""); + EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 32, 1), ""); + + EXPECT_EQ(GetBits(0XFFFFFFFFu, 0, 4), 0x0000000F); + EXPECT_EQ(GetBits(0XFFFFFFFFu, 16, 16), 0xFFFF); + EXPECT_EQ(GetBits(0x80000000u, 31, 1), 1); + EXPECT_EQ(GetBits(0xCAFEBABEu, 24, 8), 0xCA); +} + +} // namespace base +} // namespace astc_codec diff --git a/src/base/test/optional_test.cpp b/src/base/test/optional_test.cpp new file mode 100644 index 0000000..1eeefbd --- /dev/null +++ b/src/base/test/optional_test.cpp @@ -0,0 +1,481 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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 "src/base/optional.h" + +#include <gtest/gtest.h> + +#include <memory> +#include <vector> + +namespace astc_codec { +namespace base { + +TEST(Optional, TypeProperties) { + // Making sure optional has the correct alignment and doesn't waste too much + // space + + static_assert(sizeof(Optional<bool>) == 2, "bad Optional<bool> size"); + static_assert(std::alignment_of<Optional<bool>>::value == + std::alignment_of<bool>::value, + "bad Optional<bool> alignment"); + + static_assert(sizeof(Optional<char>) == 2, "bad Optional<char> size"); + static_assert(std::alignment_of<Optional<char>>::value == + std::alignment_of<char>::value, + "bad Optional<char> alignment"); + + static_assert(sizeof(Optional<int16_t>) == 4, "bad Optional<int16_t> size"); + static_assert(std::alignment_of<Optional<int16_t>>::value == + std::alignment_of<int16_t>::value, + "bad Optional<int16_t> alignment"); + + static_assert(sizeof(Optional<int32_t>) == 8, "bad Optional<int32_t> size"); + static_assert(std::alignment_of<Optional<int32_t>>::value == + std::alignment_of<int32_t>::value, + "bad Optional<int32_t> alignment"); + + static_assert(sizeof(Optional<int64_t>) == 16, "bad Optional<int64_t> size"); + static_assert(std::alignment_of<Optional<int64_t>>::value == + std::alignment_of<int64_t>::value, + "bad Optional<int64_t> alignment"); + + struct S128 { + int64_t data[2]; + }; + + static_assert(sizeof(Optional<S128>) == 3 * sizeof(int64_t), + "bad Optional<S128> size"); + static_assert(std::alignment_of<Optional<S128>>::value == + std::alignment_of<S128>::value, + "bad Optional<S128> alignment"); +} + +TEST(Optional, ConstructFromValue) { + { + Optional<int> o; + EXPECT_FALSE(o); + } + { + Optional<int> o = {}; + EXPECT_FALSE(o); + } + { + Optional<int> o = kNullopt; + EXPECT_FALSE(o); + } + { + Optional<int> o(1); + EXPECT_TRUE(o); + EXPECT_EQ(1, *o); + } + { + // check the std::decay<> constructor + Optional<int> o = static_cast<const short&>(1); + EXPECT_TRUE(o); + EXPECT_EQ(1, *o); + } + { + Optional<int> o = 1; + EXPECT_TRUE(o); + EXPECT_EQ(1, *o); + } + { + Optional<int> o{1}; + EXPECT_TRUE(o); + EXPECT_EQ(1, *o); + } + { + short val = 10; + Optional<int> o = val; + EXPECT_TRUE(o); + EXPECT_EQ(10, *o); + } + { + Optional<std::vector<int>> o(kInplace, 10); + EXPECT_TRUE(o); + EXPECT_EQ((std::vector<int>(10)), *o); + } + { + Optional<std::vector<int>> o(kInplace, {1, 2, 3, 4}); + EXPECT_TRUE(o); + EXPECT_EQ((std::vector<int>{1, 2, 3, 4}), *o); + } +} + +TEST(Optional, ConstructFromOptional) { + { + Optional<int> o = Optional<int>(); + EXPECT_FALSE(o); + } + { + Optional<short> o2; + Optional<int> o(o2); + EXPECT_FALSE(o); + } + { + Optional<short> o2 = 42; + Optional<int> o(o2); + EXPECT_TRUE(o); + EXPECT_EQ(42, *o); + } + { + Optional<int> o(Optional<int>(1)); + EXPECT_TRUE(o); + EXPECT_EQ(1, *o); + } + { + Optional<int> o2 = 2; + Optional<int> o = o2; + EXPECT_TRUE(o); + EXPECT_EQ(2, *o); + } + { + Optional<std::vector<int>> o2 = std::vector<int>{20, 30, 40}; + Optional<std::vector<int>> o = o2; + EXPECT_TRUE(o); + EXPECT_EQ((std::vector<int>{20, 30, 40}), *o); + } +} + +TEST(Optional, Assign) { + { + Optional<int> o; + o = 1; + EXPECT_TRUE(o); + EXPECT_EQ(1, *o); + + o = 2; + EXPECT_TRUE(o); + EXPECT_EQ(2, *o); + + o = kNullopt; + EXPECT_FALSE(o); + + o = Optional<int>(10); + EXPECT_TRUE(o); + EXPECT_EQ(10, *o); + + Optional<int> o2; + o = o2; + EXPECT_FALSE(o); + + o = 2u; + EXPECT_TRUE(o); + EXPECT_EQ(2, *o); + + o = Optional<short>(); + EXPECT_FALSE(o); + + o = Optional<short>(20); + EXPECT_TRUE(o); + EXPECT_EQ(20, *o); + + Optional<short> o3(200); + o = o3; + EXPECT_TRUE(o); + EXPECT_EQ(200, *o); + + o = {}; + EXPECT_FALSE(o); + + // check the std::decay<> assignment + o = static_cast<const short&>(1); + EXPECT_TRUE(o); + EXPECT_EQ(1, *o); + } +} + +TEST(Optional, MakeOptional) { + { + auto o = makeOptional(1); + static_assert(std::is_same<decltype(o), Optional<int>>::value, + "Bad type deduction in makeOptional()"); + EXPECT_TRUE(o); + EXPECT_EQ(1, *o); + } + { + auto o = makeOptional(std::vector<char>{'1', '2'}); + static_assert(std::is_same<decltype(o), Optional<std::vector<char>>>::value, + "Bad type deduction in makeOptional()"); + EXPECT_TRUE(o); + EXPECT_EQ((std::vector<char>{'1', '2'}), *o); + } + { + // check std::decay<> in the factory function + auto o = makeOptional("String"); + static_assert(std::is_same<decltype(o), Optional<const char*>>::value, + "Bad type deduction in makeOptional()"); + EXPECT_TRUE(o); + EXPECT_STREQ("String", *o); + } + { + auto o = makeOptional<std::string>("String"); + static_assert(std::is_same<decltype(o), Optional<std::string>>::value, + "Bad type deduction in makeOptional()"); + EXPECT_TRUE(o); + EXPECT_STREQ("String", o->c_str()); + } + { + auto o = makeOptional<std::string>(5, 'b'); + static_assert(std::is_same<decltype(o), Optional<std::string>>::value, + "Bad type deduction in makeOptional()"); + EXPECT_TRUE(o); + EXPECT_STREQ("bbbbb", o->c_str()); + } + { + auto o = makeOptional<std::string>(); + static_assert(std::is_same<decltype(o), Optional<std::string>>::value, + "Bad type deduction in makeOptional()"); + EXPECT_TRUE(o); + EXPECT_STREQ("", o->c_str()); + } +} + +TEST(Optional, Move) { + auto o = makeOptional(std::unique_ptr<int>(new int(10))); + { + decltype(o) o2 = std::move(o); + EXPECT_TRUE(o); + EXPECT_TRUE(o2); + EXPECT_FALSE(bool(*o)); + EXPECT_TRUE(bool(*o2)); + EXPECT_EQ(10, **o2); + + decltype(o) o3; + o3 = std::move(o2); + EXPECT_TRUE(o2); + EXPECT_TRUE(o3); + EXPECT_FALSE(bool(*o2)); + EXPECT_TRUE(bool(*o3)); + EXPECT_EQ(10, **o3); + + o3 = std::move(o2); + EXPECT_TRUE(o2); + EXPECT_TRUE(o3); + EXPECT_FALSE(bool(*o2)); + EXPECT_FALSE(bool(*o3)); + } + + { + decltype(o) o1; + decltype(o) o2 = std::move(o1); + EXPECT_FALSE(o1); + EXPECT_FALSE(o2); + + o2 = std::move(o1); + EXPECT_FALSE(o1); + EXPECT_FALSE(o2); + + decltype(o) o3{kInplace, new int(20)}; + o3 = std::move(o1); + EXPECT_FALSE(o1); + EXPECT_FALSE(o3); + } +} + +TEST(Optional, Value) { + auto o = makeOptional(1); + EXPECT_EQ(1, o.value()); + EXPECT_EQ(1, o.valueOr(2)); + + o = kNullopt; + EXPECT_EQ(2, o.valueOr(2)); +} + +TEST(Optional, Clear) { + auto o = makeOptional(1); + o.clear(); + EXPECT_FALSE(o); + + o.clear(); + EXPECT_FALSE(o); +} + +TEST(Optional, Emplace) { + auto o = makeOptional(std::vector<int>{1, 2, 3, 4}); + o.emplace(3, 1); + EXPECT_TRUE(o); + EXPECT_EQ((std::vector<int>{1, 1, 1}), *o); + EXPECT_EQ(3U, o->capacity()); + + o.clear(); + o.emplace({1, 2}); + EXPECT_TRUE(o); + EXPECT_EQ((std::vector<int>{1, 2}), *o); + EXPECT_EQ(2U, o->capacity()); +} + +TEST(Optional, Reset) { + auto o = makeOptional(std::vector<int>{1, 2, 3, 4}); + o.reset(std::vector<int>{4, 3}); + EXPECT_TRUE(o); + EXPECT_EQ((std::vector<int>{4, 3}), *o); + EXPECT_EQ(2U, o->capacity()); + + o.clear(); + o.reset(std::vector<int>{1}); + EXPECT_EQ((std::vector<int>{1}), *o); + EXPECT_EQ(1U, o->capacity()); +} + +TEST(Optional, CompareEqual) { + EXPECT_TRUE(makeOptional(1) == makeOptional(1)); + EXPECT_TRUE(makeOptional(1) == 1); + EXPECT_TRUE(1 == makeOptional(1)); + EXPECT_FALSE(makeOptional(1) == makeOptional(2)); + EXPECT_FALSE(makeOptional(2) == 1); + EXPECT_FALSE(2 == makeOptional(1)); + EXPECT_TRUE(makeOptional(1) != makeOptional(2)); + EXPECT_TRUE(makeOptional(1) != 2); + EXPECT_TRUE(1 != makeOptional(2)); + + EXPECT_FALSE(makeOptional(1) == kNullopt); + EXPECT_FALSE(makeOptional(1) == Optional<int>()); + EXPECT_FALSE(kNullopt == makeOptional(1)); + EXPECT_FALSE(Optional<int>() == makeOptional(1)); + EXPECT_TRUE(makeOptional(1) != kNullopt); + EXPECT_TRUE(makeOptional(1) != Optional<int>()); + EXPECT_TRUE(kNullopt != makeOptional(1)); + EXPECT_TRUE(Optional<int>() != makeOptional(1)); + + EXPECT_TRUE(kNullopt == Optional<int>()); + EXPECT_TRUE(kNullopt == Optional<char*>()); + EXPECT_FALSE(kNullopt != Optional<int>()); + EXPECT_FALSE(kNullopt != Optional<char*>()); + EXPECT_TRUE(Optional<int>() == Optional<int>()); + EXPECT_FALSE(Optional<int>() != Optional<int>()); +} + +TEST(Optional, CompareLess) { + EXPECT_TRUE(makeOptional(1) < makeOptional(2)); + EXPECT_TRUE(1 < makeOptional(2)); + EXPECT_TRUE(makeOptional(1) < 2); + + EXPECT_FALSE(makeOptional(1) < makeOptional(1)); + EXPECT_FALSE(1 < makeOptional(1)); + EXPECT_FALSE(makeOptional(1) < 1); + EXPECT_FALSE(makeOptional(2) < makeOptional(1)); + EXPECT_FALSE(2 < makeOptional(1)); + EXPECT_FALSE(makeOptional(2) < 1); + + EXPECT_TRUE(kNullopt < makeOptional(2)); + EXPECT_TRUE(Optional<int>() < makeOptional(2)); + EXPECT_TRUE(Optional<int>() < 2); + EXPECT_FALSE(makeOptional(2) < kNullopt); + EXPECT_FALSE(makeOptional(2) < Optional<int>()); + EXPECT_FALSE(2 < Optional<int>()); + + EXPECT_FALSE(kNullopt < Optional<int>()); + EXPECT_FALSE(Optional<int>() < kNullopt); +} + +TEST(Optional, Destruction) { + // create a reference counting class to check if we delete everything + // we've created + struct Track { + Track(int& val) : mVal(val) { ++mVal.get(); } + Track(std::initializer_list<int*> vals) : mVal(**vals.begin()) { + ++mVal.get(); + } + Track(const Track& other) : mVal(other.mVal) { ++mVal.get(); } + Track(Track&& other) : mVal(other.mVal) { ++mVal.get(); } + Track& operator=(const Track& other) { + --mVal.get(); + mVal = other.mVal; + ++mVal.get(); + return *this; + } + Track& operator=(Track&& other) { + --mVal.get(); + mVal = other.mVal; + ++mVal.get(); + return *this; + } + + ~Track() { --mVal.get(); } + + std::reference_wrapper<int> mVal; + }; + + int counter = 0; + { + auto o = makeOptional(Track(counter)); + EXPECT_EQ(1, counter); + } + EXPECT_EQ(0, counter); + + { + auto o = makeOptional(Track(counter)); + EXPECT_EQ(1, counter); + o.clear(); + EXPECT_EQ(0, counter); + } + EXPECT_EQ(0, counter); + + { + auto o = makeOptional(Track(counter)); + EXPECT_EQ(1, counter); + int counter2 = 0; + o.emplace(counter2); + EXPECT_EQ(0, counter); + EXPECT_EQ(1, counter2); + o = Track(counter); + EXPECT_EQ(1, counter); + EXPECT_EQ(0, counter2); + + auto o2 = o; + EXPECT_EQ(2, counter); + EXPECT_EQ(0, counter2); + } + EXPECT_EQ(0, counter); + + { + auto o = makeOptional(Track(counter)); + auto o2 = std::move(o); + EXPECT_EQ(2, counter); + o = o2; + EXPECT_EQ(2, counter); + } + EXPECT_EQ(0, counter); + + int counter2 = 0; + { + Optional<Track> o; + o.emplace(counter); + EXPECT_EQ(1, counter); + + o.emplace(counter2); + EXPECT_EQ(0, counter); + EXPECT_EQ(1, counter2); + } + EXPECT_EQ(0, counter); + EXPECT_EQ(0, counter2); + + { + Optional<Track> o; + o.emplace({&counter}); + EXPECT_EQ(1, counter); + + counter2 = 0; + o.emplace({&counter2}); + EXPECT_EQ(0, counter); + EXPECT_EQ(1, counter2); + } + EXPECT_EQ(0, counter); + EXPECT_EQ(0, counter2); +} + +} // namespace base +} // namespace astc_codec diff --git a/src/base/test/string_utils_test.cpp b/src/base/test/string_utils_test.cpp new file mode 100644 index 0000000..209da54 --- /dev/null +++ b/src/base/test/string_utils_test.cpp @@ -0,0 +1,110 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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 "src/base/string_utils.h" + +#include <gtest/gtest.h> + +#include <list> +#include <string> +#include <vector> + +namespace astc_codec { +namespace base { + +TEST(StringUtils, Split) { + std::vector<std::string> results; + + auto testFunc = [&results](std::string&& s) { + results.push_back(std::move(s)); + }; + + Split("", "abc", testFunc); + EXPECT_EQ(results.size(), 1); + + Split("abc", "", testFunc); + EXPECT_EQ(results.size(), 1); + + results.clear(); + Split("abc", "a", testFunc); + EXPECT_EQ(results.size(), 2); + EXPECT_EQ(results[0], ""); + EXPECT_EQ(results[1], "bc"); + + results.clear(); + Split("aaa", "a", testFunc); + EXPECT_EQ(4, results.size()); + EXPECT_EQ("", results[0]); + EXPECT_EQ("", results[1]); + EXPECT_EQ("", results[2]); + EXPECT_EQ("", results[3]); + + results.clear(); + Split("1a2a3a4", "a", testFunc); + EXPECT_EQ(4, results.size()); + EXPECT_EQ("1", results[0]); + EXPECT_EQ("2", results[1]); + EXPECT_EQ("3", results[2]); + EXPECT_EQ("4", results[3]); + + results.clear(); + Split("1a2aa3a4", "a", testFunc); + EXPECT_EQ(5, results.size()); + EXPECT_EQ("1", results[0]); + EXPECT_EQ("2", results[1]); + EXPECT_EQ("", results[2]); + EXPECT_EQ("3", results[3]); + EXPECT_EQ("4", results[4]); + + results.clear(); + Split("The quick brown fox jumped over the lazy dog", + " ", testFunc); + EXPECT_EQ(9, results.size()); + EXPECT_EQ("The", results[0]); + EXPECT_EQ("quick", results[1]); + EXPECT_EQ("brown", results[2]); + EXPECT_EQ("fox", results[3]); + EXPECT_EQ("jumped", results[4]); + EXPECT_EQ("over", results[5]); + EXPECT_EQ("the", results[6]); + EXPECT_EQ("lazy", results[7]); + EXPECT_EQ("dog", results[8]); + + results.clear(); + Split("a; b; c; d", "; ", testFunc); + EXPECT_EQ(4, results.size()); + EXPECT_EQ("a", results[0]); + EXPECT_EQ("b", results[1]); + EXPECT_EQ("c", results[2]); + EXPECT_EQ("d", results[3]); +} + +TEST(StringUtils, ParseInt32) { + EXPECT_EQ(ParseInt32("0", -1), 0); + EXPECT_EQ(ParseInt32("100", -1), 100); + EXPECT_EQ(ParseInt32("-100", -1), -100); + + EXPECT_EQ(ParseInt32("", -1), -1); + EXPECT_EQ(ParseInt32("a", -1), -1); + EXPECT_EQ(ParseInt32("10x1", -1), 10); + + EXPECT_EQ(ParseInt32("2147483647", -1), 2147483647); + EXPECT_EQ(ParseInt32("2147483648", -1), 2147483647); + + EXPECT_EQ(ParseInt32("-2147483648", -1), -2147483648); + EXPECT_EQ(ParseInt32("-2147483649", -1), -2147483648); +} + +} // namespace base +} // namespace astc_codec diff --git a/src/base/test/type_traits_test.cpp b/src/base/test/type_traits_test.cpp new file mode 100644 index 0000000..b858c01 --- /dev/null +++ b/src/base/test/type_traits_test.cpp @@ -0,0 +1,128 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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 "src/base/type_traits.h" + +#include <gtest/gtest.h> + +#include <array> +#include <functional> +#include <list> +#include <vector> + +namespace astc_codec { +namespace base { + +TEST(TypeTraits, IsCallable) { + class C; + C* c = nullptr; + + auto lambda = [c](bool) -> C* { return nullptr; }; + + static_assert(is_callable_as<void(), void()>::value, "simple function"); + static_assert(is_callable_as<void (&)(), void()>::value, + "function reference"); + static_assert(is_callable_as<void (*)(), void()>::value, "function pointer"); + static_assert(is_callable_as<int(C&, C*), int(C&, C*)>::value, + "function with arguments and return type"); + static_assert(is_callable_as<decltype(lambda), C*(bool)>::value, "lambda"); + static_assert(is_callable_as<std::function<bool(int)>, bool(int)>::value, + "std::function"); + + static_assert(!is_callable_as<int, void()>::value, + "int should not be callable"); + static_assert(!is_callable_as<C, void()>::value, "incomplete type"); + static_assert(!is_callable_as<void(), void(int)>::value, + "different arguments"); + static_assert(!is_callable_as<int(), void()>::value, + "different return types"); + static_assert(!is_callable_as<int(), short()>::value, + "slightly different return types"); + static_assert(!is_callable_as<int(int), int(int, int)>::value, + "more arguments"); + static_assert(!is_callable_as<int(int, int), int(int)>::value, + "less arguments"); + + static_assert(!is_callable_as<int(int), int>::value, + "bad required signature"); + + static_assert(is_callable_with_args<void(), void()>::value, + "simple function"); + static_assert(is_callable_with_args<void (&)(), void()>::value, + "function reference"); + static_assert(is_callable_with_args<void (*)(), void()>::value, + "function pointer"); + static_assert(is_callable_with_args<int(C&, C*), int(C&, C*)>::value, + "function with arguments and return type"); + static_assert(is_callable_with_args<decltype(lambda), C*(bool)>::value, + "lambda"); + static_assert( + is_callable_with_args<std::function<bool(int)>, bool(int)>::value, + "std::function"); + + static_assert(!is_callable_with_args<int, void()>::value, + "int should not be callable"); + static_assert(!is_callable_with_args<C, void()>::value, "incomplete type"); + static_assert(!is_callable_with_args<void(), void(int)>::value, + "different arguments"); + static_assert(is_callable_with_args<int(), void()>::value, + "different return types are ignored"); + static_assert(is_callable_with_args<int(), short()>::value, + "slightly different return types are ignored"); + static_assert(!is_callable_with_args<int(int), int(int, int)>::value, + "more arguments"); + static_assert(!is_callable_with_args<int(int, int), int(int)>::value, + "less arguments"); + + static_assert(!is_callable_with_args<int(int), int>::value, + "bad required signature"); +} + +TEST(TypeTraits, IsTemplateInstantiation) { + static_assert(!is_template_instantiation_of<int, std::vector>::value, + "int is not an instance of vector"); + static_assert(!is_template_instantiation_of<std::list<std::vector<int>>, + std::vector>::value, + "list is not an instance of vector"); + + static_assert( + is_template_instantiation_of<std::vector<int>, std::vector>::value, + "std::vector<int> is an instance of vector"); + static_assert( + is_template_instantiation_of<std::vector<std::vector<std::vector<int>>>, + std::vector>::value, + "nested std::vector<> is an instance of vector"); +} + +TEST(TypeTraits, IsRange) { + static_assert(is_range<std::vector<int>>::value, + "vector<> should be detected as a range"); + static_assert(is_range<const std::list<std::function<void()>>>::value, + "const list<> should be detected as a range"); + static_assert(is_range<std::array<std::vector<int>, 10>>::value, + "array<> should be detected as a range"); + char arr[100]; + static_assert(is_range<decltype(arr)>::value, + "C array should be detected as a range"); + static_assert(is_range<decltype("string")>::value, + "String literal should be detected as a range"); + + static_assert(!is_range<int>::value, "int shouldn't be a range"); + static_assert(!is_range<int*>::value, "int* shouldn't be a range"); + static_assert(!is_range<const int*>::value, + "even const int* shouldn't be a range"); +} + +} // namespace base +} // namespace astc_codec diff --git a/src/base/test/uint128_test.cpp b/src/base/test/uint128_test.cpp new file mode 100644 index 0000000..0a52244 --- /dev/null +++ b/src/base/test/uint128_test.cpp @@ -0,0 +1,140 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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 "src/base/uint128.h" + +#include <gtest/gtest.h> + +namespace astc_codec { +namespace base { + +TEST(UInt128, Equality) { + const UInt128 zero(0); + const UInt128 max64(~0ULL); + + EXPECT_EQ(zero, zero); + EXPECT_NE(zero, max64); + EXPECT_EQ(zero, UInt128(0)); + EXPECT_NE(zero, UInt128(1)); + EXPECT_EQ(max64, max64); +} + +TEST(UInt128, Shifting) { + const UInt128 max64(~0ULL); + const UInt128 upper64(~0ULL, 0); + EXPECT_EQ(upper64.HighBits(), ~0ULL); + EXPECT_EQ(upper64.LowBits(), 0); + + EXPECT_EQ(upper64 >> 64, max64); + + EXPECT_EQ(UInt128(1) << 1, UInt128(2)); + EXPECT_EQ(UInt128(0) << 0, UInt128(0)); + EXPECT_EQ(max64 << 0, max64); + EXPECT_EQ(max64 >> 0, max64); + EXPECT_EQ(upper64 << 0, upper64); + EXPECT_EQ(upper64 >> 0, upper64); + + { + const UInt128 bit63 = UInt128(1ULL << 62) << 1; + EXPECT_EQ(bit63.LowBits(), 1ULL << 63); + EXPECT_EQ(bit63.HighBits(), 0); + } + + { + const UInt128 bit64 = UInt128(1ULL << 63) << 1; + EXPECT_EQ(bit64.LowBits(), 0); + EXPECT_EQ(bit64.HighBits(), 1); + EXPECT_EQ(bit64 >> 1, UInt128(1ULL << 63)); + } + + { + const UInt128 overshift = max64 << 128; + EXPECT_EQ(overshift.HighBits(), 0); + EXPECT_EQ(overshift.LowBits(), 0); + } + + { + const UInt128 overlap = upper64 >> 32; + EXPECT_EQ(overlap.HighBits(), 0x00000000FFFFFFFF); + EXPECT_EQ(overlap.LowBits(), 0xFFFFFFFF00000000); + } + + { + const UInt128 overlap = max64 << 32; + EXPECT_EQ(overlap.HighBits(), 0x00000000FFFFFFFF); + EXPECT_EQ(overlap.LowBits(), 0xFFFFFFFF00000000); + } +} + +TEST(UInt128, LargeShift) { + const UInt128 base(0xFF); + EXPECT_EQ(base << 64, UInt128(0xFFULL, 0)); + EXPECT_EQ(base << 72, UInt128(0xFF00ULL, 0)); + EXPECT_EQ(base << 80, UInt128(0xFF0000ULL, 0)); + EXPECT_EQ(base << 88, UInt128(0xFF000000ULL, 0)); + EXPECT_EQ(base << 96, UInt128(0xFF00000000ULL, 0)); + EXPECT_EQ(base << 104, UInt128(0xFF0000000000ULL, 0)); + EXPECT_EQ(base << 112, UInt128(0xFF000000000000ULL, 0)); + EXPECT_EQ(base << 120, UInt128(0xFF00000000000000ULL, 0)); + + const UInt128 upper(0xFF00000000000000ULL, 0); + EXPECT_EQ(upper >> 64, UInt128(0, 0xFF00000000000000ULL)); + EXPECT_EQ(upper >> 72, UInt128(0, 0xFF000000000000ULL)); + EXPECT_EQ(upper >> 80, UInt128(0, 0xFF0000000000ULL)); + EXPECT_EQ(upper >> 88, UInt128(0, 0xFF00000000ULL)); + EXPECT_EQ(upper >> 96, UInt128(0, 0xFF000000ULL)); + EXPECT_EQ(upper >> 104, UInt128(0, 0xFF0000ULL)); + EXPECT_EQ(upper >> 112, UInt128(0, 0xFF00ULL)); + EXPECT_EQ(upper >> 120, UInt128(0, 0xFFULL)); +} + +TEST(UInt128, BooleanOperators) { + const UInt128 allOnes(~0ULL, ~0ULL); + EXPECT_EQ(allOnes.HighBits(), ~0ULL); + EXPECT_EQ(allOnes.LowBits(), ~0ULL); + + EXPECT_EQ(~allOnes, UInt128(0)); + EXPECT_EQ(~UInt128(0), allOnes); + + EXPECT_EQ(UInt128(0xFFFF00) & UInt128(0x00FFFF), UInt128(0x00FF00)); + EXPECT_EQ(UInt128(0xFFFF00) | UInt128(0x00FFFF), UInt128(0xFFFFFF)); + EXPECT_EQ(UInt128(0xFFFF00) ^ UInt128(0x00FFFF), UInt128(0xFF00FF)); +} + +TEST(UInt128, Addition) { + const UInt128 bit63(1ULL << 63); + + EXPECT_EQ(UInt128(1) + 1, UInt128(2)); + EXPECT_EQ(bit63 + bit63, UInt128(1) << 64); + + const UInt128 carryUp = UInt128(~0ULL) + 1; + EXPECT_EQ(carryUp.HighBits(), 1); + EXPECT_EQ(carryUp.LowBits(), 0); + + const UInt128 allOnes(~0ULL, ~0ULL); + EXPECT_EQ(allOnes + 1, UInt128(0)); +} + +TEST(UInt128, Subtraction) { + const UInt128 bit64 = UInt128(1) << 64; + EXPECT_EQ(bit64 - 1, UInt128(~0ULL)); + + EXPECT_EQ(UInt128(1) - 1, UInt128(0)); + + const UInt128 allOnes(~0ULL, ~0ULL); + EXPECT_EQ(UInt128(0) - 1, allOnes); +} + +} // namespace base +} // namespace astc_codec diff --git a/src/base/type_traits.h b/src/base/type_traits.h new file mode 100644 index 0000000..917125d --- /dev/null +++ b/src/base/type_traits.h @@ -0,0 +1,172 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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. + +#ifndef ASTC_CODEC_BASE_TYPE_TRAITS_H_ +#define ASTC_CODEC_BASE_TYPE_TRAITS_H_ + +#include <iterator> +#include <type_traits> + +namespace astc_codec { +namespace base { + +namespace details { + +// a simple helper class for SFINAE below. +template<class X = void> +struct dummy { + using type = X; +}; + +} // namespace details + +// add some convenience shortcuts for an overly complex std::enable_if syntax + +// Use 'enable_if<Predicate,Type>' instead of +// 'typename std::enable_if<Predicate::value,Type>::type' +template<class Predicate, class Type = void*> +using enable_if = typename std::enable_if<Predicate::value, Type>::type; + +// Use 'enable_if_c<BooleanFlag,Type>' instead of +// 'typename std::enable_if<BooleanFlag,Type>::type' +template<bool predicate, class Type = void*> +using enable_if_c = typename std::enable_if<predicate, Type>::type; + +// Use 'enable_if_convertible<From,To,Type>' instead of +// 'typename std::enable_if<std::is_convertible<From,To>::value, Type>::type' +template<class From, class To, class Type = void*> +using enable_if_convertible = enable_if<std::is_convertible<From, To>>; + +// ----------------------------------------------------------------------------- +// A predicate for checking if some object is callable with a specific +// signature. Examples: +// +// is_callable_as<int, void()>::value == false. +// is_callable_as<strcmp, void()>::value == false. +// is_callable_as<strcmp, int(const char*, const char*)>::value == true +// +template<class F, class Signature, class X = void> +struct is_callable_as : std::false_type {}; + +// This specialization is SFINAE-d out if template arguments can't be combined +// into a call expression F(), or if the result of that call is not |R| +template<class F, class R, class... Args> +struct is_callable_as<F, R(Args...), + typename std::enable_if<std::is_same< + typename details::dummy<decltype(std::declval<F>()( + std::declval<Args>()...))>::type, + R>::value>::type> : std::true_type {}; + +// +// A similar predicate to only check arguments of the function call and ignore +// the specified return type +// +// is_callable_as<strcmp, int(const char*, const char*)>::value == true +// is_callable_as<strcmp, void(const char*, const char*)>::value == false +// is_callable_with_args<strcmp, void(const char*, const char*)>::value == true +// +template<class F, class Signature, class X = void> +struct is_callable_with_args : std::false_type {}; + +template<class F, class R, class... Args> +struct is_callable_with_args< + F, R(Args...), + typename std::enable_if< + !std::is_same<typename details::dummy<decltype( + std::declval<F>()(std::declval<Args>()...))>::type, + F>::value>::type> : std::true_type {}; + +// ----------------------------------------------------------------------------- +// Check if a type |T| is any instantiation of a template |U|. Examples: +// +// is_template_instantiation_of<int, std::vector>::value == false +// is_template_instantiation_of< +// std::list<std::vector<int>>, std::vector>::value == false +// is_template_instantiation_of<std::vector<int>, std::vector>::value == true +// is_template_instantiation_of< +// std::vector<std::vector<int>>, std::vector>::value == true +// +template<class T, template<class...> class U> +struct is_template_instantiation_of : std::false_type {}; + +template<template<class...> class U, class... Args> +struct is_template_instantiation_of<U<Args...>, U> : std::true_type {}; +// ----------------------------------------------------------------------------- + +// +// is_range<T> - check if type |T| is a range-like type. +// +// It makes sure that expressions std::begin(t) and std::end(t) are well-formed +// and those return the same type. +// +// Note: with expression SFINAE from C++14 is_range_helper<> could be renamed to +// is_range<> with no extra code. C++11 needs an extra level of enable_if<> +// to make it work when the type isn't a range. +// + +namespace details { + +template<class T> +using is_range_helper = std::is_same< + decltype(std::begin( + std::declval<typename std::add_lvalue_reference<T>::type>())), + decltype( + std::end(std::declval<typename std::add_lvalue_reference<T>::type>()))>; + +} // namespace details + +template<class T, class = void> +struct is_range : std::false_type {}; + +template<class T> +struct is_range< + T, typename std::enable_if<details::is_range_helper<T>::value>::type> + : std::true_type {}; + +//////////////////////////////////////////////////////////////////////////////// +// +// A class to incapsulate integer sequence 0, 1, ..., <num_args> +// Seq<int...> +// Useful to pass function parameters in an array/tuple to call it later. +// + +template<int...> +struct Seq {}; + +// A 'maker' class to construct Seq<int...> given only <num_args> +// value. +// MakeSeq<N, S...> works this way, e.g. +// +// MakeSeq<2> inherits MakeSeq<2 - 1, 2 - 1> == MakeSeq<1, 1> +// MakeSeq<1, 1> : MakeSeq<1 - 1, 1 - 1, 1> == MakeSeq<0, 0, 1> +// MakeSeq<0, 0, 1> == MakeSeq<0, S...> and defines |type| = Seq<0, 1> + +template<int N, int... S> +struct MakeSeq : MakeSeq<N - 1, N - 1, S...> {}; + +template<int... S> +struct MakeSeq<0, S...> { + using type = Seq<S...>; +}; + +// +// MakeSeqT alias to quickly create Seq<...>: +// MakeSeqT<3> == Seq<0, 1, 2> +template<int... S> +using MakeSeqT = typename MakeSeq<S...>::type; + +} // namespace base +} // namespace astc_codec + +#endif // ASTC_CODEC_BASE_TYPE_TRAITS_H_ diff --git a/src/base/uint128.h b/src/base/uint128.h new file mode 100644 index 0000000..481e4ea --- /dev/null +++ b/src/base/uint128.h @@ -0,0 +1,175 @@ +// Copyright 2018 Google LLC +// +// Licensed 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 +// +// https://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. + +#ifndef ASTC_CODEC_BASE_UINT128_H_ +#define ASTC_CODEC_BASE_UINT128_H_ + +#include <cassert> +#include <cstdint> + +namespace astc_codec { +namespace base { + +class UInt128 { + public: + UInt128() = default; + UInt128(uint64_t low) : low_(low) { } + UInt128(uint64_t high, uint64_t low) : low_(low), high_(high) { } + UInt128(const UInt128& other) : low_(other.low_), high_(other.high_) { } + + uint64_t LowBits() const { return low_; } + uint64_t HighBits() const { return high_; } + + // Allow explicit casts to uint64_t. + explicit operator uint64_t() const { return low_; } + + // Copy operators. + UInt128& operator=(const UInt128& other) { + high_ = other.high_; + low_ = other.low_; + return *this; + } + + // Equality operators. + bool operator==(const UInt128& other) const { + return high_ == other.high_ && low_ == other.low_; + } + + bool operator!=(const UInt128& other) const { + return high_ != other.high_ || low_ != other.low_; + } + + // Shifting. + UInt128& operator<<=(int shift) { + high_ = shift >= 64 ? (shift >= 128 ? 0 : low_ << (shift - 64)) + : high_ << shift; + + if (shift > 0 && shift < 64) { + const uint64_t overlappingBits = low_ >> (64 - shift); + high_ |= overlappingBits; + } + + low_ = shift >= 64 ? 0 : low_ << shift; + return *this; + } + + UInt128 operator<<(int shift) const { + UInt128 result = *this; + result <<= shift; + return result; + } + + UInt128& operator>>=(int shift) { + low_ = shift >= 64 ? (shift >= 128 ? 0 : high_ >> (shift - 64)) + : low_ >> shift; + + if (shift > 0 && shift < 64) { + const uint64_t overlappingBits = high_ << (64 - shift); + low_ |= overlappingBits; + } + + high_ = shift >= 64 ? 0 : high_ >> shift; + + return *this; + } + + UInt128 operator>>(int shift) const { + UInt128 result = *this; + result >>= shift; + return result; + } + + // Binary operations. + UInt128& operator|=(const UInt128& other) { + high_ |= other.high_; + low_ |= other.low_; + return *this; + } + + UInt128 operator|(const UInt128& other) const { + UInt128 result = *this; + result |= other; + return result; + } + + UInt128& operator&=(const UInt128& other) { + high_ &= other.high_; + low_ &= other.low_; + return *this; + } + + UInt128 operator&(const UInt128& other) const { + UInt128 result = *this; + result &= other; + return result; + } + + UInt128& operator^=(const UInt128& other) { + high_ ^= other.high_; + low_ ^= other.low_; + return *this; + } + + UInt128 operator^(const UInt128& other) const { + UInt128 result = *this; + result ^= other; + return result; + } + + UInt128 operator~() const { + UInt128 result = *this; + result.high_ = ~high_; + result.low_ = ~low_; + return result; + } + + // Addition/subtraction. + UInt128& operator+=(const UInt128& other) { + const uint64_t carry = + (((low_ & other.low_) & 1) + (low_ >> 1) + (other.low_ >> 1)) >> 63; + high_ += other.high_ + carry; + low_ += other.low_; + return *this; + } + + UInt128 operator+(const UInt128& other) const { + UInt128 result = *this; + result += other; + return result; + } + + UInt128& operator-=(const UInt128& other) { + low_ -= other.low_; + const uint64_t carry = + (((low_ & other.low_) & 1) + (low_ >> 1) + (other.low_ >> 1)) >> 63; + high_ -= other.high_ + carry; + return *this; + } + + UInt128 operator-(const UInt128& other) const { + UInt128 result = *this; + result -= other; + return result; + } + + private: + // TODO(google): Different order for little endian. + uint64_t low_ = 0; + uint64_t high_ = 0; +}; + +} // namespace base +} // namespace astc_codec + +#endif // ASTC_CODEC_BASE_UINT128_H_ |