Bug 1014377 - Convert the first quarter of MFBT to Gecko style. r=froydnj.

--HG--
extra : rebase_source : b3b2da775e2c0e8a6ecbed70e7bd0c8f7af67b47
This commit is contained in:
Nicholas Nethercote 2014-05-29 22:40:33 -07:00
parent dab2c77f74
commit 0b4c7c33bf
16 changed files with 1510 additions and 1278 deletions

View File

@ -21,14 +21,14 @@ namespace mozilla {
template<typename T> template<typename T>
class AlignmentFinder class AlignmentFinder
{ {
struct Aligner struct Aligner
{ {
char c; char mChar;
T t; T mT;
}; };
public: public:
static const size_t alignment = sizeof(Aligner) - sizeof(T); static const size_t alignment = sizeof(Aligner) - sizeof(T);
}; };
#define MOZ_ALIGNOF(T) mozilla::AlignmentFinder<T>::alignment #define MOZ_ALIGNOF(T) mozilla::AlignmentFinder<T>::alignment
@ -71,31 +71,31 @@ struct AlignedElem;
template<> template<>
struct AlignedElem<1> struct AlignedElem<1>
{ {
MOZ_ALIGNED_DECL(uint8_t elem, 1); MOZ_ALIGNED_DECL(uint8_t elem, 1);
}; };
template<> template<>
struct AlignedElem<2> struct AlignedElem<2>
{ {
MOZ_ALIGNED_DECL(uint8_t elem, 2); MOZ_ALIGNED_DECL(uint8_t elem, 2);
}; };
template<> template<>
struct AlignedElem<4> struct AlignedElem<4>
{ {
MOZ_ALIGNED_DECL(uint8_t elem, 4); MOZ_ALIGNED_DECL(uint8_t elem, 4);
}; };
template<> template<>
struct AlignedElem<8> struct AlignedElem<8>
{ {
MOZ_ALIGNED_DECL(uint8_t elem, 8); MOZ_ALIGNED_DECL(uint8_t elem, 8);
}; };
template<> template<>
struct AlignedElem<16> struct AlignedElem<16>
{ {
MOZ_ALIGNED_DECL(uint8_t elem, 16); MOZ_ALIGNED_DECL(uint8_t elem, 16);
}; };
/* /*
@ -111,25 +111,27 @@ struct AlignedElem<16>
template<size_t Nbytes> template<size_t Nbytes>
struct AlignedStorage struct AlignedStorage
{ {
union U { union U
char bytes[Nbytes]; {
uint64_t _; char mBytes[Nbytes];
} u; uint64_t mDummy;
} u;
const void* addr() const { return u.bytes; } const void* addr() const { return u.mBytes; }
void* addr() { return u.bytes; } void* addr() { return u.mBytes; }
}; };
template<typename T> template<typename T>
struct AlignedStorage2 struct AlignedStorage2
{ {
union U { union U
char bytes[sizeof(T)]; {
uint64_t _; char mBytes[sizeof(T)];
} u; uint64_t mDummy;
} u;
const T* addr() const { return reinterpret_cast<const T*>(u.bytes); } const T* addr() const { return reinterpret_cast<const T*>(u.mBytes); }
T* addr() { return static_cast<T*>(static_cast<void*>(u.bytes)); } T* addr() { return static_cast<T*>(static_cast<void*>(u.mBytes)); }
}; };
} /* namespace mozilla */ } /* namespace mozilla */

View File

@ -49,14 +49,31 @@ namespace mozilla {
*/ */
class MallocAllocPolicy class MallocAllocPolicy
{ {
public: public:
void* malloc_(size_t bytes) { return malloc(bytes); } void* malloc_(size_t aBytes)
void* calloc_(size_t bytes) { return calloc(bytes, 1); } {
void* realloc_(void* p, size_t oldBytes, size_t bytes) { return realloc(p, bytes); } return malloc(aBytes);
void free_(void* p) { free(p); } }
void reportAllocOverflow() const {}
};
void* calloc_(size_t aBytes)
{
return calloc(aBytes, 1);
}
void* realloc_(void* aPtr, size_t aOldBytes, size_t aBytes)
{
return realloc(aPtr, aBytes);
}
void free_(void* aPtr)
{
free(aPtr);
}
void reportAllocOverflow() const
{
}
};
} // namespace mozilla } // namespace mozilla

View File

@ -19,31 +19,35 @@ namespace mozilla {
template<typename T, size_t Length> template<typename T, size_t Length>
class Array class Array
{ {
T arr[Length]; T mArr[Length];
public: public:
T& operator[](size_t i) { T& operator[](size_t aIndex)
MOZ_ASSERT(i < Length); {
return arr[i]; MOZ_ASSERT(aIndex < Length);
} return mArr[aIndex];
}
const T& operator[](size_t i) const { const T& operator[](size_t aIndex) const
MOZ_ASSERT(i < Length); {
return arr[i]; MOZ_ASSERT(aIndex < Length);
} return mArr[aIndex];
}
}; };
template<typename T> template<typename T>
class Array<T, 0> class Array<T, 0>
{ {
public: public:
T& operator[](size_t i) { T& operator[](size_t aIndex)
MOZ_CRASH("indexing into zero-length array"); {
} MOZ_CRASH("indexing into zero-length array");
}
const T& operator[](size_t i) const { const T& operator[](size_t aIndex) const
MOZ_CRASH("indexing into zero-length array"); {
} MOZ_CRASH("indexing into zero-length array");
}
}; };
} /* namespace mozilla */ } /* namespace mozilla */

View File

@ -23,17 +23,17 @@
namespace mozilla { namespace mozilla {
/* /*
* Safely subtract two pointers when it is known that end >= begin. This avoids * Safely subtract two pointers when it is known that aEnd >= aBegin. This
* the common compiler bug that if (size_t(end) - size_t(begin)) has the MSB * avoids the common compiler bug that if (size_t(aEnd) - size_t(aBegin)) has
* set, the unsigned subtraction followed by right shift will produce -1, or * the MSB set, the unsigned subtraction followed by right shift will produce
* size_t(-1), instead of the real difference. * -1, or size_t(-1), instead of the real difference.
*/ */
template<class T> template<class T>
MOZ_ALWAYS_INLINE size_t MOZ_ALWAYS_INLINE size_t
PointerRangeSize(T* begin, T* end) PointerRangeSize(T* aBegin, T* aEnd)
{ {
MOZ_ASSERT(end >= begin); MOZ_ASSERT(aEnd >= aBegin);
return (size_t(end) - size_t(begin)) / sizeof(T); return (size_t(aEnd) - size_t(aBegin)) / sizeof(T);
} }
/* /*
@ -44,14 +44,14 @@ PointerRangeSize(T* begin, T* end)
*/ */
template<typename T, size_t N> template<typename T, size_t N>
MOZ_CONSTEXPR size_t MOZ_CONSTEXPR size_t
ArrayLength(T (&arr)[N]) ArrayLength(T (&aArr)[N])
{ {
return N; return N;
} }
template<typename T, size_t N> template<typename T, size_t N>
MOZ_CONSTEXPR size_t MOZ_CONSTEXPR size_t
ArrayLength(const Array<T, N>& arr) ArrayLength(const Array<T, N>& aArr)
{ {
return N; return N;
} }
@ -63,23 +63,23 @@ ArrayLength(const Array<T, N>& arr)
*/ */
template<typename T, size_t N> template<typename T, size_t N>
MOZ_CONSTEXPR T* MOZ_CONSTEXPR T*
ArrayEnd(T (&arr)[N]) ArrayEnd(T (&aArr)[N])
{ {
return arr + ArrayLength(arr); return aArr + ArrayLength(aArr);
} }
template<typename T, size_t N> template<typename T, size_t N>
MOZ_CONSTEXPR T* MOZ_CONSTEXPR T*
ArrayEnd(Array<T, N>& arr) ArrayEnd(Array<T, N>& aArr)
{ {
return &arr[0] + ArrayLength(arr); return &aArr[0] + ArrayLength(aArr);
} }
template<typename T, size_t N> template<typename T, size_t N>
MOZ_CONSTEXPR const T* MOZ_CONSTEXPR const T*
ArrayEnd(const Array<T, N>& arr) ArrayEnd(const Array<T, N>& aArr)
{ {
return &arr[0] + ArrayLength(arr); return &aArr[0] + ArrayLength(aArr);
} }
namespace detail { namespace detail {

View File

@ -32,13 +32,13 @@
* number of undesired macros and symbols. * number of undesired macros and symbols.
*/ */
# ifdef __cplusplus # ifdef __cplusplus
extern "C" { extern "C" {
# endif # endif
__declspec(dllimport) int __stdcall __declspec(dllimport) int __stdcall
TerminateProcess(void* hProcess, unsigned int uExitCode); TerminateProcess(void* hProcess, unsigned int uExitCode);
__declspec(dllimport) void* __stdcall GetCurrentProcess(void); __declspec(dllimport) void* __stdcall GetCurrentProcess(void);
# ifdef __cplusplus # ifdef __cplusplus
} }
# endif # endif
#else #else
# include <signal.h> # include <signal.h>
@ -124,21 +124,23 @@ extern "C" {
#endif #endif
/* /*
* Prints |s| as an assertion failure (using file and ln as the location of the * Prints |aStr| as an assertion failure (using aFilename and aLine as the
* assertion) to the standard debug-output channel. * location of the assertion) to the standard debug-output channel.
* *
* Usually you should use MOZ_ASSERT or MOZ_CRASH instead of this method. This * Usually you should use MOZ_ASSERT or MOZ_CRASH instead of this method. This
* method is primarily for internal use in this header, and only secondarily * method is primarily for internal use in this header, and only secondarily
* for use in implementing release-build assertions. * for use in implementing release-build assertions.
*/ */
static MOZ_ALWAYS_INLINE void static MOZ_ALWAYS_INLINE void
MOZ_ReportAssertionFailure(const char* s, const char* file, int ln) MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS MOZ_ReportAssertionFailure(const char* aStr, const char* aFilename, int aLine)
MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS
{ {
#ifdef ANDROID #ifdef ANDROID
__android_log_print(ANDROID_LOG_FATAL, "MOZ_Assert", __android_log_print(ANDROID_LOG_FATAL, "MOZ_Assert",
"Assertion failure: %s, at %s:%d\n", s, file, ln); "Assertion failure: %s, at %s:%d\n",
aStr, aFilename, aLine);
#else #else
fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); fprintf(stderr, "Assertion failure: %s, at %s:%d\n", aStr, aFilename, aLine);
#ifdef MOZ_DUMP_ASSERTION_STACK #ifdef MOZ_DUMP_ASSERTION_STACK
nsTraceRefcnt::WalkTheStack(stderr); nsTraceRefcnt::WalkTheStack(stderr);
#endif #endif
@ -147,13 +149,13 @@ MOZ_ReportAssertionFailure(const char* s, const char* file, int ln) MOZ_PRETEND_
} }
static MOZ_ALWAYS_INLINE void static MOZ_ALWAYS_INLINE void
MOZ_ReportCrash(const char* s, const char* file, int ln) MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS MOZ_ReportCrash(const char* aStr, const char* aFilename, int aLine) MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS
{ {
#ifdef ANDROID #ifdef ANDROID
__android_log_print(ANDROID_LOG_FATAL, "MOZ_CRASH", __android_log_print(ANDROID_LOG_FATAL, "MOZ_CRASH",
"Hit MOZ_CRASH(%s) at %s:%d\n", s, file, ln); "Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine);
#else #else
fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", s, file, ln); fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine);
#ifdef MOZ_DUMP_ASSERTION_STACK #ifdef MOZ_DUMP_ASSERTION_STACK
nsTraceRefcnt::WalkTheStack(stderr); nsTraceRefcnt::WalkTheStack(stderr);
#endif #endif
@ -319,13 +321,13 @@ namespace detail {
template<typename T> template<typename T>
struct IsFunction struct IsFunction
{ {
static const bool value = false; static const bool value = false;
}; };
template<typename R, typename... A> template<typename R, typename... A>
struct IsFunction<R(A...)> struct IsFunction<R(A...)>
{ {
static const bool value = true; static const bool value = true;
}; };
template<typename T> template<typename T>
@ -333,47 +335,51 @@ void ValidateAssertConditionType()
{ {
typedef typename RemoveReference<T>::Type ValueT; typedef typename RemoveReference<T>::Type ValueT;
static_assert(!IsArray<ValueT>::value, static_assert(!IsArray<ValueT>::value,
"Expected boolean assertion condition, got an array or a string!"); "Expected boolean assertion condition, got an array or a "
"string!");
static_assert(!IsFunction<ValueT>::value, static_assert(!IsFunction<ValueT>::value,
"Expected boolean assertion condition, got a function! Did you intend to call that function?"); "Expected boolean assertion condition, got a function! Did "
"you intend to call that function?");
static_assert(!IsFloatingPoint<ValueT>::value, static_assert(!IsFloatingPoint<ValueT>::value,
"It's often a bad idea to assert that a floating-point number is nonzero, " "It's often a bad idea to assert that a floating-point number "
"because such assertions tend to intermittently fail. Shouldn't your code gracefully handle " "is nonzero, because such assertions tend to intermittently "
"this case instead of asserting? Anyway, if you really want to " "fail. Shouldn't your code gracefully handle this case instead "
"do that, write an explicit boolean condition, like !!x or x!=0."); "of asserting? Anyway, if you really want to do that, write an "
"explicit boolean condition, like !!x or x!=0.");
} }
} // namespace detail } // namespace detail
} // namespace mozilla } // namespace mozilla
# define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x) mozilla::detail::ValidateAssertConditionType<decltype(x)>() # define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x) \
mozilla::detail::ValidateAssertConditionType<decltype(x)>()
#else #else
# define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x) # define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x)
#endif #endif
/* First the single-argument form. */ /* First the single-argument form. */
#define MOZ_ASSERT_HELPER1(expr) \ #define MOZ_ASSERT_HELPER1(expr) \
do { \ do { \
MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \ MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
if (MOZ_UNLIKELY(!(expr))) { \ if (MOZ_UNLIKELY(!(expr))) { \
MOZ_ReportAssertionFailure(#expr, __FILE__, __LINE__); \ MOZ_ReportAssertionFailure(#expr, __FILE__, __LINE__); \
MOZ_REALLY_CRASH(); \ MOZ_REALLY_CRASH(); \
} \ } \
} while (0) } while (0)
/* Now the two-argument form. */ /* Now the two-argument form. */
#define MOZ_ASSERT_HELPER2(expr, explain) \ #define MOZ_ASSERT_HELPER2(expr, explain) \
do { \ do { \
MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \ MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
if (MOZ_UNLIKELY(!(expr))) { \ if (MOZ_UNLIKELY(!(expr))) { \
MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \ MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \
MOZ_REALLY_CRASH(); \ MOZ_REALLY_CRASH(); \
} \ } \
} while (0) } while (0)
#define MOZ_RELEASE_ASSERT_GLUE(a, b) a b #define MOZ_RELEASE_ASSERT_GLUE(a, b) a b
#define MOZ_RELEASE_ASSERT(...) \ #define MOZ_RELEASE_ASSERT(...) \
MOZ_RELEASE_ASSERT_GLUE( \ MOZ_RELEASE_ASSERT_GLUE( \
MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \ MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \
(__VA_ARGS__)) (__VA_ARGS__))
#ifdef DEBUG #ifdef DEBUG
# define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__) # define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__)
@ -411,10 +417,11 @@ void ValidateAssertConditionType()
#endif #endif
/* /*
* MOZ_ASSUME_UNREACHABLE_MARKER() expands to an expression which states that it is * MOZ_ASSUME_UNREACHABLE_MARKER() expands to an expression which states that
* undefined behavior for execution to reach this point. No guarantees are made * it is undefined behavior for execution to reach this point. No guarantees
* about what will happen if this is reached at runtime. Most code should use * are made about what will happen if this is reached at runtime. Most code
* MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE because it has extra asserts. * should use MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE because it has extra
* asserts.
*/ */
#if defined(__clang__) #if defined(__clang__)
# define MOZ_ASSUME_UNREACHABLE_MARKER() __builtin_unreachable() # define MOZ_ASSUME_UNREACHABLE_MARKER() __builtin_unreachable()

File diff suppressed because it is too large Load Diff

View File

@ -150,10 +150,10 @@
* template<typename T> * template<typename T>
* class Ptr * class Ptr
* { * {
* T* ptr; * T* ptr;
* MOZ_EXPLICIT_CONVERSION operator bool() const { * MOZ_EXPLICIT_CONVERSION operator bool() const {
* return ptr != nullptr; * return ptr != nullptr;
* } * }
* }; * };
* *
*/ */
@ -267,9 +267,9 @@
* *
* struct NonCopyable * struct NonCopyable
* { * {
* private: * private:
* NonCopyable(const NonCopyable& other) MOZ_DELETE; * NonCopyable(const NonCopyable& other) MOZ_DELETE;
* void operator=(const NonCopyable& other) MOZ_DELETE; * void operator=(const NonCopyable& other) MOZ_DELETE;
* }; * };
* *
* If MOZ_DELETE can't be implemented for the current compiler, use of the * If MOZ_DELETE can't be implemented for the current compiler, use of the
@ -295,23 +295,23 @@
* *
* class Base * class Base
* { * {
* public: * public:
* virtual void f() = 0; * virtual void f() = 0;
* }; * };
* class Derived1 : public Base * class Derived1 : public Base
* { * {
* public: * public:
* virtual void f() MOZ_OVERRIDE; * virtual void f() MOZ_OVERRIDE;
* }; * };
* class Derived2 : public Base * class Derived2 : public Base
* { * {
* public: * public:
* virtual void f() MOZ_OVERRIDE = 0; * virtual void f() MOZ_OVERRIDE = 0;
* }; * };
* class Derived3 : public Base * class Derived3 : public Base
* { * {
* public: * public:
* virtual void f() MOZ_OVERRIDE { } * virtual void f() MOZ_OVERRIDE { }
* }; * };
* *
* In compilers supporting C++11 override controls, MOZ_OVERRIDE *requires* that * In compilers supporting C++11 override controls, MOZ_OVERRIDE *requires* that
@ -339,16 +339,16 @@
* *
* class Base MOZ_FINAL * class Base MOZ_FINAL
* { * {
* public: * public:
* Base(); * Base();
* ~Base(); * ~Base();
* virtual void f() { } * virtual void f() { }
* }; * };
* // This will be an error in some compilers: * // This will be an error in some compilers:
* class Derived : public Base * class Derived : public Base
* { * {
* public: * public:
* ~Derived() { } * ~Derived() { }
* }; * };
* *
* One particularly common reason to specify MOZ_FINAL upon a class is to tell * One particularly common reason to specify MOZ_FINAL upon a class is to tell
@ -375,14 +375,14 @@
* *
* class Base * class Base
* { * {
* public: * public:
* virtual void f() MOZ_FINAL; * virtual void f() MOZ_FINAL;
* }; * };
* class Derived * class Derived
* { * {
* public: * public:
* // This will be an error in some compilers: * // This will be an error in some compilers:
* virtual void f(); * virtual void f();
* }; * };
* *
* In compilers implementing final controls, it is an error for a derived class * In compilers implementing final controls, it is an error for a derived class

View File

@ -14,12 +14,12 @@
namespace mozilla { namespace mozilla {
/* /*
* The algorithm searches the given container 'c' over the sorted index range * The algorithm searches the given container |aContainer| over the sorted
* [begin, end) for an index 'i' where 'c[i] == target'. If such an index 'i' is * index range [aBegin, aEnd) for an index |i| where |aContainer[i] == aTarget|.
* found, BinarySearch returns 'true' and the index is returned via the outparam * If such an index |i| is found, BinarySearch returns |true| and the index is
* 'matchOrInsertionPoint'. If no index is found, BinarySearch returns 'false' * returned via the outparam |aMatchOrInsertionPoint|. If no index is found,
* and the outparam returns the first index in [begin, end] where 'target' can * BinarySearch returns |false| and the outparam returns the first index in
* be inserted to maintain sorted order. * [aBegin, aEnd] where |aTarget| can be inserted to maintain sorted order.
* *
* Example: * Example:
* *
@ -32,32 +32,34 @@ namespace mozilla {
template <typename Container, typename T> template <typename Container, typename T>
bool bool
BinarySearch(const Container &c, size_t begin, size_t end, T target, size_t *matchOrInsertionPoint) BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd,
T aTarget, size_t* aMatchOrInsertionPoint)
{ {
MOZ_ASSERT(begin <= end); MOZ_ASSERT(aBegin <= aEnd);
size_t low = begin; size_t low = aBegin;
size_t high = end; size_t high = aEnd;
while (low != high) { while (low != high) {
size_t middle = low + (high - low) / 2; size_t middle = low + (high - low) / 2;
const T &middleValue = c[middle]; const T& middleValue = aContainer[middle];
MOZ_ASSERT(c[low] <= c[middle]); MOZ_ASSERT(aContainer[low] <= aContainer[middle]);
MOZ_ASSERT(c[middle] <= c[high - 1]); MOZ_ASSERT(aContainer[middle] <= aContainer[high - 1]);
MOZ_ASSERT(c[low] <= c[high - 1]); MOZ_ASSERT(aContainer[low] <= aContainer[high - 1]);
if (target == middleValue) { if (aTarget == middleValue) {
*matchOrInsertionPoint = middle; *aMatchOrInsertionPoint = middle;
return true; return true;
} }
if (target < middleValue) if (aTarget < middleValue) {
high = middle; high = middle;
else } else {
low = middle + 1; low = middle + 1;
}
} }
*matchOrInsertionPoint = low; *aMatchOrInsertionPoint = low;
return false; return false;
} }

View File

@ -55,177 +55,199 @@ namespace mozilla {
template<unsigned KeySize, class T> template<unsigned KeySize, class T>
class BloomFilter class BloomFilter
{ {
/* /*
* A counting Bloom filter with 8-bit counters. For now we assume * A counting Bloom filter with 8-bit counters. For now we assume
* that having two hash functions is enough, but we may revisit that * that having two hash functions is enough, but we may revisit that
* decision later. * decision later.
* *
* The filter uses an array with 2**KeySize entries. * The filter uses an array with 2**KeySize entries.
* *
* Assuming a well-distributed hash function, a Bloom filter with * Assuming a well-distributed hash function, a Bloom filter with
* array size M containing N elements and * array size M containing N elements and
* using k hash function has expected false positive rate exactly * using k hash function has expected false positive rate exactly
* *
* $ (1 - (1 - 1/M)^{kN})^k $ * $ (1 - (1 - 1/M)^{kN})^k $
* *
* because each array slot has a * because each array slot has a
* *
* $ (1 - 1/M)^{kN} $ * $ (1 - 1/M)^{kN} $
* *
* chance of being 0, and the expected false positive rate is the * chance of being 0, and the expected false positive rate is the
* probability that all of the k hash functions will hit a nonzero * probability that all of the k hash functions will hit a nonzero
* slot. * slot.
* *
* For reasonable assumptions (M large, kN large, which should both * For reasonable assumptions (M large, kN large, which should both
* hold if we're worried about false positives) about M and kN this * hold if we're worried about false positives) about M and kN this
* becomes approximately * becomes approximately
* *
* $$ (1 - \exp(-kN/M))^k $$ * $$ (1 - \exp(-kN/M))^k $$
* *
* For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$, * For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
* or in other words * or in other words
* *
* $$ N/M = -0.5 * \ln(1 - \sqrt(r)) $$ * $$ N/M = -0.5 * \ln(1 - \sqrt(r)) $$
* *
* where r is the false positive rate. This can be used to compute * where r is the false positive rate. This can be used to compute
* the desired KeySize for a given load N and false positive rate r. * the desired KeySize for a given load N and false positive rate r.
* *
* If N/M is assumed small, then the false positive rate can * If N/M is assumed small, then the false positive rate can
* further be approximated as 4*N^2/M^2. So increasing KeySize by * further be approximated as 4*N^2/M^2. So increasing KeySize by
* 1, which doubles M, reduces the false positive rate by about a * 1, which doubles M, reduces the false positive rate by about a
* factor of 4, and a false positive rate of 1% corresponds to * factor of 4, and a false positive rate of 1% corresponds to
* about M/N == 20. * about M/N == 20.
* *
* What this means in practice is that for a few hundred keys using a * What this means in practice is that for a few hundred keys using a
* KeySize of 12 gives false positive rates on the order of 0.25-4%. * KeySize of 12 gives false positive rates on the order of 0.25-4%.
* *
* Similarly, using a KeySize of 10 would lead to a 4% false * Similarly, using a KeySize of 10 would lead to a 4% false
* positive rate for N == 100 and to quite bad false positive * positive rate for N == 100 and to quite bad false positive
* rates for larger N. * rates for larger N.
*/ */
public: public:
BloomFilter() { BloomFilter()
static_assert(KeySize <= keyShift, "KeySize too big"); {
static_assert(KeySize <= kKeyShift, "KeySize too big");
// Should we have a custom operator new using calloc instead and // Should we have a custom operator new using calloc instead and
// require that we're allocated via the operator? // require that we're allocated via the operator?
clear(); clear();
} }
/* /*
* Clear the filter. This should be done before reusing it, because * Clear the filter. This should be done before reusing it, because
* just removing all items doesn't clear counters that hit the upper * just removing all items doesn't clear counters that hit the upper
* bound. * bound.
*/ */
void clear(); void clear();
/* /*
* Add an item to the filter. * Add an item to the filter.
*/ */
void add(const T* t); void add(const T* aValue);
/* /*
* Remove an item from the filter. * Remove an item from the filter.
*/ */
void remove(const T* t); void remove(const T* aValue);
/* /*
* Check whether the filter might contain an item. This can * Check whether the filter might contain an item. This can
* sometimes return true even if the item is not in the filter, * sometimes return true even if the item is not in the filter,
* but will never return false for items that are actually in the * but will never return false for items that are actually in the
* filter. * filter.
*/ */
bool mightContain(const T* t) const; bool mightContain(const T* aValue) const;
/* /*
* Methods for add/remove/contain when we already have a hash computed * Methods for add/remove/contain when we already have a hash computed
*/ */
void add(uint32_t hash); void add(uint32_t aHash);
void remove(uint32_t hash); void remove(uint32_t aHash);
bool mightContain(uint32_t hash) const; bool mightContain(uint32_t aHash) const;
private: private:
static const size_t arraySize = (1 << KeySize); static const size_t kArraySize = (1 << KeySize);
static const uint32_t keyMask = (1 << KeySize) - 1; static const uint32_t kKeyMask = (1 << KeySize) - 1;
static const uint32_t keyShift = 16; static const uint32_t kKeyShift = 16;
static uint32_t hash1(uint32_t hash) { return hash & keyMask; } static uint32_t hash1(uint32_t aHash)
static uint32_t hash2(uint32_t hash) { return (hash >> keyShift) & keyMask; } {
return aHash & kKeyMask;
}
static uint32_t hash2(uint32_t aHash)
{
return (aHash >> kKeyShift) & kKeyMask;
}
uint8_t& firstSlot(uint32_t hash) { return counters[hash1(hash)]; } uint8_t& firstSlot(uint32_t aHash)
uint8_t& secondSlot(uint32_t hash) { return counters[hash2(hash)]; } {
const uint8_t& firstSlot(uint32_t hash) const { return counters[hash1(hash)]; } return mCounters[hash1(aHash)];
const uint8_t& secondSlot(uint32_t hash) const { return counters[hash2(hash)]; } }
uint8_t& secondSlot(uint32_t aHash)
{
return mCounters[hash2(aHash)];
}
static bool full(const uint8_t& slot) { return slot == UINT8_MAX; } const uint8_t& firstSlot(uint32_t aHash) const
{
return mCounters[hash1(aHash)];
}
const uint8_t& secondSlot(uint32_t aHash) const
{
return mCounters[hash2(aHash)];
}
uint8_t counters[arraySize]; static bool full(const uint8_t& aSlot) { return aSlot == UINT8_MAX; }
uint8_t mCounters[kArraySize];
}; };
template<unsigned KeySize, class T> template<unsigned KeySize, class T>
inline void inline void
BloomFilter<KeySize, T>::clear() BloomFilter<KeySize, T>::clear()
{ {
memset(counters, 0, arraySize); memset(mCounters, 0, kArraySize);
} }
template<unsigned KeySize, class T> template<unsigned KeySize, class T>
inline void inline void
BloomFilter<KeySize, T>::add(uint32_t hash) BloomFilter<KeySize, T>::add(uint32_t aHash)
{ {
uint8_t& slot1 = firstSlot(hash); uint8_t& slot1 = firstSlot(aHash);
if (MOZ_LIKELY(!full(slot1))) if (MOZ_LIKELY(!full(slot1))) {
++slot1; ++slot1;
}
uint8_t& slot2 = secondSlot(hash); uint8_t& slot2 = secondSlot(aHash); {
if (MOZ_LIKELY(!full(slot2))) if (MOZ_LIKELY(!full(slot2)))
++slot2; ++slot2;
}
} }
template<unsigned KeySize, class T> template<unsigned KeySize, class T>
MOZ_ALWAYS_INLINE void MOZ_ALWAYS_INLINE void
BloomFilter<KeySize, T>::add(const T* t) BloomFilter<KeySize, T>::add(const T* aValue)
{ {
uint32_t hash = t->hash(); uint32_t hash = aValue->hash();
return add(hash); return add(hash);
} }
template<unsigned KeySize, class T> template<unsigned KeySize, class T>
inline void inline void
BloomFilter<KeySize, T>::remove(uint32_t hash) BloomFilter<KeySize, T>::remove(uint32_t aHash)
{ {
// If the slots are full, we don't know whether we bumped them to be // If the slots are full, we don't know whether we bumped them to be
// there when we added or not, so just leave them full. // there when we added or not, so just leave them full.
uint8_t& slot1 = firstSlot(hash); uint8_t& slot1 = firstSlot(aHash);
if (MOZ_LIKELY(!full(slot1))) if (MOZ_LIKELY(!full(slot1))) {
--slot1; --slot1;
}
uint8_t& slot2 = secondSlot(hash); uint8_t& slot2 = secondSlot(aHash);
if (MOZ_LIKELY(!full(slot2))) if (MOZ_LIKELY(!full(slot2))) {
--slot2; --slot2;
}
} }
template<unsigned KeySize, class T> template<unsigned KeySize, class T>
MOZ_ALWAYS_INLINE void MOZ_ALWAYS_INLINE void
BloomFilter<KeySize, T>::remove(const T* t) BloomFilter<KeySize, T>::remove(const T* aValue)
{ {
uint32_t hash = t->hash(); uint32_t hash = aValue->hash();
remove(hash); remove(hash);
} }
template<unsigned KeySize, class T> template<unsigned KeySize, class T>
MOZ_ALWAYS_INLINE bool MOZ_ALWAYS_INLINE bool
BloomFilter<KeySize, T>::mightContain(uint32_t hash) const BloomFilter<KeySize, T>::mightContain(uint32_t aHash) const
{ {
// Check that all the slots for this hash contain something // Check that all the slots for this hash contain something
return firstSlot(hash) && secondSlot(hash); return firstSlot(aHash) && secondSlot(aHash);
} }
template<unsigned KeySize, class T> template<unsigned KeySize, class T>
MOZ_ALWAYS_INLINE bool MOZ_ALWAYS_INLINE bool
BloomFilter<KeySize, T>::mightContain(const T* t) const BloomFilter<KeySize, T>::mightContain(const T* aValue) const
{ {
uint32_t hash = t->hash(); uint32_t hash = aValue->hash();
return mightContain(hash); return mightContain(hash);
} }

View File

@ -17,7 +17,8 @@
namespace mozilla { namespace mozilla {
/** /**
* Return a value of type |To|, containing the underlying bit pattern of |from|. * Return a value of type |To|, containing the underlying bit pattern of
* |aFrom|.
* *
* |To| and |From| must be types of the same size; be careful of cross-platform * |To| and |From| must be types of the same size; be careful of cross-platform
* size differences, or this might fail to compile on some but not all * size differences, or this might fail to compile on some but not all
@ -25,16 +26,17 @@ namespace mozilla {
*/ */
template<typename To, typename From> template<typename To, typename From>
inline To inline To
BitwiseCast(const From from) BitwiseCast(const From aFrom)
{ {
static_assert(sizeof(From) == sizeof(To), static_assert(sizeof(From) == sizeof(To),
"To and From must have the same size"); "To and From must have the same size");
union { union
From from; {
To to; From mFrom;
To mTo;
} u; } u;
u.from = from; u.mFrom = aFrom;
return u.to; return u.mTo;
} }
namespace detail { namespace detail {
@ -58,34 +60,39 @@ enum UUComparison { FromIsBigger, FromIsNotBigger };
// Unsigned-to-unsigned range check // Unsigned-to-unsigned range check
template<typename From, typename To, template<typename From, typename To,
UUComparison = (sizeof(From) > sizeof(To)) ? FromIsBigger : FromIsNotBigger> UUComparison = (sizeof(From) > sizeof(To))
? FromIsBigger
: FromIsNotBigger>
struct UnsignedUnsignedCheck; struct UnsignedUnsignedCheck;
template<typename From, typename To> template<typename From, typename To>
struct UnsignedUnsignedCheck<From, To, FromIsBigger> struct UnsignedUnsignedCheck<From, To, FromIsBigger>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
return from <= From(To(-1)); {
} return aFrom <= From(To(-1));
}
}; };
template<typename From, typename To> template<typename From, typename To>
struct UnsignedUnsignedCheck<From, To, FromIsNotBigger> struct UnsignedUnsignedCheck<From, To, FromIsNotBigger>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
return true; {
} return true;
}
}; };
template<typename From, typename To> template<typename From, typename To>
struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned> struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
return UnsignedUnsignedCheck<From, To>::checkBounds(from); {
} return UnsignedUnsignedCheck<From, To>::checkBounds(aFrom);
}
}; };
// Signed-to-unsigned range check // Signed-to-unsigned range check
@ -93,14 +100,17 @@ struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned>
template<typename From, typename To> template<typename From, typename To>
struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned> struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
if (from < 0) {
return false; if (aFrom < 0) {
if (sizeof(To) >= sizeof(From)) return false;
return true;
return from <= From(To(-1));
} }
if (sizeof(To) >= sizeof(From)) {
return true;
}
return aFrom <= From(To(-1));
}
}; };
// Unsigned-to-signed range check // Unsigned-to-signed range check
@ -108,35 +118,40 @@ struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned>
enum USComparison { FromIsSmaller, FromIsNotSmaller }; enum USComparison { FromIsSmaller, FromIsNotSmaller };
template<typename From, typename To, template<typename From, typename To,
USComparison = (sizeof(From) < sizeof(To)) ? FromIsSmaller : FromIsNotSmaller> USComparison = (sizeof(From) < sizeof(To))
? FromIsSmaller
: FromIsNotSmaller>
struct UnsignedSignedCheck; struct UnsignedSignedCheck;
template<typename From, typename To> template<typename From, typename To>
struct UnsignedSignedCheck<From, To, FromIsSmaller> struct UnsignedSignedCheck<From, To, FromIsSmaller>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
return true; {
} return true;
}
}; };
template<typename From, typename To> template<typename From, typename To>
struct UnsignedSignedCheck<From, To, FromIsNotSmaller> struct UnsignedSignedCheck<From, To, FromIsNotSmaller>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1); {
return from <= From(MaxValue); const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
} return aFrom <= From(MaxValue);
}
}; };
template<typename From, typename To> template<typename From, typename To>
struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned> struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
return UnsignedSignedCheck<From, To>::checkBounds(from); {
} return UnsignedSignedCheck<From, To>::checkBounds(aFrom);
}
}; };
// Signed-to-signed range check // Signed-to-signed range check
@ -144,42 +159,46 @@ struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned>
template<typename From, typename To> template<typename From, typename To>
struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned> struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
if (sizeof(From) <= sizeof(To)) {
return true; if (sizeof(From) <= sizeof(To)) {
const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1); return true;
const To MinValue = -MaxValue - To(1);
return From(MinValue) <= from &&
From(from) <= From(MaxValue);
} }
const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
const To MinValue = -MaxValue - To(1);
return From(MinValue) <= aFrom &&
From(aFrom) <= From(MaxValue);
}
}; };
template<typename From, typename To, template<typename From, typename To,
bool TypesAreIntegral = IsIntegral<From>::value && IsIntegral<To>::value> bool TypesAreIntegral = IsIntegral<From>::value &&
IsIntegral<To>::value>
class BoundsChecker; class BoundsChecker;
template<typename From> template<typename From>
class BoundsChecker<From, From, true> class BoundsChecker<From, From, true>
{ {
public: public:
static bool checkBounds(const From from) { return true; } static bool checkBounds(const From aFrom) { return true; }
}; };
template<typename From, typename To> template<typename From, typename To>
class BoundsChecker<From, To, true> class BoundsChecker<From, To, true>
{ {
public: public:
static bool checkBounds(const From from) { static bool checkBounds(const From aFrom)
return BoundsCheckImpl<From, To>::checkBounds(from); {
} return BoundsCheckImpl<From, To>::checkBounds(aFrom);
}
}; };
template<typename From, typename To> template<typename From, typename To>
inline bool inline bool
IsInBounds(const From from) IsInBounds(const From aFrom)
{ {
return BoundsChecker<From, To>::checkBounds(from); return BoundsChecker<From, To>::checkBounds(aFrom);
} }
} // namespace detail } // namespace detail
@ -191,10 +210,10 @@ IsInBounds(const From from)
*/ */
template<typename To, typename From> template<typename To, typename From>
inline To inline To
SafeCast(const From from) SafeCast(const From aFrom)
{ {
MOZ_ASSERT((detail::IsInBounds<From, To>(from))); MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
return static_cast<To>(from); return static_cast<To>(aFrom);
} }
} // namespace mozilla } // namespace mozilla

View File

@ -19,21 +19,21 @@ namespace mozilla {
*/ */
class ChaosMode class ChaosMode
{ {
public: public:
static bool isActive() static bool isActive()
{ {
// Flip this to true to activate chaos mode // Flip this to true to activate chaos mode
return false; return false;
} }
/** /**
* Returns a somewhat (but not uniformly) random uint32_t < aBound. * Returns a somewhat (but not uniformly) random uint32_t < aBound.
* Not to be used for anything except ChaosMode, since it's not very random. * Not to be used for anything except ChaosMode, since it's not very random.
*/ */
static uint32_t randomUint32LessThan(uint32_t aBound) static uint32_t randomUint32LessThan(uint32_t aBound)
{ {
return uint32_t(rand()) % aBound; return uint32_t(rand()) % aBound;
} }
}; };
} /* namespace mozilla */ } /* namespace mozilla */

View File

@ -32,8 +32,8 @@
*/ */
# define MOZ_UTF16_HELPER(s) L##s # define MOZ_UTF16_HELPER(s) L##s
# define _CHAR16T # define _CHAR16T
typedef wchar_t char16_t; typedef wchar_t char16_t;
typedef unsigned int char32_t; typedef unsigned int char32_t;
#else #else
/* C++11 has a builtin char16_t type. */ /* C++11 has a builtin char16_t type. */
# define MOZ_UTF16_HELPER(s) u##s # define MOZ_UTF16_HELPER(s) u##s
@ -61,86 +61,108 @@
*/ */
class char16ptr_t class char16ptr_t
{ {
private: private:
const char16_t* ptr; const char16_t* mPtr;
static_assert(sizeof(char16_t) == sizeof(wchar_t), "char16_t and wchar_t sizes differ"); static_assert(sizeof(char16_t) == sizeof(wchar_t),
"char16_t and wchar_t sizes differ");
public: public:
char16ptr_t(const char16_t* p) : ptr(p) {} char16ptr_t(const char16_t* aPtr) : mPtr(aPtr) {}
char16ptr_t(const wchar_t* p) : ptr(reinterpret_cast<const char16_t*>(p)) {} char16ptr_t(const wchar_t* aPtr) :
mPtr(reinterpret_cast<const char16_t*>(aPtr))
{}
/* Without this, nullptr assignment would be ambiguous. */ /* Without this, nullptr assignment would be ambiguous. */
constexpr char16ptr_t(decltype(nullptr)) : ptr(nullptr) {} constexpr char16ptr_t(decltype(nullptr)) : mPtr(nullptr) {}
operator const char16_t*() const { operator const char16_t*() const
return ptr; {
} return mPtr;
operator const wchar_t*() const { }
return reinterpret_cast<const wchar_t*>(ptr); operator const wchar_t*() const
} {
operator const void*() const { return reinterpret_cast<const wchar_t*>(mPtr);
return ptr; }
} operator const void*() const
operator bool() const { {
return ptr != nullptr; return mPtr;
} }
operator std::wstring() const { operator bool() const
return std::wstring(static_cast<const wchar_t*>(*this)); {
} return mPtr != nullptr;
}
operator std::wstring() const
{
return std::wstring(static_cast<const wchar_t*>(*this));
}
/* Explicit cast operators to allow things like (char16_t*)str. */ /* Explicit cast operators to allow things like (char16_t*)str. */
explicit operator char16_t*() const { explicit operator char16_t*() const
return const_cast<char16_t*>(ptr); {
} return const_cast<char16_t*>(mPtr);
explicit operator wchar_t*() const { }
return const_cast<wchar_t*>(static_cast<const wchar_t*>(*this)); explicit operator wchar_t*() const
} {
return const_cast<wchar_t*>(static_cast<const wchar_t*>(*this));
}
/** /**
* Some Windows API calls accept BYTE* but require that data actually be WCHAR*. * Some Windows API calls accept BYTE* but require that data actually be WCHAR*.
* Supporting this requires explicit operators to support the requisite explicit * Supporting this requires explicit operators to support the requisite explicit
* casts. * casts.
*/ */
explicit operator const char*() const { explicit operator const char*() const
return reinterpret_cast<const char*>(ptr); {
} return reinterpret_cast<const char*>(mPtr);
explicit operator const unsigned char*() const { }
return reinterpret_cast<const unsigned char*>(ptr); explicit operator const unsigned char*() const
} {
explicit operator unsigned char*() const { return reinterpret_cast<const unsigned char*>(mPtr);
return const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(ptr)); }
} explicit operator unsigned char*() const
explicit operator void*() const { {
return const_cast<char16_t*>(ptr); return const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(mPtr));
} }
explicit operator void*() const
{
return const_cast<char16_t*>(mPtr);
}
/* Some operators used on pointers. */ /* Some operators used on pointers. */
char16_t operator[](size_t i) const { char16_t operator[](size_t aIndex) const
return ptr[i]; {
} return mPtr[aIndex];
bool operator==(const char16ptr_t &x) const { }
return ptr == x.ptr; bool operator==(const char16ptr_t &aOther) const
} {
bool operator==(decltype(nullptr)) const { return mPtr == aOther.mPtr;
return ptr == nullptr; }
} bool operator==(decltype(nullptr)) const
bool operator!=(const char16ptr_t &x) const { {
return ptr != x.ptr; return mPtr == nullptr;
} }
bool operator!=(decltype(nullptr)) const { bool operator!=(const char16ptr_t &aOther) const
return ptr != nullptr; {
} return mPtr != aOther.mPtr;
char16ptr_t operator+(size_t add) const { }
return char16ptr_t(ptr + add); bool operator!=(decltype(nullptr)) const
} {
ptrdiff_t operator-(const char16ptr_t &other) const { return mPtr != nullptr;
return ptr - other.ptr; }
} char16ptr_t operator+(size_t aValue) const
{
return char16ptr_t(mPtr + aValue);
}
ptrdiff_t operator-(const char16ptr_t &aOther) const
{
return mPtr - aOther.mPtr;
}
}; };
inline decltype((char*)0-(char*)0) inline decltype((char*)0-(char*)0)
operator-(const char16_t* x, const char16ptr_t y) { operator-(const char16_t* aX, const char16ptr_t aY)
return x - static_cast<const char16_t*>(y); {
return aX - static_cast<const char16_t*>(aY);
} }
#else #else

View File

@ -35,13 +35,13 @@ struct UnsupportedType {};
template<typename IntegerType> template<typename IntegerType>
struct IsSupportedPass2 struct IsSupportedPass2
{ {
static const bool value = false; static const bool value = false;
}; };
template<typename IntegerType> template<typename IntegerType>
struct IsSupported struct IsSupported
{ {
static const bool value = IsSupportedPass2<IntegerType>::value; static const bool value = IsSupportedPass2<IntegerType>::value;
}; };
template<> template<>
@ -130,36 +130,37 @@ struct IsSupportedPass2<unsigned long long>
template<typename IntegerType, size_t Size = sizeof(IntegerType)> template<typename IntegerType, size_t Size = sizeof(IntegerType)>
struct TwiceBiggerType struct TwiceBiggerType
{ {
typedef typename detail::StdintTypeForSizeAndSignedness< typedef typename detail::StdintTypeForSizeAndSignedness<
sizeof(IntegerType) * 2, sizeof(IntegerType) * 2,
IsSigned<IntegerType>::value IsSigned<IntegerType>::value
>::Type Type; >::Type Type;
}; };
template<typename IntegerType> template<typename IntegerType>
struct TwiceBiggerType<IntegerType, 8> struct TwiceBiggerType<IntegerType, 8>
{ {
typedef UnsupportedType Type; typedef UnsupportedType Type;
}; };
template<typename T> template<typename T>
inline bool inline bool
HasSignBit(T x) HasSignBit(T aX)
{ {
// In C++, right bit shifts on negative values is undefined by the standard. // In C++, right bit shifts on negative values is undefined by the standard.
// Notice that signed-to-unsigned conversions are always well-defined in the // Notice that signed-to-unsigned conversions are always well-defined in the
// standard, as the value congruent modulo 2**n as expected. By contrast, // standard, as the value congruent modulo 2**n as expected. By contrast,
// unsigned-to-signed is only well-defined if the value is representable. // unsigned-to-signed is only well-defined if the value is representable.
return bool(typename MakeUnsigned<T>::Type(x) >> PositionOfSignBit<T>::value); return bool(typename MakeUnsigned<T>::Type(aX) >>
PositionOfSignBit<T>::value);
} }
// Bitwise ops may return a larger type, so it's good to use this inline // Bitwise ops may return a larger type, so it's good to use this inline
// helper guaranteeing that the result is really of type T. // helper guaranteeing that the result is really of type T.
template<typename T> template<typename T>
inline T inline T
BinaryComplement(T x) BinaryComplement(T aX)
{ {
return ~x; return ~aX;
} }
template<typename T, template<typename T,
@ -173,19 +174,19 @@ struct DoesRangeContainRange
template<typename T, typename U, bool Signedness> template<typename T, typename U, bool Signedness>
struct DoesRangeContainRange<T, U, Signedness, Signedness> struct DoesRangeContainRange<T, U, Signedness, Signedness>
{ {
static const bool value = sizeof(T) >= sizeof(U); static const bool value = sizeof(T) >= sizeof(U);
}; };
template<typename T, typename U> template<typename T, typename U>
struct DoesRangeContainRange<T, U, true, false> struct DoesRangeContainRange<T, U, true, false>
{ {
static const bool value = sizeof(T) > sizeof(U); static const bool value = sizeof(T) > sizeof(U);
}; };
template<typename T, typename U> template<typename T, typename U>
struct DoesRangeContainRange<T, U, false, true> struct DoesRangeContainRange<T, U, false, true>
{ {
static const bool value = false; static const bool value = false;
}; };
template<typename T, template<typename T,
@ -198,89 +199,89 @@ struct IsInRangeImpl {};
template<typename T, typename U, bool IsTSigned, bool IsUSigned> template<typename T, typename U, bool IsTSigned, bool IsUSigned>
struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true> struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true>
{ {
static bool run(U) static bool run(U)
{ {
return true; return true;
} }
}; };
template<typename T, typename U> template<typename T, typename U>
struct IsInRangeImpl<T, U, true, true, false> struct IsInRangeImpl<T, U, true, true, false>
{ {
static bool run(U x) static bool run(U aX)
{ {
return x <= MaxValue<T>::value && x >= MinValue<T>::value; return aX <= MaxValue<T>::value && aX >= MinValue<T>::value;
} }
}; };
template<typename T, typename U> template<typename T, typename U>
struct IsInRangeImpl<T, U, false, false, false> struct IsInRangeImpl<T, U, false, false, false>
{ {
static bool run(U x) static bool run(U aX)
{ {
return x <= MaxValue<T>::value; return aX <= MaxValue<T>::value;
} }
}; };
template<typename T, typename U> template<typename T, typename U>
struct IsInRangeImpl<T, U, true, false, false> struct IsInRangeImpl<T, U, true, false, false>
{ {
static bool run(U x) static bool run(U aX)
{ {
return sizeof(T) > sizeof(U) || x <= U(MaxValue<T>::value); return sizeof(T) > sizeof(U) || aX <= U(MaxValue<T>::value);
} }
}; };
template<typename T, typename U> template<typename T, typename U>
struct IsInRangeImpl<T, U, false, true, false> struct IsInRangeImpl<T, U, false, true, false>
{ {
static bool run(U x) static bool run(U aX)
{ {
return sizeof(T) >= sizeof(U) return sizeof(T) >= sizeof(U)
? x >= 0 ? aX >= 0
: x >= 0 && x <= U(MaxValue<T>::value); : aX >= 0 && aX <= U(MaxValue<T>::value);
} }
}; };
template<typename T, typename U> template<typename T, typename U>
inline bool inline bool
IsInRange(U x) IsInRange(U aX)
{ {
return IsInRangeImpl<T, U>::run(x); return IsInRangeImpl<T, U>::run(aX);
} }
template<typename T> template<typename T>
inline bool inline bool
IsAddValid(T x, T y) IsAddValid(T aX, T aY)
{ {
// Addition is valid if the sign of x+y is equal to either that of x or that // Addition is valid if the sign of aX+aY is equal to either that of aX or
// of y. Since the value of x+y is undefined if we have a signed type, we // that of aY. Since the value of aX+aY is undefined if we have a signed
// compute it using the unsigned type of the same size. // type, we compute it using the unsigned type of the same size. Beware!
// Beware! These bitwise operations can return a larger integer type, // These bitwise operations can return a larger integer type, if T was a
// if T was a small type like int8_t, so we explicitly cast to T. // small type like int8_t, so we explicitly cast to T.
typename MakeUnsigned<T>::Type ux = x; typename MakeUnsigned<T>::Type ux = aX;
typename MakeUnsigned<T>::Type uy = y; typename MakeUnsigned<T>::Type uy = aY;
typename MakeUnsigned<T>::Type result = ux + uy; typename MakeUnsigned<T>::Type result = ux + uy;
return IsSigned<T>::value return IsSigned<T>::value
? HasSignBit(BinaryComplement(T((result ^ x) & (result ^ y)))) ? HasSignBit(BinaryComplement(T((result ^ aX) & (result ^ aY))))
: BinaryComplement(x) >= y; : BinaryComplement(aX) >= aY;
} }
template<typename T> template<typename T>
inline bool inline bool
IsSubValid(T x, T y) IsSubValid(T aX, T aY)
{ {
// Subtraction is valid if either x and y have same sign, or x-y and x have // Subtraction is valid if either aX and aY have same sign, or aX-aY and aX
// same sign. Since the value of x-y is undefined if we have a signed type, // have same sign. Since the value of aX-aY is undefined if we have a signed
// we compute it using the unsigned type of the same size. // type, we compute it using the unsigned type of the same size.
typename MakeUnsigned<T>::Type ux = x; typename MakeUnsigned<T>::Type ux = aX;
typename MakeUnsigned<T>::Type uy = y; typename MakeUnsigned<T>::Type uy = aY;
typename MakeUnsigned<T>::Type result = ux - uy; typename MakeUnsigned<T>::Type result = ux - uy;
return IsSigned<T>::value return IsSigned<T>::value
? HasSignBit(BinaryComplement(T((result ^ x) & (x ^ y)))) ? HasSignBit(BinaryComplement(T((result ^ aX) & (aX ^ aY))))
: x >= y; : aX >= aY;
} }
template<typename T, template<typename T,
@ -292,61 +293,62 @@ struct IsMulValidImpl {};
template<typename T, bool IsTSigned> template<typename T, bool IsTSigned>
struct IsMulValidImpl<T, IsTSigned, true> struct IsMulValidImpl<T, IsTSigned, true>
{ {
static bool run(T x, T y) static bool run(T aX, T aY)
{ {
typedef typename TwiceBiggerType<T>::Type TwiceBiggerType; typedef typename TwiceBiggerType<T>::Type TwiceBiggerType;
TwiceBiggerType product = TwiceBiggerType(x) * TwiceBiggerType(y); TwiceBiggerType product = TwiceBiggerType(aX) * TwiceBiggerType(aY);
return IsInRange<T>(product); return IsInRange<T>(product);
} }
}; };
template<typename T> template<typename T>
struct IsMulValidImpl<T, true, false> struct IsMulValidImpl<T, true, false>
{ {
static bool run(T x, T y) static bool run(T aX, T aY)
{ {
const T max = MaxValue<T>::value; const T max = MaxValue<T>::value;
const T min = MinValue<T>::value; const T min = MinValue<T>::value;
if (x == 0 || y == 0) if (aX == 0 || aY == 0) {
return true; return true;
if (x > 0) {
return y > 0
? x <= max / y
: y >= min / x;
}
// If we reach this point, we know that x < 0.
return y > 0
? x >= min / y
: y >= max / x;
} }
if (aX > 0) {
return aY > 0
? aX <= max / aY
: aY >= min / aX;
}
// If we reach this point, we know that aX < 0.
return aY > 0
? aX >= min / aY
: aY >= max / aX;
}
}; };
template<typename T> template<typename T>
struct IsMulValidImpl<T, false, false> struct IsMulValidImpl<T, false, false>
{ {
static bool run(T x, T y) static bool run(T aX, T aY)
{ {
return y == 0 || x <= MaxValue<T>::value / y; return aY == 0 || aX <= MaxValue<T>::value / aY;
} }
}; };
template<typename T> template<typename T>
inline bool inline bool
IsMulValid(T x, T y) IsMulValid(T aX, T aY)
{ {
return IsMulValidImpl<T>::run(x, y); return IsMulValidImpl<T>::run(aX, aY);
} }
template<typename T> template<typename T>
inline bool inline bool
IsDivValid(T x, T y) IsDivValid(T aX, T aY)
{ {
// Keep in mind that in the signed case, min/-1 is invalid because abs(min)>max. // Keep in mind that in the signed case, min/-1 is invalid because
return y != 0 && // abs(min)>max.
!(IsSigned<T>::value && x == MinValue<T>::value && y == T(-1)); return aY != 0 &&
!(IsSigned<T>::value && aX == MinValue<T>::value && aY == T(-1));
} }
template<typename T, bool IsTSigned = IsSigned<T>::value> template<typename T, bool IsTSigned = IsSigned<T>::value>
@ -354,36 +356,40 @@ struct IsModValidImpl;
template<typename T> template<typename T>
inline bool inline bool
IsModValid(T x, T y) IsModValid(T aX, T aY)
{ {
return IsModValidImpl<T>::run(x, y); return IsModValidImpl<T>::run(aX, aY);
} }
/* /*
* Mod is pretty simple. * Mod is pretty simple.
* For now, let's just use the ANSI C definition: * For now, let's just use the ANSI C definition:
* If x or y are negative, the results are implementation defined. * If aX or aY are negative, the results are implementation defined.
* Consider these invalid. * Consider these invalid.
* Undefined for y=0. * Undefined for aY=0.
* The result will never exceed either x or y. * The result will never exceed either aX or aY.
* *
* Checking that x>=0 is a warning when T is unsigned. * Checking that aX>=0 is a warning when T is unsigned.
*/ */
template<typename T> template<typename T>
struct IsModValidImpl<T, false> { struct IsModValidImpl<T, false>
static inline bool run(T x, T y) { {
return y >= 1; static inline bool run(T aX, T aY)
{
return aY >= 1;
} }
}; };
template<typename T> template<typename T>
struct IsModValidImpl<T, true> { struct IsModValidImpl<T, true>
static inline bool run(T x, T y) { {
if (x < 0) static inline bool run(T aX, T aY)
{
if (aX < 0) {
return false; return false;
}
return y >= 1; return aY >= 1;
} }
}; };
@ -393,25 +399,26 @@ struct NegateImpl;
template<typename T> template<typename T>
struct NegateImpl<T, false> struct NegateImpl<T, false>
{ {
static CheckedInt<T> negate(const CheckedInt<T>& val) static CheckedInt<T> negate(const CheckedInt<T>& aVal)
{ {
// Handle negation separately for signed/unsigned, for simpler code and to // Handle negation separately for signed/unsigned, for simpler code and to
// avoid an MSVC warning negating an unsigned value. // avoid an MSVC warning negating an unsigned value.
return CheckedInt<T>(0, val.isValid() && val.mValue == 0); return CheckedInt<T>(0, aVal.isValid() && aVal.mValue == 0);
} }
}; };
template<typename T> template<typename T>
struct NegateImpl<T, true> struct NegateImpl<T, true>
{ {
static CheckedInt<T> negate(const CheckedInt<T>& val) static CheckedInt<T> negate(const CheckedInt<T>& aVal)
{ {
// Watch out for the min-value, which (with twos-complement) can't be // Watch out for the min-value, which (with twos-complement) can't be
// negated as -min-value is then (max-value + 1). // negated as -min-value is then (max-value + 1).
if (!val.isValid() || val.mValue == MinValue<T>::value) if (!aVal.isValid() || aVal.mValue == MinValue<T>::value) {
return CheckedInt<T>(val.mValue, false); return CheckedInt<T>(aVal.mValue, false);
return CheckedInt<T>(-val.mValue, true);
} }
return CheckedInt<T>(-aVal.mValue, true);
}
}; };
} // namespace detail } // namespace detail
@ -437,18 +444,18 @@ struct NegateImpl<T, true>
* (e.g. in case of a division by zero). * (e.g. in case of a division by zero).
* *
* For example, suppose that you want to implement a function that computes * For example, suppose that you want to implement a function that computes
* (x+y)/z, that doesn't crash if z==0, and that reports on error (divide by * (aX+aY)/aZ, that doesn't crash if aZ==0, and that reports on error (divide by
* zero or integer overflow). You could code it as follows: * zero or integer overflow). You could code it as follows:
@code @code
bool computeXPlusYOverZ(int x, int y, int z, int *result) bool computeXPlusYOverZ(int aX, int aY, int aZ, int *aResult)
{ {
CheckedInt<int> checkedResult = (CheckedInt<int>(x) + y) / z; CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
if (checkedResult.isValid()) { if (checkedResult.isValid()) {
*result = checkedResult.value(); *aResult = checkedResult.value();
return true; return true;
} else { } else {
return false; return false;
} }
} }
@endcode @endcode
* *
@ -491,193 +498,188 @@ struct NegateImpl<T, true>
template<typename T> template<typename T>
class CheckedInt class CheckedInt
{ {
protected: protected:
T mValue; T mValue;
bool mIsValid; bool mIsValid;
template<typename U> template<typename U>
CheckedInt(U aValue, bool aIsValid) : mValue(aValue), mIsValid(aIsValid) CheckedInt(U aValue, bool aIsValid) : mValue(aValue), mIsValid(aIsValid)
{ {
static_assert(detail::IsSupported<T>::value && static_assert(detail::IsSupported<T>::value &&
detail::IsSupported<U>::value, detail::IsSupported<U>::value,
"This type is not supported by CheckedInt"); "This type is not supported by CheckedInt");
} }
friend struct detail::NegateImpl<T>; friend struct detail::NegateImpl<T>;
public: public:
/** /**
* Constructs a checked integer with given @a value. The checked integer is * Constructs a checked integer with given @a value. The checked integer is
* initialized as valid or invalid depending on whether the @a value * initialized as valid or invalid depending on whether the @a value
* is in range. * is in range.
* *
* This constructor is not explicit. Instead, the type of its argument is a * This constructor is not explicit. Instead, the type of its argument is a
* separate template parameter, ensuring that no conversion is performed * separate template parameter, ensuring that no conversion is performed
* before this constructor is actually called. As explained in the above * before this constructor is actually called. As explained in the above
* documentation for class CheckedInt, this constructor checks that its * documentation for class CheckedInt, this constructor checks that its
* argument is valid. * argument is valid.
*/ */
template<typename U> template<typename U>
CheckedInt(U aValue) CheckedInt(U aValue)
: mValue(T(aValue)), : mValue(T(aValue)),
mIsValid(detail::IsInRange<T>(aValue)) mIsValid(detail::IsInRange<T>(aValue))
{ {
static_assert(detail::IsSupported<T>::value && static_assert(detail::IsSupported<T>::value &&
detail::IsSupported<U>::value, detail::IsSupported<U>::value,
"This type is not supported by CheckedInt"); "This type is not supported by CheckedInt");
} }
template<typename U> template<typename U>
friend class CheckedInt; friend class CheckedInt;
template<typename U> template<typename U>
CheckedInt<U> toChecked() const CheckedInt<U> toChecked() const
{ {
CheckedInt<U> ret(mValue); CheckedInt<U> ret(mValue);
ret.mIsValid = ret.mIsValid && mIsValid; ret.mIsValid = ret.mIsValid && mIsValid;
return ret; return ret;
} }
/** Constructs a valid checked integer with initial value 0 */ /** Constructs a valid checked integer with initial value 0 */
CheckedInt() : mValue(0), mIsValid(true) CheckedInt() : mValue(0), mIsValid(true)
{ {
static_assert(detail::IsSupported<T>::value, static_assert(detail::IsSupported<T>::value,
"This type is not supported by CheckedInt"); "This type is not supported by CheckedInt");
} }
/** @returns the actual value */ /** @returns the actual value */
T value() const T value() const
{ {
MOZ_ASSERT(mIsValid, "Invalid checked integer (division by zero or integer overflow)"); MOZ_ASSERT(mIsValid, "Invalid checked integer (division by zero or integer overflow)");
return mValue; return mValue;
} }
/** /**
* @returns true if the checked integer is valid, i.e. is not the result * @returns true if the checked integer is valid, i.e. is not the result
* of an invalid operation or of an operation involving an invalid checked * of an invalid operation or of an operation involving an invalid checked
* integer * integer
*/ */
bool isValid() const bool isValid() const
{ {
return mIsValid; return mIsValid;
} }
template<typename U> template<typename U>
friend CheckedInt<U> operator +(const CheckedInt<U>& lhs, friend CheckedInt<U> operator +(const CheckedInt<U>& aLhs,
const CheckedInt<U>& rhs); const CheckedInt<U>& aRhs);
template<typename U> template<typename U>
CheckedInt& operator +=(U rhs); CheckedInt& operator +=(U aRhs);
template<typename U> template<typename U>
friend CheckedInt<U> operator -(const CheckedInt<U>& lhs, friend CheckedInt<U> operator -(const CheckedInt<U>& aLhs,
const CheckedInt<U>& rhs); const CheckedInt<U>& aRhs);
template<typename U> template<typename U>
CheckedInt& operator -=(U rhs); CheckedInt& operator -=(U aRhs);
template<typename U> template<typename U>
friend CheckedInt<U> operator *(const CheckedInt<U>& lhs, friend CheckedInt<U> operator *(const CheckedInt<U>& aLhs,
const CheckedInt<U>& rhs); const CheckedInt<U>& aRhs);
template<typename U> template<typename U>
CheckedInt& operator *=(U rhs); CheckedInt& operator *=(U aRhs);
template<typename U> template<typename U>
friend CheckedInt<U> operator /(const CheckedInt<U>& lhs, friend CheckedInt<U> operator /(const CheckedInt<U>& aLhs,
const CheckedInt<U>& rhs); const CheckedInt<U>& aRhs);
template<typename U> template<typename U>
CheckedInt& operator /=(U rhs); CheckedInt& operator /=(U aRhs);
template<typename U> template<typename U>
friend CheckedInt<U> operator %(const CheckedInt<U>& lhs, friend CheckedInt<U> operator %(const CheckedInt<U>& aLhs,
const CheckedInt<U>& rhs); const CheckedInt<U>& aRhs);
template<typename U> template<typename U>
CheckedInt& operator %=(U rhs); CheckedInt& operator %=(U aRhs);
CheckedInt operator -() const CheckedInt operator -() const
{ {
return detail::NegateImpl<T>::negate(*this); return detail::NegateImpl<T>::negate(*this);
} }
/** /**
* @returns true if the left and right hand sides are valid * @returns true if the left and right hand sides are valid
* and have the same value. * and have the same value.
* *
* Note that these semantics are the reason why we don't offer * Note that these semantics are the reason why we don't offer
* a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b) * a operator!=. Indeed, we'd want to have a!=b be equivalent to !(a==b)
* but that would mean that whenever a or b is invalid, a!=b * but that would mean that whenever a or b is invalid, a!=b
* is always true, which would be very confusing. * is always true, which would be very confusing.
* *
* For similar reasons, operators <, >, <=, >= would be very tricky to * For similar reasons, operators <, >, <=, >= would be very tricky to
* specify, so we just avoid offering them. * specify, so we just avoid offering them.
* *
* Notice that these == semantics are made more reasonable by these facts: * Notice that these == semantics are made more reasonable by these facts:
* 1. a==b implies equality at the raw data level * 1. a==b implies equality at the raw data level
* (the converse is false, as a==b is never true among invalids) * (the converse is false, as a==b is never true among invalids)
* 2. This is similar to the behavior of IEEE floats, where a==b * 2. This is similar to the behavior of IEEE floats, where a==b
* means that a and b have the same value *and* neither is NaN. * means that a and b have the same value *and* neither is NaN.
*/ */
bool operator ==(const CheckedInt& other) const bool operator ==(const CheckedInt& aOther) const
{ {
return mIsValid && other.mIsValid && mValue == other.mValue; return mIsValid && aOther.mIsValid && mValue == aOther.mValue;
} }
/** prefix ++ */ /** prefix ++ */
CheckedInt& operator++() CheckedInt& operator++()
{ {
*this += 1; *this += 1;
return *this; return *this;
} }
/** postfix ++ */ /** postfix ++ */
CheckedInt operator++(int) CheckedInt operator++(int)
{ {
CheckedInt tmp = *this; CheckedInt tmp = *this;
*this += 1; *this += 1;
return tmp; return tmp;
} }
/** prefix -- */ /** prefix -- */
CheckedInt& operator--() CheckedInt& operator--()
{ {
*this -= 1; *this -= 1;
return *this; return *this;
} }
/** postfix -- */ /** postfix -- */
CheckedInt operator--(int) CheckedInt operator--(int)
{ {
CheckedInt tmp = *this; CheckedInt tmp = *this;
*this -= 1; *this -= 1;
return tmp; return tmp;
} }
private: private:
/** /**
* The !=, <, <=, >, >= operators are disabled: * The !=, <, <=, >, >= operators are disabled:
* see the comment on operator==. * see the comment on operator==.
*/ */
template<typename U> template<typename U> bool operator !=(U aOther) const MOZ_DELETE;
bool operator !=(U other) const MOZ_DELETE; template<typename U> bool operator < (U aOther) const MOZ_DELETE;
template<typename U> template<typename U> bool operator <=(U aOther) const MOZ_DELETE;
bool operator <(U other) const MOZ_DELETE; template<typename U> bool operator > (U aOther) const MOZ_DELETE;
template<typename U> template<typename U> bool operator >=(U aOther) const MOZ_DELETE;
bool operator <=(U other) const MOZ_DELETE;
template<typename U>
bool operator >(U other) const MOZ_DELETE;
template<typename U>
bool operator >=(U other) const MOZ_DELETE;
}; };
#define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \ #define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
template<typename T> \ template<typename T> \
inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, \ inline CheckedInt<T> \
const CheckedInt<T> &rhs) \ operator OP(const CheckedInt<T> &aLhs, const CheckedInt<T> &aRhs) \
{ \ { \
if (!detail::Is##NAME##Valid(lhs.mValue, rhs.mValue)) \ if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \
return CheckedInt<T>(0, false); \ return CheckedInt<T>(0, false); \
\ } \
return CheckedInt<T>(lhs.mValue OP rhs.mValue, \ return CheckedInt<T>(aLhs.mValue OP aRhs.mValue, \
lhs.mIsValid && rhs.mIsValid); \ aLhs.mIsValid && aRhs.mIsValid); \
} }
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +) MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +)
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -) MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -)
@ -698,47 +700,47 @@ namespace detail {
template<typename T, typename U> template<typename T, typename U>
struct CastToCheckedIntImpl struct CastToCheckedIntImpl
{ {
typedef CheckedInt<T> ReturnType; typedef CheckedInt<T> ReturnType;
static CheckedInt<T> run(U u) { return u; } static CheckedInt<T> run(U aU) { return aU; }
}; };
template<typename T> template<typename T>
struct CastToCheckedIntImpl<T, CheckedInt<T> > struct CastToCheckedIntImpl<T, CheckedInt<T> >
{ {
typedef const CheckedInt<T>& ReturnType; typedef const CheckedInt<T>& ReturnType;
static const CheckedInt<T>& run(const CheckedInt<T>& u) { return u; } static const CheckedInt<T>& run(const CheckedInt<T>& aU) { return aU; }
}; };
} // namespace detail } // namespace detail
template<typename T, typename U> template<typename T, typename U>
inline typename detail::CastToCheckedIntImpl<T, U>::ReturnType inline typename detail::CastToCheckedIntImpl<T, U>::ReturnType
castToCheckedInt(U u) castToCheckedInt(U aU)
{ {
static_assert(detail::IsSupported<T>::value && static_assert(detail::IsSupported<T>::value &&
detail::IsSupported<U>::value, detail::IsSupported<U>::value,
"This type is not supported by CheckedInt"); "This type is not supported by CheckedInt");
return detail::CastToCheckedIntImpl<T, U>::run(u); return detail::CastToCheckedIntImpl<T, U>::run(aU);
} }
#define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \ #define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
template<typename T> \ template<typename T> \
template<typename U> \ template<typename U> \
CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U rhs) \ CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) \
{ \ { \
*this = *this OP castToCheckedInt<T>(rhs); \ *this = *this OP castToCheckedInt<T>(aRhs); \
return *this; \ return *this; \
} \ } \
template<typename T, typename U> \ template<typename T, typename U> \
inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, U rhs) \ inline CheckedInt<T> operator OP(const CheckedInt<T> &aLhs, U aRhs) \
{ \ { \
return lhs OP castToCheckedInt<T>(rhs); \ return aLhs OP castToCheckedInt<T>(aRhs); \
} \ } \
template<typename T, typename U> \ template<typename T, typename U> \
inline CheckedInt<T> operator OP(U lhs, const CheckedInt<T> &rhs) \ inline CheckedInt<T> operator OP(U aLhs, const CheckedInt<T> &aRhs) \
{ \ { \
return castToCheckedInt<T>(lhs) OP rhs; \ return castToCheckedInt<T>(aLhs) OP aRhs; \
} }
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=) MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=) MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
@ -750,16 +752,16 @@ MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
template<typename T, typename U> template<typename T, typename U>
inline bool inline bool
operator ==(const CheckedInt<T> &lhs, U rhs) operator ==(const CheckedInt<T> &aLhs, U aRhs)
{ {
return lhs == castToCheckedInt<T>(rhs); return aLhs == castToCheckedInt<T>(aRhs);
} }
template<typename T, typename U> template<typename T, typename U>
inline bool inline bool
operator ==(U lhs, const CheckedInt<T> &rhs) operator ==(U aLhs, const CheckedInt<T> &aRhs)
{ {
return castToCheckedInt<T>(lhs) == rhs; return castToCheckedInt<T>(aLhs) == aRhs;
} }
// Convenience typedefs. // Convenience typedefs.

View File

@ -1,9 +1,12 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Compression.h" #include "mozilla/Compression.h"
#include "mozilla/CheckedInt.h" #include "mozilla/CheckedInt.h"
using namespace mozilla::Compression; using namespace mozilla::Compression;
namespace { namespace {
@ -15,50 +18,51 @@ namespace {
/* Our wrappers */ /* Our wrappers */
size_t size_t
LZ4::compress(const char* source, size_t inputSize, char* dest) LZ4::compress(const char* aSource, size_t aInputSize, char* aDest)
{ {
CheckedInt<int> inputSizeChecked = inputSize; CheckedInt<int> inputSizeChecked = aInputSize;
MOZ_ASSERT(inputSizeChecked.isValid()); MOZ_ASSERT(inputSizeChecked.isValid());
return LZ4_compress(source, dest, inputSizeChecked.value()); return LZ4_compress(aSource, aDest, inputSizeChecked.value());
} }
size_t size_t
LZ4::compressLimitedOutput(const char* source, size_t inputSize, char* dest, size_t maxOutputSize) LZ4::compressLimitedOutput(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize)
{ {
CheckedInt<int> inputSizeChecked = inputSize; CheckedInt<int> inputSizeChecked = aInputSize;
MOZ_ASSERT(inputSizeChecked.isValid()); MOZ_ASSERT(inputSizeChecked.isValid());
CheckedInt<int> maxOutputSizeChecked = maxOutputSize; CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
MOZ_ASSERT(maxOutputSizeChecked.isValid()); MOZ_ASSERT(maxOutputSizeChecked.isValid());
return LZ4_compress_limitedOutput(source, dest, inputSizeChecked.value(), return LZ4_compress_limitedOutput(aSource, aDest, inputSizeChecked.value(),
maxOutputSizeChecked.value()); maxOutputSizeChecked.value());
} }
bool bool
LZ4::decompress(const char* source, char* dest, size_t outputSize) LZ4::decompress(const char* aSource, char* aDest, size_t aOutputSize)
{ {
CheckedInt<int> outputSizeChecked = outputSize; CheckedInt<int> outputSizeChecked = aOutputSize;
MOZ_ASSERT(outputSizeChecked.isValid()); MOZ_ASSERT(outputSizeChecked.isValid());
int ret = LZ4_decompress_fast(source, dest, outputSizeChecked.value()); int ret = LZ4_decompress_fast(aSource, aDest, outputSizeChecked.value());
return ret >= 0; return ret >= 0;
} }
bool bool
LZ4::decompress(const char* source, size_t inputSize, char* dest, size_t maxOutputSize, LZ4::decompress(const char* aSource, size_t aInputSize, char* aDest,
size_t *outputSize) size_t aMaxOutputSize, size_t *aOutputSize)
{ {
CheckedInt<int> maxOutputSizeChecked = maxOutputSize; CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
MOZ_ASSERT(maxOutputSizeChecked.isValid()); MOZ_ASSERT(maxOutputSizeChecked.isValid());
CheckedInt<int> inputSizeChecked = inputSize; CheckedInt<int> inputSizeChecked = aInputSize;
MOZ_ASSERT(inputSizeChecked.isValid()); MOZ_ASSERT(inputSizeChecked.isValid());
int ret = LZ4_decompress_safe(source, dest, inputSizeChecked.value(), int ret = LZ4_decompress_safe(aSource, aDest, inputSizeChecked.value(),
maxOutputSizeChecked.value()); maxOutputSizeChecked.value());
if (ret >= 0) { if (ret >= 0) {
*outputSize = ret; *aOutputSize = ret;
return true; return true;
} else { }
*outputSize = 0;
return false; *aOutputSize = 0;
} return false;
} }

View File

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -22,41 +23,41 @@ namespace Compression {
* *
* Compared to zlib it compresses at about 10x the speed, decompresses at about * Compared to zlib it compresses at about 10x the speed, decompresses at about
* 4x the speed and produces output of about 1.5x the size. * 4x the speed and produces output of about 1.5x the size.
*
*/ */
class LZ4 class LZ4
{ {
public: public:
/** /**
* Compresses 'inputSize' bytes from 'source' into 'dest'. * Compresses |aInputSize| bytes from |aSource| into |aDest|. Destination
* Destination buffer must be already allocated, * buffer must be already allocated, and must be sized to handle worst cases
* and must be sized to handle worst cases situations (input data not compressible) * situations (input data not compressible). Worst case size evaluation is
* Worst case size evaluation is provided by function maxCompressedSize() * provided by function maxCompressedSize()
* *
* @param inputSize is the input size. Max supported value is ~1.9GB * @param aInputSize is the input size. Max supported value is ~1.9GB
* @param return the number of bytes written in buffer dest * @return the number of bytes written in buffer |aDest|
*/ */
static MFBT_API size_t compress(const char* source, size_t inputSize, char* dest); static MFBT_API size_t
compress(const char* aSource, size_t aInputSize, char* aDest);
/** /**
* Compress 'inputSize' bytes from 'source' into an output buffer * Compress |aInputSize| bytes from |aSource| into an output buffer
* 'dest' of maximum size 'maxOutputSize'. If it cannot achieve it, * |aDest| of maximum size |aMaxOutputSize|. If it cannot achieve it,
* compression will stop, and result of the function will be zero, * compression will stop, and result of the function will be zero,
* 'dest' will still be written to, but since the number of input * |aDest| will still be written to, but since the number of input
* bytes consumed is not returned the result is not usable. * bytes consumed is not returned the result is not usable.
* *
* This function never writes outside of provided output buffer. * This function never writes outside of provided output buffer.
* *
* @param inputSize is the input size. Max supported value is ~1.9GB * @param aInputSize is the input size. Max supported value is ~1.9GB
* @param maxOutputSize is the size of the destination buffer (which must be already allocated) * @param aMaxOutputSize is the size of the destination buffer (which must
* @return the number of bytes written in buffer 'dest' * be already allocated)
or 0 if the compression fails * @return the number of bytes written in buffer |aDest| or 0 if the
*/ * compression fails
static MFBT_API size_t compressLimitedOutput(const char* source, size_t inputSize, char* dest, */
size_t maxOutputSize); static MFBT_API size_t
compressLimitedOutput(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize);
/** /**
* If the source stream is malformed, the function will stop decoding * If the source stream is malformed, the function will stop decoding
@ -66,48 +67,50 @@ public:
* This function never writes outside of provided buffers, and never * This function never writes outside of provided buffers, and never
* modifies input buffer. * modifies input buffer.
* *
* note : destination buffer must be already allocated. * Note: destination buffer must be already allocated, and its size must be a
* its size must be a minimum of 'outputSize' bytes. * minimum of |aOutputSize| bytes.
* @param outputSize is the output size, therefore the original size *
* @param aOutputSize is the output size, therefore the original size
* @return the number of bytes read in the source buffer * @return the number of bytes read in the source buffer
*/ */
static MFBT_API bool decompress(const char* source, char* dest, size_t outputSize); static MFBT_API bool
decompress(const char* aSource, char* aDest, size_t aOutputSize);
/** /**
* If the source stream is malformed, the function will stop decoding * If the source stream is malformed, the function will stop decoding
* and return false. * and return false.
* *
* This function never writes beyond dest + maxOutputSize, and is * This function never writes beyond aDest + aMaxOutputSize, and is
* therefore protected against malicious data packets. * therefore protected against malicious data packets.
* *
* note : Destination buffer must be already allocated. * Note: Destination buffer must be already allocated. This version is
* This version is slightly slower than the decompress * slightly slower than the decompress without the aMaxOutputSize.
* without the maxOutputSize
* *
* @param inputSize is the length of the input compressed data * @param aInputSize is the length of the input compressed data
* @param maxOutputSize is the size of the destination buffer (which must be already allocated) * @param aMaxOutputSize is the size of the destination buffer (which must be
* @param outputSize the actual number of bytes decoded in the destination buffer (necessarily <= maxOutputSize) * already allocated)
* @param aOutputSize the actual number of bytes decoded in the destination
*/ * buffer (necessarily <= aMaxOutputSize)
static MFBT_API bool decompress(const char* source, size_t inputSize, char* dest, */
size_t maxOutputSize, size_t *outputSize); static MFBT_API bool
decompress(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize, size_t *aOutputSize);
/* /*
Provides the maximum size that LZ4 may output in a "worst case" * Provides the maximum size that LZ4 may output in a "worst case"
scenario (input data not compressible) primarily useful for memory * scenario (input data not compressible) primarily useful for memory
allocation of output buffer. * allocation of output buffer.
note : this function is limited by "int" range (2^31-1) * note : this function is limited by "int" range (2^31-1)
*
@param inputSize is the input size. Max supported value is ~1.9GB * @param aInputSize is the input size. Max supported value is ~1.9GB
@return maximum output size in a "worst case" scenario * @return maximum output size in a "worst case" scenario
*/ */
static inline size_t maxCompressedSize(size_t inputSize) static inline size_t maxCompressedSize(size_t aInputSize)
{ {
size_t max = ((inputSize) + ((inputSize)/255) + 16); size_t max = (aInputSize + (aInputSize / 255) + 16);
MOZ_ASSERT(max > inputSize); MOZ_ASSERT(max > aInputSize);
return max; return max;
} }
}; };
} /* namespace Compression */ } /* namespace Compression */

View File

@ -34,47 +34,44 @@ namespace mozilla {
template<typename T> template<typename T>
class DebugOnly class DebugOnly
{ {
public: public:
#ifdef DEBUG #ifdef DEBUG
T value; T value;
DebugOnly() { } DebugOnly() { }
DebugOnly(const T& other) : value(other) { } DebugOnly(const T& aOther) : value(aOther) { }
DebugOnly(const DebugOnly& other) : value(other.value) { } DebugOnly(const DebugOnly& aOther) : value(aOther.value) { }
DebugOnly& operator=(const T& rhs) { DebugOnly& operator=(const T& aRhs) {
value = rhs; value = aRhs;
return *this; return *this;
} }
void operator++(int) {
value++;
}
void operator--(int) {
value--;
}
T* operator&() { return &value; } void operator++(int) { value++; }
void operator--(int) { value--; }
operator T&() { return value; } T* operator&() { return &value; }
operator const T&() const { return value; }
T& operator->() { return value; } operator T&() { return value; }
const T& operator->() const { return value; } operator const T&() const { return value; }
T& operator->() { return value; }
const T& operator->() const { return value; }
#else #else
DebugOnly() { } DebugOnly() { }
DebugOnly(const T&) { } DebugOnly(const T&) { }
DebugOnly(const DebugOnly&) { } DebugOnly(const DebugOnly&) { }
DebugOnly& operator=(const T&) { return *this; } DebugOnly& operator=(const T&) { return *this; }
void operator++(int) { } void operator++(int) { }
void operator--(int) { } void operator--(int) { }
#endif #endif
/* /*
* DebugOnly must always have a destructor or else it will * DebugOnly must always have a destructor or else it will
* generate "unused variable" warnings, exactly what it's intended * generate "unused variable" warnings, exactly what it's intended
* to avoid! * to avoid!
*/ */
~DebugOnly() {} ~DebugOnly() {}
}; };
} }