mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1014377 - Convert the first quarter of MFBT to Gecko style. r=froydnj.
--HG-- extra : rebase_source : b3b2da775e2c0e8a6ecbed70e7bd0c8f7af67b47
This commit is contained in:
parent
dab2c77f74
commit
0b4c7c33bf
@ -21,14 +21,14 @@ namespace mozilla {
|
||||
template<typename T>
|
||||
class AlignmentFinder
|
||||
{
|
||||
struct Aligner
|
||||
{
|
||||
char c;
|
||||
T t;
|
||||
};
|
||||
struct Aligner
|
||||
{
|
||||
char mChar;
|
||||
T mT;
|
||||
};
|
||||
|
||||
public:
|
||||
static const size_t alignment = sizeof(Aligner) - sizeof(T);
|
||||
public:
|
||||
static const size_t alignment = sizeof(Aligner) - sizeof(T);
|
||||
};
|
||||
|
||||
#define MOZ_ALIGNOF(T) mozilla::AlignmentFinder<T>::alignment
|
||||
@ -71,31 +71,31 @@ struct AlignedElem;
|
||||
template<>
|
||||
struct AlignedElem<1>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 1);
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 1);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AlignedElem<2>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 2);
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 2);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AlignedElem<4>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 4);
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 4);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AlignedElem<8>
|
||||
{
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 8);
|
||||
MOZ_ALIGNED_DECL(uint8_t elem, 8);
|
||||
};
|
||||
|
||||
template<>
|
||||
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>
|
||||
struct AlignedStorage
|
||||
{
|
||||
union U {
|
||||
char bytes[Nbytes];
|
||||
uint64_t _;
|
||||
} u;
|
||||
union U
|
||||
{
|
||||
char mBytes[Nbytes];
|
||||
uint64_t mDummy;
|
||||
} u;
|
||||
|
||||
const void* addr() const { return u.bytes; }
|
||||
void* addr() { return u.bytes; }
|
||||
const void* addr() const { return u.mBytes; }
|
||||
void* addr() { return u.mBytes; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct AlignedStorage2
|
||||
{
|
||||
union U {
|
||||
char bytes[sizeof(T)];
|
||||
uint64_t _;
|
||||
} u;
|
||||
union U
|
||||
{
|
||||
char mBytes[sizeof(T)];
|
||||
uint64_t mDummy;
|
||||
} u;
|
||||
|
||||
const T* addr() const { return reinterpret_cast<const T*>(u.bytes); }
|
||||
T* addr() { return static_cast<T*>(static_cast<void*>(u.bytes)); }
|
||||
const T* addr() const { return reinterpret_cast<const T*>(u.mBytes); }
|
||||
T* addr() { return static_cast<T*>(static_cast<void*>(u.mBytes)); }
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
@ -49,14 +49,31 @@ namespace mozilla {
|
||||
*/
|
||||
class MallocAllocPolicy
|
||||
{
|
||||
public:
|
||||
void* malloc_(size_t bytes) { return malloc(bytes); }
|
||||
void* calloc_(size_t bytes) { return calloc(bytes, 1); }
|
||||
void* realloc_(void* p, size_t oldBytes, size_t bytes) { return realloc(p, bytes); }
|
||||
void free_(void* p) { free(p); }
|
||||
void reportAllocOverflow() const {}
|
||||
};
|
||||
public:
|
||||
void* malloc_(size_t aBytes)
|
||||
{
|
||||
return malloc(aBytes);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
38
mfbt/Array.h
38
mfbt/Array.h
@ -19,31 +19,35 @@ namespace mozilla {
|
||||
template<typename T, size_t Length>
|
||||
class Array
|
||||
{
|
||||
T arr[Length];
|
||||
T mArr[Length];
|
||||
|
||||
public:
|
||||
T& operator[](size_t i) {
|
||||
MOZ_ASSERT(i < Length);
|
||||
return arr[i];
|
||||
}
|
||||
public:
|
||||
T& operator[](size_t aIndex)
|
||||
{
|
||||
MOZ_ASSERT(aIndex < Length);
|
||||
return mArr[aIndex];
|
||||
}
|
||||
|
||||
const T& operator[](size_t i) const {
|
||||
MOZ_ASSERT(i < Length);
|
||||
return arr[i];
|
||||
}
|
||||
const T& operator[](size_t aIndex) const
|
||||
{
|
||||
MOZ_ASSERT(aIndex < Length);
|
||||
return mArr[aIndex];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Array<T, 0>
|
||||
{
|
||||
public:
|
||||
T& operator[](size_t i) {
|
||||
MOZ_CRASH("indexing into zero-length array");
|
||||
}
|
||||
public:
|
||||
T& operator[](size_t aIndex)
|
||||
{
|
||||
MOZ_CRASH("indexing into zero-length array");
|
||||
}
|
||||
|
||||
const T& operator[](size_t i) const {
|
||||
MOZ_CRASH("indexing into zero-length array");
|
||||
}
|
||||
const T& operator[](size_t aIndex) const
|
||||
{
|
||||
MOZ_CRASH("indexing into zero-length array");
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
@ -23,17 +23,17 @@
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* Safely subtract two pointers when it is known that end >= begin. This avoids
|
||||
* the common compiler bug that if (size_t(end) - size_t(begin)) has the MSB
|
||||
* set, the unsigned subtraction followed by right shift will produce -1, or
|
||||
* size_t(-1), instead of the real difference.
|
||||
* Safely subtract two pointers when it is known that aEnd >= aBegin. This
|
||||
* avoids the common compiler bug that if (size_t(aEnd) - size_t(aBegin)) has
|
||||
* the MSB set, the unsigned subtraction followed by right shift will produce
|
||||
* -1, or size_t(-1), instead of the real difference.
|
||||
*/
|
||||
template<class T>
|
||||
MOZ_ALWAYS_INLINE size_t
|
||||
PointerRangeSize(T* begin, T* end)
|
||||
PointerRangeSize(T* aBegin, T* aEnd)
|
||||
{
|
||||
MOZ_ASSERT(end >= begin);
|
||||
return (size_t(end) - size_t(begin)) / sizeof(T);
|
||||
MOZ_ASSERT(aEnd >= aBegin);
|
||||
return (size_t(aEnd) - size_t(aBegin)) / sizeof(T);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -44,14 +44,14 @@ PointerRangeSize(T* begin, T* end)
|
||||
*/
|
||||
template<typename T, size_t N>
|
||||
MOZ_CONSTEXPR size_t
|
||||
ArrayLength(T (&arr)[N])
|
||||
ArrayLength(T (&aArr)[N])
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
MOZ_CONSTEXPR size_t
|
||||
ArrayLength(const Array<T, N>& arr)
|
||||
ArrayLength(const Array<T, N>& aArr)
|
||||
{
|
||||
return N;
|
||||
}
|
||||
@ -63,23 +63,23 @@ ArrayLength(const Array<T, N>& arr)
|
||||
*/
|
||||
template<typename T, size_t N>
|
||||
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>
|
||||
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>
|
||||
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 {
|
||||
|
@ -32,13 +32,13 @@
|
||||
* number of undesired macros and symbols.
|
||||
*/
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
# endif
|
||||
__declspec(dllimport) int __stdcall
|
||||
TerminateProcess(void* hProcess, unsigned int uExitCode);
|
||||
__declspec(dllimport) void* __stdcall GetCurrentProcess(void);
|
||||
__declspec(dllimport) int __stdcall
|
||||
TerminateProcess(void* hProcess, unsigned int uExitCode);
|
||||
__declspec(dllimport) void* __stdcall GetCurrentProcess(void);
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
}
|
||||
# endif
|
||||
#else
|
||||
# include <signal.h>
|
||||
@ -124,21 +124,23 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Prints |s| as an assertion failure (using file and ln as the location of the
|
||||
* assertion) to the standard debug-output channel.
|
||||
* Prints |aStr| as an assertion failure (using aFilename and aLine as the
|
||||
* location of the assertion) to the standard debug-output channel.
|
||||
*
|
||||
* 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
|
||||
* for use in implementing release-build assertions.
|
||||
*/
|
||||
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
|
||||
__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
|
||||
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
|
||||
nsTraceRefcnt::WalkTheStack(stderr);
|
||||
#endif
|
||||
@ -147,13 +149,13 @@ MOZ_ReportAssertionFailure(const char* s, const char* file, int ln) MOZ_PRETEND_
|
||||
}
|
||||
|
||||
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
|
||||
__android_log_print(ANDROID_LOG_FATAL, "MOZ_CRASH",
|
||||
"Hit MOZ_CRASH(%s) at %s:%d\n", s, file, ln);
|
||||
__android_log_print(ANDROID_LOG_FATAL, "MOZ_CRASH",
|
||||
"Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine);
|
||||
#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
|
||||
nsTraceRefcnt::WalkTheStack(stderr);
|
||||
#endif
|
||||
@ -319,13 +321,13 @@ namespace detail {
|
||||
template<typename T>
|
||||
struct IsFunction
|
||||
{
|
||||
static const bool value = false;
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename R, typename... A>
|
||||
struct IsFunction<R(A...)>
|
||||
{
|
||||
static const bool value = true;
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -333,47 +335,51 @@ void ValidateAssertConditionType()
|
||||
{
|
||||
typedef typename RemoveReference<T>::Type ValueT;
|
||||
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,
|
||||
"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,
|
||||
"It's often a bad idea to assert that a floating-point number is nonzero, "
|
||||
"because such assertions tend to intermittently fail. Shouldn't your code gracefully handle "
|
||||
"this case instead of asserting? Anyway, if you really want to "
|
||||
"do that, write an explicit boolean condition, like !!x or x!=0.");
|
||||
"It's often a bad idea to assert that a floating-point number "
|
||||
"is nonzero, because such assertions tend to intermittently "
|
||||
"fail. Shouldn't your code gracefully handle this case instead "
|
||||
"of asserting? Anyway, if you really want to do that, write an "
|
||||
"explicit boolean condition, like !!x or x!=0.");
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // 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
|
||||
# define MOZ_VALIDATE_ASSERT_CONDITION_TYPE(x)
|
||||
#endif
|
||||
|
||||
/* First the single-argument form. */
|
||||
#define MOZ_ASSERT_HELPER1(expr) \
|
||||
do { \
|
||||
MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
|
||||
if (MOZ_UNLIKELY(!(expr))) { \
|
||||
MOZ_ReportAssertionFailure(#expr, __FILE__, __LINE__); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
} \
|
||||
} while (0)
|
||||
do { \
|
||||
MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
|
||||
if (MOZ_UNLIKELY(!(expr))) { \
|
||||
MOZ_ReportAssertionFailure(#expr, __FILE__, __LINE__); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
} \
|
||||
} while (0)
|
||||
/* Now the two-argument form. */
|
||||
#define MOZ_ASSERT_HELPER2(expr, explain) \
|
||||
do { \
|
||||
MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
|
||||
if (MOZ_UNLIKELY(!(expr))) { \
|
||||
MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
} \
|
||||
} while (0)
|
||||
do { \
|
||||
MOZ_VALIDATE_ASSERT_CONDITION_TYPE(expr); \
|
||||
if (MOZ_UNLIKELY(!(expr))) { \
|
||||
MOZ_ReportAssertionFailure(#expr " (" explain ")", __FILE__, __LINE__); \
|
||||
MOZ_REALLY_CRASH(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MOZ_RELEASE_ASSERT_GLUE(a, b) a b
|
||||
#define MOZ_RELEASE_ASSERT(...) \
|
||||
MOZ_RELEASE_ASSERT_GLUE( \
|
||||
MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \
|
||||
(__VA_ARGS__))
|
||||
MOZ_RELEASE_ASSERT_GLUE( \
|
||||
MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \
|
||||
(__VA_ARGS__))
|
||||
|
||||
#ifdef DEBUG
|
||||
# define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__)
|
||||
@ -411,10 +417,11 @@ void ValidateAssertConditionType()
|
||||
#endif
|
||||
|
||||
/*
|
||||
* MOZ_ASSUME_UNREACHABLE_MARKER() expands to an expression which states that it is
|
||||
* undefined behavior for execution to reach this point. No guarantees are made
|
||||
* about what will happen if this is reached at runtime. Most code should use
|
||||
* MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE because it has extra asserts.
|
||||
* MOZ_ASSUME_UNREACHABLE_MARKER() expands to an expression which states that
|
||||
* it is undefined behavior for execution to reach this point. No guarantees
|
||||
* are made about what will happen if this is reached at runtime. Most code
|
||||
* should use MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE because it has extra
|
||||
* asserts.
|
||||
*/
|
||||
#if defined(__clang__)
|
||||
# define MOZ_ASSUME_UNREACHABLE_MARKER() __builtin_unreachable()
|
||||
|
997
mfbt/Atomics.h
997
mfbt/Atomics.h
File diff suppressed because it is too large
Load Diff
@ -150,10 +150,10 @@
|
||||
* template<typename T>
|
||||
* class Ptr
|
||||
* {
|
||||
* T* ptr;
|
||||
* MOZ_EXPLICIT_CONVERSION operator bool() const {
|
||||
* return ptr != nullptr;
|
||||
* }
|
||||
* T* ptr;
|
||||
* MOZ_EXPLICIT_CONVERSION operator bool() const {
|
||||
* return ptr != nullptr;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
*/
|
||||
@ -267,9 +267,9 @@
|
||||
*
|
||||
* struct NonCopyable
|
||||
* {
|
||||
* private:
|
||||
* NonCopyable(const NonCopyable& other) MOZ_DELETE;
|
||||
* void operator=(const NonCopyable& other) MOZ_DELETE;
|
||||
* private:
|
||||
* NonCopyable(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
|
||||
@ -295,23 +295,23 @@
|
||||
*
|
||||
* class Base
|
||||
* {
|
||||
* public:
|
||||
* virtual void f() = 0;
|
||||
* public:
|
||||
* virtual void f() = 0;
|
||||
* };
|
||||
* class Derived1 : public Base
|
||||
* {
|
||||
* public:
|
||||
* virtual void f() MOZ_OVERRIDE;
|
||||
* public:
|
||||
* virtual void f() MOZ_OVERRIDE;
|
||||
* };
|
||||
* class Derived2 : public Base
|
||||
* {
|
||||
* public:
|
||||
* virtual void f() MOZ_OVERRIDE = 0;
|
||||
* public:
|
||||
* virtual void f() MOZ_OVERRIDE = 0;
|
||||
* };
|
||||
* class Derived3 : public Base
|
||||
* {
|
||||
* public:
|
||||
* virtual void f() MOZ_OVERRIDE { }
|
||||
* public:
|
||||
* virtual void f() MOZ_OVERRIDE { }
|
||||
* };
|
||||
*
|
||||
* In compilers supporting C++11 override controls, MOZ_OVERRIDE *requires* that
|
||||
@ -339,16 +339,16 @@
|
||||
*
|
||||
* class Base MOZ_FINAL
|
||||
* {
|
||||
* public:
|
||||
* Base();
|
||||
* ~Base();
|
||||
* virtual void f() { }
|
||||
* public:
|
||||
* Base();
|
||||
* ~Base();
|
||||
* virtual void f() { }
|
||||
* };
|
||||
* // This will be an error in some compilers:
|
||||
* class Derived : public Base
|
||||
* {
|
||||
* public:
|
||||
* ~Derived() { }
|
||||
* public:
|
||||
* ~Derived() { }
|
||||
* };
|
||||
*
|
||||
* One particularly common reason to specify MOZ_FINAL upon a class is to tell
|
||||
@ -375,14 +375,14 @@
|
||||
*
|
||||
* class Base
|
||||
* {
|
||||
* public:
|
||||
* virtual void f() MOZ_FINAL;
|
||||
* public:
|
||||
* virtual void f() MOZ_FINAL;
|
||||
* };
|
||||
* class Derived
|
||||
* {
|
||||
* public:
|
||||
* // This will be an error in some compilers:
|
||||
* virtual void f();
|
||||
* public:
|
||||
* // This will be an error in some compilers:
|
||||
* virtual void f();
|
||||
* };
|
||||
*
|
||||
* In compilers implementing final controls, it is an error for a derived class
|
||||
|
@ -14,12 +14,12 @@
|
||||
namespace mozilla {
|
||||
|
||||
/*
|
||||
* The algorithm searches the given container 'c' over the sorted index range
|
||||
* [begin, end) for an index 'i' where 'c[i] == target'. If such an index 'i' is
|
||||
* found, BinarySearch returns 'true' and the index is returned via the outparam
|
||||
* 'matchOrInsertionPoint'. If no index is found, BinarySearch returns 'false'
|
||||
* and the outparam returns the first index in [begin, end] where 'target' can
|
||||
* be inserted to maintain sorted order.
|
||||
* The algorithm searches the given container |aContainer| over the sorted
|
||||
* index range [aBegin, aEnd) for an index |i| where |aContainer[i] == aTarget|.
|
||||
* If such an index |i| is found, BinarySearch returns |true| and the index is
|
||||
* returned via the outparam |aMatchOrInsertionPoint|. If no index is found,
|
||||
* BinarySearch returns |false| and the outparam returns the first index in
|
||||
* [aBegin, aEnd] where |aTarget| can be inserted to maintain sorted order.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
@ -32,32 +32,34 @@ namespace mozilla {
|
||||
|
||||
template <typename Container, typename T>
|
||||
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 high = end;
|
||||
size_t low = aBegin;
|
||||
size_t high = aEnd;
|
||||
while (low != high) {
|
||||
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(c[middle] <= c[high - 1]);
|
||||
MOZ_ASSERT(c[low] <= c[high - 1]);
|
||||
MOZ_ASSERT(aContainer[low] <= aContainer[middle]);
|
||||
MOZ_ASSERT(aContainer[middle] <= aContainer[high - 1]);
|
||||
MOZ_ASSERT(aContainer[low] <= aContainer[high - 1]);
|
||||
|
||||
if (target == middleValue) {
|
||||
*matchOrInsertionPoint = middle;
|
||||
if (aTarget == middleValue) {
|
||||
*aMatchOrInsertionPoint = middle;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target < middleValue)
|
||||
if (aTarget < middleValue) {
|
||||
high = middle;
|
||||
else
|
||||
} else {
|
||||
low = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
*matchOrInsertionPoint = low;
|
||||
*aMatchOrInsertionPoint = low;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -55,177 +55,199 @@ namespace mozilla {
|
||||
template<unsigned KeySize, class T>
|
||||
class BloomFilter
|
||||
{
|
||||
/*
|
||||
* A counting Bloom filter with 8-bit counters. For now we assume
|
||||
* that having two hash functions is enough, but we may revisit that
|
||||
* decision later.
|
||||
*
|
||||
* The filter uses an array with 2**KeySize entries.
|
||||
*
|
||||
* Assuming a well-distributed hash function, a Bloom filter with
|
||||
* array size M containing N elements and
|
||||
* using k hash function has expected false positive rate exactly
|
||||
*
|
||||
* $ (1 - (1 - 1/M)^{kN})^k $
|
||||
*
|
||||
* because each array slot has a
|
||||
*
|
||||
* $ (1 - 1/M)^{kN} $
|
||||
*
|
||||
* chance of being 0, and the expected false positive rate is the
|
||||
* probability that all of the k hash functions will hit a nonzero
|
||||
* slot.
|
||||
*
|
||||
* For reasonable assumptions (M large, kN large, which should both
|
||||
* hold if we're worried about false positives) about M and kN this
|
||||
* becomes approximately
|
||||
*
|
||||
* $$ (1 - \exp(-kN/M))^k $$
|
||||
*
|
||||
* For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
|
||||
* or in other words
|
||||
*
|
||||
* $$ N/M = -0.5 * \ln(1 - \sqrt(r)) $$
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 1, which doubles M, reduces the false positive rate by about a
|
||||
* factor of 4, and a false positive rate of 1% corresponds to
|
||||
* about M/N == 20.
|
||||
*
|
||||
* 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%.
|
||||
*
|
||||
* Similarly, using a KeySize of 10 would lead to a 4% false
|
||||
* positive rate for N == 100 and to quite bad false positive
|
||||
* rates for larger N.
|
||||
*/
|
||||
public:
|
||||
BloomFilter() {
|
||||
static_assert(KeySize <= keyShift, "KeySize too big");
|
||||
/*
|
||||
* A counting Bloom filter with 8-bit counters. For now we assume
|
||||
* that having two hash functions is enough, but we may revisit that
|
||||
* decision later.
|
||||
*
|
||||
* The filter uses an array with 2**KeySize entries.
|
||||
*
|
||||
* Assuming a well-distributed hash function, a Bloom filter with
|
||||
* array size M containing N elements and
|
||||
* using k hash function has expected false positive rate exactly
|
||||
*
|
||||
* $ (1 - (1 - 1/M)^{kN})^k $
|
||||
*
|
||||
* because each array slot has a
|
||||
*
|
||||
* $ (1 - 1/M)^{kN} $
|
||||
*
|
||||
* chance of being 0, and the expected false positive rate is the
|
||||
* probability that all of the k hash functions will hit a nonzero
|
||||
* slot.
|
||||
*
|
||||
* For reasonable assumptions (M large, kN large, which should both
|
||||
* hold if we're worried about false positives) about M and kN this
|
||||
* becomes approximately
|
||||
*
|
||||
* $$ (1 - \exp(-kN/M))^k $$
|
||||
*
|
||||
* For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
|
||||
* or in other words
|
||||
*
|
||||
* $$ N/M = -0.5 * \ln(1 - \sqrt(r)) $$
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 1, which doubles M, reduces the false positive rate by about a
|
||||
* factor of 4, and a false positive rate of 1% corresponds to
|
||||
* about M/N == 20.
|
||||
*
|
||||
* 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%.
|
||||
*
|
||||
* Similarly, using a KeySize of 10 would lead to a 4% false
|
||||
* positive rate for N == 100 and to quite bad false positive
|
||||
* rates for larger N.
|
||||
*/
|
||||
public:
|
||||
BloomFilter()
|
||||
{
|
||||
static_assert(KeySize <= kKeyShift, "KeySize too big");
|
||||
|
||||
// Should we have a custom operator new using calloc instead and
|
||||
// require that we're allocated via the operator?
|
||||
clear();
|
||||
}
|
||||
// Should we have a custom operator new using calloc instead and
|
||||
// require that we're allocated via the operator?
|
||||
clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the filter. This should be done before reusing it, because
|
||||
* just removing all items doesn't clear counters that hit the upper
|
||||
* bound.
|
||||
*/
|
||||
void clear();
|
||||
/*
|
||||
* Clear the filter. This should be done before reusing it, because
|
||||
* just removing all items doesn't clear counters that hit the upper
|
||||
* bound.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/*
|
||||
* Add an item to the filter.
|
||||
*/
|
||||
void add(const T* t);
|
||||
/*
|
||||
* Add an item to the filter.
|
||||
*/
|
||||
void add(const T* aValue);
|
||||
|
||||
/*
|
||||
* Remove an item from the filter.
|
||||
*/
|
||||
void remove(const T* t);
|
||||
/*
|
||||
* Remove an item from the filter.
|
||||
*/
|
||||
void remove(const T* aValue);
|
||||
|
||||
/*
|
||||
* Check whether the filter might contain an item. This can
|
||||
* sometimes return true even if the item is not in the filter,
|
||||
* but will never return false for items that are actually in the
|
||||
* filter.
|
||||
*/
|
||||
bool mightContain(const T* t) const;
|
||||
/*
|
||||
* Check whether the filter might contain an item. This can
|
||||
* sometimes return true even if the item is not in the filter,
|
||||
* but will never return false for items that are actually in the
|
||||
* filter.
|
||||
*/
|
||||
bool mightContain(const T* aValue) const;
|
||||
|
||||
/*
|
||||
* Methods for add/remove/contain when we already have a hash computed
|
||||
*/
|
||||
void add(uint32_t hash);
|
||||
void remove(uint32_t hash);
|
||||
bool mightContain(uint32_t hash) const;
|
||||
/*
|
||||
* Methods for add/remove/contain when we already have a hash computed
|
||||
*/
|
||||
void add(uint32_t aHash);
|
||||
void remove(uint32_t aHash);
|
||||
bool mightContain(uint32_t aHash) const;
|
||||
|
||||
private:
|
||||
static const size_t arraySize = (1 << KeySize);
|
||||
static const uint32_t keyMask = (1 << KeySize) - 1;
|
||||
static const uint32_t keyShift = 16;
|
||||
private:
|
||||
static const size_t kArraySize = (1 << KeySize);
|
||||
static const uint32_t kKeyMask = (1 << KeySize) - 1;
|
||||
static const uint32_t kKeyShift = 16;
|
||||
|
||||
static uint32_t hash1(uint32_t hash) { return hash & keyMask; }
|
||||
static uint32_t hash2(uint32_t hash) { return (hash >> keyShift) & keyMask; }
|
||||
static uint32_t hash1(uint32_t aHash)
|
||||
{
|
||||
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& secondSlot(uint32_t hash) { return counters[hash2(hash)]; }
|
||||
const uint8_t& firstSlot(uint32_t hash) const { return counters[hash1(hash)]; }
|
||||
const uint8_t& secondSlot(uint32_t hash) const { return counters[hash2(hash)]; }
|
||||
uint8_t& firstSlot(uint32_t aHash)
|
||||
{
|
||||
return mCounters[hash1(aHash)];
|
||||
}
|
||||
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>
|
||||
inline void
|
||||
BloomFilter<KeySize, T>::clear()
|
||||
{
|
||||
memset(counters, 0, arraySize);
|
||||
memset(mCounters, 0, kArraySize);
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
inline void
|
||||
BloomFilter<KeySize, T>::add(uint32_t hash)
|
||||
BloomFilter<KeySize, T>::add(uint32_t aHash)
|
||||
{
|
||||
uint8_t& slot1 = firstSlot(hash);
|
||||
if (MOZ_LIKELY(!full(slot1)))
|
||||
uint8_t& slot1 = firstSlot(aHash);
|
||||
if (MOZ_LIKELY(!full(slot1))) {
|
||||
++slot1;
|
||||
|
||||
uint8_t& slot2 = secondSlot(hash);
|
||||
}
|
||||
uint8_t& slot2 = secondSlot(aHash); {
|
||||
if (MOZ_LIKELY(!full(slot2)))
|
||||
++slot2;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
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);
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
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
|
||||
// there when we added or not, so just leave them full.
|
||||
uint8_t& slot1 = firstSlot(hash);
|
||||
if (MOZ_LIKELY(!full(slot1)))
|
||||
uint8_t& slot1 = firstSlot(aHash);
|
||||
if (MOZ_LIKELY(!full(slot1))) {
|
||||
--slot1;
|
||||
|
||||
uint8_t& slot2 = secondSlot(hash);
|
||||
if (MOZ_LIKELY(!full(slot2)))
|
||||
}
|
||||
uint8_t& slot2 = secondSlot(aHash);
|
||||
if (MOZ_LIKELY(!full(slot2))) {
|
||||
--slot2;
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
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);
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
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
|
||||
return firstSlot(hash) && secondSlot(hash);
|
||||
return firstSlot(aHash) && secondSlot(aHash);
|
||||
}
|
||||
|
||||
template<unsigned KeySize, class T>
|
||||
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);
|
||||
}
|
||||
|
||||
|
141
mfbt/Casting.h
141
mfbt/Casting.h
@ -17,7 +17,8 @@
|
||||
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
|
||||
* size differences, or this might fail to compile on some but not all
|
||||
@ -25,16 +26,17 @@ namespace mozilla {
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
inline To
|
||||
BitwiseCast(const From from)
|
||||
BitwiseCast(const From aFrom)
|
||||
{
|
||||
static_assert(sizeof(From) == sizeof(To),
|
||||
"To and From must have the same size");
|
||||
union {
|
||||
From from;
|
||||
To to;
|
||||
union
|
||||
{
|
||||
From mFrom;
|
||||
To mTo;
|
||||
} u;
|
||||
u.from = from;
|
||||
return u.to;
|
||||
u.mFrom = aFrom;
|
||||
return u.mTo;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
@ -58,34 +60,39 @@ enum UUComparison { FromIsBigger, FromIsNotBigger };
|
||||
// Unsigned-to-unsigned range check
|
||||
|
||||
template<typename From, typename To,
|
||||
UUComparison = (sizeof(From) > sizeof(To)) ? FromIsBigger : FromIsNotBigger>
|
||||
UUComparison = (sizeof(From) > sizeof(To))
|
||||
? FromIsBigger
|
||||
: FromIsNotBigger>
|
||||
struct UnsignedUnsignedCheck;
|
||||
|
||||
template<typename From, typename To>
|
||||
struct UnsignedUnsignedCheck<From, To, FromIsBigger>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
return from <= From(To(-1));
|
||||
}
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return aFrom <= From(To(-1));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct UnsignedUnsignedCheck<From, To, FromIsNotBigger>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
return UnsignedUnsignedCheck<From, To>::checkBounds(from);
|
||||
}
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return UnsignedUnsignedCheck<From, To>::checkBounds(aFrom);
|
||||
}
|
||||
};
|
||||
|
||||
// Signed-to-unsigned range check
|
||||
@ -93,14 +100,17 @@ struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsUnsigned>
|
||||
template<typename From, typename To>
|
||||
struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
if (from < 0)
|
||||
return false;
|
||||
if (sizeof(To) >= sizeof(From))
|
||||
return true;
|
||||
return from <= From(To(-1));
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
if (aFrom < 0) {
|
||||
return false;
|
||||
}
|
||||
if (sizeof(To) >= sizeof(From)) {
|
||||
return true;
|
||||
}
|
||||
return aFrom <= From(To(-1));
|
||||
}
|
||||
};
|
||||
|
||||
// Unsigned-to-signed range check
|
||||
@ -108,35 +118,40 @@ struct BoundsCheckImpl<From, To, FromIsSigned, ToIsUnsigned>
|
||||
enum USComparison { FromIsSmaller, FromIsNotSmaller };
|
||||
|
||||
template<typename From, typename To,
|
||||
USComparison = (sizeof(From) < sizeof(To)) ? FromIsSmaller : FromIsNotSmaller>
|
||||
USComparison = (sizeof(From) < sizeof(To))
|
||||
? FromIsSmaller
|
||||
: FromIsNotSmaller>
|
||||
struct UnsignedSignedCheck;
|
||||
|
||||
template<typename From, typename To>
|
||||
struct UnsignedSignedCheck<From, To, FromIsSmaller>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct UnsignedSignedCheck<From, To, FromIsNotSmaller>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
|
||||
return from <= From(MaxValue);
|
||||
}
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
|
||||
return aFrom <= From(MaxValue);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
return UnsignedSignedCheck<From, To>::checkBounds(from);
|
||||
}
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return UnsignedSignedCheck<From, To>::checkBounds(aFrom);
|
||||
}
|
||||
};
|
||||
|
||||
// Signed-to-signed range check
|
||||
@ -144,42 +159,46 @@ struct BoundsCheckImpl<From, To, FromIsUnsigned, ToIsSigned>
|
||||
template<typename From, typename To>
|
||||
struct BoundsCheckImpl<From, To, FromIsSigned, ToIsSigned>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
if (sizeof(From) <= sizeof(To))
|
||||
return true;
|
||||
const To MaxValue = To((1ULL << (CHAR_BIT * sizeof(To) - 1)) - 1);
|
||||
const To MinValue = -MaxValue - To(1);
|
||||
return From(MinValue) <= from &&
|
||||
From(from) <= From(MaxValue);
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
if (sizeof(From) <= sizeof(To)) {
|
||||
return true;
|
||||
}
|
||||
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,
|
||||
bool TypesAreIntegral = IsIntegral<From>::value && IsIntegral<To>::value>
|
||||
bool TypesAreIntegral = IsIntegral<From>::value &&
|
||||
IsIntegral<To>::value>
|
||||
class BoundsChecker;
|
||||
|
||||
template<typename From>
|
||||
class BoundsChecker<From, From, true>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) { return true; }
|
||||
public:
|
||||
static bool checkBounds(const From aFrom) { return true; }
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
class BoundsChecker<From, To, true>
|
||||
{
|
||||
public:
|
||||
static bool checkBounds(const From from) {
|
||||
return BoundsCheckImpl<From, To>::checkBounds(from);
|
||||
}
|
||||
public:
|
||||
static bool checkBounds(const From aFrom)
|
||||
{
|
||||
return BoundsCheckImpl<From, To>::checkBounds(aFrom);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename From, typename To>
|
||||
inline bool
|
||||
IsInBounds(const From from)
|
||||
IsInBounds(const From aFrom)
|
||||
{
|
||||
return BoundsChecker<From, To>::checkBounds(from);
|
||||
return BoundsChecker<From, To>::checkBounds(aFrom);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
@ -191,10 +210,10 @@ IsInBounds(const From from)
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
inline To
|
||||
SafeCast(const From from)
|
||||
SafeCast(const From aFrom)
|
||||
{
|
||||
MOZ_ASSERT((detail::IsInBounds<From, To>(from)));
|
||||
return static_cast<To>(from);
|
||||
MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
|
||||
return static_cast<To>(aFrom);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -19,21 +19,21 @@ namespace mozilla {
|
||||
*/
|
||||
class ChaosMode
|
||||
{
|
||||
public:
|
||||
static bool isActive()
|
||||
{
|
||||
// Flip this to true to activate chaos mode
|
||||
return false;
|
||||
}
|
||||
public:
|
||||
static bool isActive()
|
||||
{
|
||||
// Flip this to true to activate chaos mode
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a somewhat (but not uniformly) random uint32_t < aBound.
|
||||
* Not to be used for anything except ChaosMode, since it's not very random.
|
||||
*/
|
||||
static uint32_t randomUint32LessThan(uint32_t aBound)
|
||||
{
|
||||
return uint32_t(rand()) % 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.
|
||||
*/
|
||||
static uint32_t randomUint32LessThan(uint32_t aBound)
|
||||
{
|
||||
return uint32_t(rand()) % aBound;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
168
mfbt/Char16.h
168
mfbt/Char16.h
@ -32,8 +32,8 @@
|
||||
*/
|
||||
# define MOZ_UTF16_HELPER(s) L##s
|
||||
# define _CHAR16T
|
||||
typedef wchar_t char16_t;
|
||||
typedef unsigned int char32_t;
|
||||
typedef wchar_t char16_t;
|
||||
typedef unsigned int char32_t;
|
||||
#else
|
||||
/* C++11 has a builtin char16_t type. */
|
||||
# define MOZ_UTF16_HELPER(s) u##s
|
||||
@ -61,86 +61,108 @@
|
||||
*/
|
||||
class char16ptr_t
|
||||
{
|
||||
private:
|
||||
const char16_t* ptr;
|
||||
static_assert(sizeof(char16_t) == sizeof(wchar_t), "char16_t and wchar_t sizes differ");
|
||||
private:
|
||||
const char16_t* mPtr;
|
||||
static_assert(sizeof(char16_t) == sizeof(wchar_t),
|
||||
"char16_t and wchar_t sizes differ");
|
||||
|
||||
public:
|
||||
char16ptr_t(const char16_t* p) : ptr(p) {}
|
||||
char16ptr_t(const wchar_t* p) : ptr(reinterpret_cast<const char16_t*>(p)) {}
|
||||
public:
|
||||
char16ptr_t(const char16_t* aPtr) : mPtr(aPtr) {}
|
||||
char16ptr_t(const wchar_t* aPtr) :
|
||||
mPtr(reinterpret_cast<const char16_t*>(aPtr))
|
||||
{}
|
||||
|
||||
/* Without this, nullptr assignment would be ambiguous. */
|
||||
constexpr char16ptr_t(decltype(nullptr)) : ptr(nullptr) {}
|
||||
/* Without this, nullptr assignment would be ambiguous. */
|
||||
constexpr char16ptr_t(decltype(nullptr)) : mPtr(nullptr) {}
|
||||
|
||||
operator const char16_t*() const {
|
||||
return ptr;
|
||||
}
|
||||
operator const wchar_t*() const {
|
||||
return reinterpret_cast<const wchar_t*>(ptr);
|
||||
}
|
||||
operator const void*() const {
|
||||
return ptr;
|
||||
}
|
||||
operator bool() const {
|
||||
return ptr != nullptr;
|
||||
}
|
||||
operator std::wstring() const {
|
||||
return std::wstring(static_cast<const wchar_t*>(*this));
|
||||
}
|
||||
operator const char16_t*() const
|
||||
{
|
||||
return mPtr;
|
||||
}
|
||||
operator const wchar_t*() const
|
||||
{
|
||||
return reinterpret_cast<const wchar_t*>(mPtr);
|
||||
}
|
||||
operator const void*() const
|
||||
{
|
||||
return mPtr;
|
||||
}
|
||||
operator bool() const
|
||||
{
|
||||
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 operator char16_t*() const {
|
||||
return const_cast<char16_t*>(ptr);
|
||||
}
|
||||
explicit operator wchar_t*() const {
|
||||
return const_cast<wchar_t*>(static_cast<const wchar_t*>(*this));
|
||||
}
|
||||
/* Explicit cast operators to allow things like (char16_t*)str. */
|
||||
explicit operator char16_t*() const
|
||||
{
|
||||
return const_cast<char16_t*>(mPtr);
|
||||
}
|
||||
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*.
|
||||
* Supporting this requires explicit operators to support the requisite explicit
|
||||
* casts.
|
||||
*/
|
||||
explicit operator const char*() const {
|
||||
return reinterpret_cast<const char*>(ptr);
|
||||
}
|
||||
explicit operator const unsigned char*() const {
|
||||
return reinterpret_cast<const unsigned char*>(ptr);
|
||||
}
|
||||
explicit operator unsigned char*() const {
|
||||
return const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(ptr));
|
||||
}
|
||||
explicit operator void*() const {
|
||||
return const_cast<char16_t*>(ptr);
|
||||
}
|
||||
/**
|
||||
* Some Windows API calls accept BYTE* but require that data actually be WCHAR*.
|
||||
* Supporting this requires explicit operators to support the requisite explicit
|
||||
* casts.
|
||||
*/
|
||||
explicit operator const char*() const
|
||||
{
|
||||
return reinterpret_cast<const char*>(mPtr);
|
||||
}
|
||||
explicit operator const unsigned char*() const
|
||||
{
|
||||
return reinterpret_cast<const unsigned char*>(mPtr);
|
||||
}
|
||||
explicit operator unsigned char*() const
|
||||
{
|
||||
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. */
|
||||
char16_t operator[](size_t i) const {
|
||||
return ptr[i];
|
||||
}
|
||||
bool operator==(const char16ptr_t &x) const {
|
||||
return ptr == x.ptr;
|
||||
}
|
||||
bool operator==(decltype(nullptr)) const {
|
||||
return ptr == nullptr;
|
||||
}
|
||||
bool operator!=(const char16ptr_t &x) const {
|
||||
return ptr != x.ptr;
|
||||
}
|
||||
bool operator!=(decltype(nullptr)) const {
|
||||
return ptr != nullptr;
|
||||
}
|
||||
char16ptr_t operator+(size_t add) const {
|
||||
return char16ptr_t(ptr + add);
|
||||
}
|
||||
ptrdiff_t operator-(const char16ptr_t &other) const {
|
||||
return ptr - other.ptr;
|
||||
}
|
||||
/* Some operators used on pointers. */
|
||||
char16_t operator[](size_t aIndex) const
|
||||
{
|
||||
return mPtr[aIndex];
|
||||
}
|
||||
bool operator==(const char16ptr_t &aOther) const
|
||||
{
|
||||
return mPtr == aOther.mPtr;
|
||||
}
|
||||
bool operator==(decltype(nullptr)) const
|
||||
{
|
||||
return mPtr == nullptr;
|
||||
}
|
||||
bool operator!=(const char16ptr_t &aOther) const
|
||||
{
|
||||
return mPtr != aOther.mPtr;
|
||||
}
|
||||
bool operator!=(decltype(nullptr)) const
|
||||
{
|
||||
return mPtr != nullptr;
|
||||
}
|
||||
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)
|
||||
operator-(const char16_t* x, const char16ptr_t y) {
|
||||
return x - static_cast<const char16_t*>(y);
|
||||
operator-(const char16_t* aX, const char16ptr_t aY)
|
||||
{
|
||||
return aX - static_cast<const char16_t*>(aY);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -35,13 +35,13 @@ struct UnsupportedType {};
|
||||
template<typename IntegerType>
|
||||
struct IsSupportedPass2
|
||||
{
|
||||
static const bool value = false;
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct IsSupported
|
||||
{
|
||||
static const bool value = IsSupportedPass2<IntegerType>::value;
|
||||
static const bool value = IsSupportedPass2<IntegerType>::value;
|
||||
};
|
||||
|
||||
template<>
|
||||
@ -130,36 +130,37 @@ struct IsSupportedPass2<unsigned long long>
|
||||
template<typename IntegerType, size_t Size = sizeof(IntegerType)>
|
||||
struct TwiceBiggerType
|
||||
{
|
||||
typedef typename detail::StdintTypeForSizeAndSignedness<
|
||||
sizeof(IntegerType) * 2,
|
||||
IsSigned<IntegerType>::value
|
||||
>::Type Type;
|
||||
typedef typename detail::StdintTypeForSizeAndSignedness<
|
||||
sizeof(IntegerType) * 2,
|
||||
IsSigned<IntegerType>::value
|
||||
>::Type Type;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct TwiceBiggerType<IntegerType, 8>
|
||||
{
|
||||
typedef UnsupportedType Type;
|
||||
typedef UnsupportedType Type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
HasSignBit(T x)
|
||||
HasSignBit(T aX)
|
||||
{
|
||||
// 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
|
||||
// standard, as the value congruent modulo 2**n as expected. By contrast,
|
||||
// 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
|
||||
// helper guaranteeing that the result is really of type T.
|
||||
template<typename T>
|
||||
inline T
|
||||
BinaryComplement(T x)
|
||||
BinaryComplement(T aX)
|
||||
{
|
||||
return ~x;
|
||||
return ~aX;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
@ -173,19 +174,19 @@ struct DoesRangeContainRange
|
||||
template<typename T, typename U, bool 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>
|
||||
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>
|
||||
struct DoesRangeContainRange<T, U, false, true>
|
||||
{
|
||||
static const bool value = false;
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename T,
|
||||
@ -198,89 +199,89 @@ struct IsInRangeImpl {};
|
||||
template<typename T, typename U, bool IsTSigned, bool IsUSigned>
|
||||
struct IsInRangeImpl<T, U, IsTSigned, IsUSigned, true>
|
||||
{
|
||||
static bool run(U)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
static bool run(U)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, true, true, false>
|
||||
{
|
||||
static bool run(U x)
|
||||
{
|
||||
return x <= MaxValue<T>::value && x >= MinValue<T>::value;
|
||||
}
|
||||
static bool run(U aX)
|
||||
{
|
||||
return aX <= MaxValue<T>::value && aX >= MinValue<T>::value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, false, false, false>
|
||||
{
|
||||
static bool run(U x)
|
||||
{
|
||||
return x <= MaxValue<T>::value;
|
||||
}
|
||||
static bool run(U aX)
|
||||
{
|
||||
return aX <= MaxValue<T>::value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, true, false, false>
|
||||
{
|
||||
static bool run(U x)
|
||||
{
|
||||
return sizeof(T) > sizeof(U) || x <= U(MaxValue<T>::value);
|
||||
}
|
||||
static bool run(U aX)
|
||||
{
|
||||
return sizeof(T) > sizeof(U) || aX <= U(MaxValue<T>::value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, false, true, false>
|
||||
{
|
||||
static bool run(U x)
|
||||
{
|
||||
return sizeof(T) >= sizeof(U)
|
||||
? x >= 0
|
||||
: x >= 0 && x <= U(MaxValue<T>::value);
|
||||
}
|
||||
static bool run(U aX)
|
||||
{
|
||||
return sizeof(T) >= sizeof(U)
|
||||
? aX >= 0
|
||||
: aX >= 0 && aX <= U(MaxValue<T>::value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool
|
||||
IsInRange(U x)
|
||||
IsInRange(U aX)
|
||||
{
|
||||
return IsInRangeImpl<T, U>::run(x);
|
||||
return IsInRangeImpl<T, U>::run(aX);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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
|
||||
// of y. Since the value of x+y is undefined if we have a signed type, we
|
||||
// compute it using the unsigned type of the same size.
|
||||
// Beware! These bitwise operations can return a larger integer type,
|
||||
// if T was a small type like int8_t, so we explicitly cast to T.
|
||||
// Addition is valid if the sign of aX+aY is equal to either that of aX or
|
||||
// that of aY. Since the value of aX+aY is undefined if we have a signed
|
||||
// type, we compute it using the unsigned type of the same size. Beware!
|
||||
// These bitwise operations can return a larger integer type, if T was a
|
||||
// small type like int8_t, so we explicitly cast to T.
|
||||
|
||||
typename MakeUnsigned<T>::Type ux = x;
|
||||
typename MakeUnsigned<T>::Type uy = y;
|
||||
typename MakeUnsigned<T>::Type ux = aX;
|
||||
typename MakeUnsigned<T>::Type uy = aY;
|
||||
typename MakeUnsigned<T>::Type result = ux + uy;
|
||||
return IsSigned<T>::value
|
||||
? HasSignBit(BinaryComplement(T((result ^ x) & (result ^ y))))
|
||||
: BinaryComplement(x) >= y;
|
||||
? HasSignBit(BinaryComplement(T((result ^ aX) & (result ^ aY))))
|
||||
: BinaryComplement(aX) >= aY;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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
|
||||
// same sign. Since the value of x-y is undefined if we have a signed type,
|
||||
// we compute it using the unsigned type of the same size.
|
||||
typename MakeUnsigned<T>::Type ux = x;
|
||||
typename MakeUnsigned<T>::Type uy = y;
|
||||
// Subtraction is valid if either aX and aY have same sign, or aX-aY and aX
|
||||
// have same sign. Since the value of aX-aY is undefined if we have a signed
|
||||
// type, we compute it using the unsigned type of the same size.
|
||||
typename MakeUnsigned<T>::Type ux = aX;
|
||||
typename MakeUnsigned<T>::Type uy = aY;
|
||||
typename MakeUnsigned<T>::Type result = ux - uy;
|
||||
|
||||
return IsSigned<T>::value
|
||||
? HasSignBit(BinaryComplement(T((result ^ x) & (x ^ y))))
|
||||
: x >= y;
|
||||
? HasSignBit(BinaryComplement(T((result ^ aX) & (aX ^ aY))))
|
||||
: aX >= aY;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
@ -292,61 +293,62 @@ struct IsMulValidImpl {};
|
||||
template<typename T, bool IsTSigned>
|
||||
struct IsMulValidImpl<T, IsTSigned, true>
|
||||
{
|
||||
static bool run(T x, T y)
|
||||
{
|
||||
typedef typename TwiceBiggerType<T>::Type TwiceBiggerType;
|
||||
TwiceBiggerType product = TwiceBiggerType(x) * TwiceBiggerType(y);
|
||||
return IsInRange<T>(product);
|
||||
}
|
||||
static bool run(T aX, T aY)
|
||||
{
|
||||
typedef typename TwiceBiggerType<T>::Type TwiceBiggerType;
|
||||
TwiceBiggerType product = TwiceBiggerType(aX) * TwiceBiggerType(aY);
|
||||
return IsInRange<T>(product);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsMulValidImpl<T, true, false>
|
||||
{
|
||||
static bool run(T x, T y)
|
||||
{
|
||||
const T max = MaxValue<T>::value;
|
||||
const T min = MinValue<T>::value;
|
||||
static bool run(T aX, T aY)
|
||||
{
|
||||
const T max = MaxValue<T>::value;
|
||||
const T min = MinValue<T>::value;
|
||||
|
||||
if (x == 0 || y == 0)
|
||||
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 || aY == 0) {
|
||||
return true;
|
||||
}
|
||||
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>
|
||||
struct IsMulValidImpl<T, false, false>
|
||||
{
|
||||
static bool run(T x, T y)
|
||||
{
|
||||
return y == 0 || x <= MaxValue<T>::value / y;
|
||||
}
|
||||
static bool run(T aX, T aY)
|
||||
{
|
||||
return aY == 0 || aX <= MaxValue<T>::value / aY;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
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>
|
||||
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.
|
||||
return y != 0 &&
|
||||
!(IsSigned<T>::value && x == MinValue<T>::value && y == T(-1));
|
||||
// Keep in mind that in the signed case, min/-1 is invalid because
|
||||
// abs(min)>max.
|
||||
return aY != 0 &&
|
||||
!(IsSigned<T>::value && aX == MinValue<T>::value && aY == T(-1));
|
||||
}
|
||||
|
||||
template<typename T, bool IsTSigned = IsSigned<T>::value>
|
||||
@ -354,36 +356,40 @@ struct IsModValidImpl;
|
||||
|
||||
template<typename T>
|
||||
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.
|
||||
* 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.
|
||||
* Undefined for y=0.
|
||||
* The result will never exceed either x or y.
|
||||
* Undefined for aY=0.
|
||||
* 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>
|
||||
struct IsModValidImpl<T, false> {
|
||||
static inline bool run(T x, T y) {
|
||||
return y >= 1;
|
||||
struct IsModValidImpl<T, false>
|
||||
{
|
||||
static inline bool run(T aX, T aY)
|
||||
{
|
||||
return aY >= 1;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsModValidImpl<T, true> {
|
||||
static inline bool run(T x, T y) {
|
||||
if (x < 0)
|
||||
struct IsModValidImpl<T, true>
|
||||
{
|
||||
static inline bool run(T aX, T aY)
|
||||
{
|
||||
if (aX < 0) {
|
||||
return false;
|
||||
|
||||
return y >= 1;
|
||||
}
|
||||
return aY >= 1;
|
||||
}
|
||||
};
|
||||
|
||||
@ -393,25 +399,26 @@ struct NegateImpl;
|
||||
template<typename T>
|
||||
struct NegateImpl<T, false>
|
||||
{
|
||||
static CheckedInt<T> negate(const CheckedInt<T>& val)
|
||||
{
|
||||
// Handle negation separately for signed/unsigned, for simpler code and to
|
||||
// avoid an MSVC warning negating an unsigned value.
|
||||
return CheckedInt<T>(0, val.isValid() && val.mValue == 0);
|
||||
}
|
||||
static CheckedInt<T> negate(const CheckedInt<T>& aVal)
|
||||
{
|
||||
// Handle negation separately for signed/unsigned, for simpler code and to
|
||||
// avoid an MSVC warning negating an unsigned value.
|
||||
return CheckedInt<T>(0, aVal.isValid() && aVal.mValue == 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct NegateImpl<T, true>
|
||||
{
|
||||
static CheckedInt<T> negate(const CheckedInt<T>& val)
|
||||
{
|
||||
// Watch out for the min-value, which (with twos-complement) can't be
|
||||
// negated as -min-value is then (max-value + 1).
|
||||
if (!val.isValid() || val.mValue == MinValue<T>::value)
|
||||
return CheckedInt<T>(val.mValue, false);
|
||||
return CheckedInt<T>(-val.mValue, true);
|
||||
static CheckedInt<T> negate(const CheckedInt<T>& aVal)
|
||||
{
|
||||
// Watch out for the min-value, which (with twos-complement) can't be
|
||||
// negated as -min-value is then (max-value + 1).
|
||||
if (!aVal.isValid() || aVal.mValue == MinValue<T>::value) {
|
||||
return CheckedInt<T>(aVal.mValue, false);
|
||||
}
|
||||
return CheckedInt<T>(-aVal.mValue, true);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
@ -437,18 +444,18 @@ struct NegateImpl<T, true>
|
||||
* (e.g. in case of a division by zero).
|
||||
*
|
||||
* 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:
|
||||
@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;
|
||||
if (checkedResult.isValid()) {
|
||||
*result = checkedResult.value();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
CheckedInt<int> checkedResult = (CheckedInt<int>(aX) + aY) / aZ;
|
||||
if (checkedResult.isValid()) {
|
||||
*aResult = checkedResult.value();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*
|
||||
@ -491,193 +498,188 @@ struct NegateImpl<T, true>
|
||||
template<typename T>
|
||||
class CheckedInt
|
||||
{
|
||||
protected:
|
||||
T mValue;
|
||||
bool mIsValid;
|
||||
protected:
|
||||
T mValue;
|
||||
bool mIsValid;
|
||||
|
||||
template<typename U>
|
||||
CheckedInt(U aValue, bool aIsValid) : mValue(aValue), mIsValid(aIsValid)
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value &&
|
||||
detail::IsSupported<U>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
template<typename U>
|
||||
CheckedInt(U aValue, bool aIsValid) : mValue(aValue), mIsValid(aIsValid)
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value &&
|
||||
detail::IsSupported<U>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
|
||||
friend struct detail::NegateImpl<T>;
|
||||
friend struct detail::NegateImpl<T>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a checked integer with given @a value. The checked integer is
|
||||
* initialized as valid or invalid depending on whether the @a value
|
||||
* is in range.
|
||||
*
|
||||
* This constructor is not explicit. Instead, the type of its argument is a
|
||||
* separate template parameter, ensuring that no conversion is performed
|
||||
* before this constructor is actually called. As explained in the above
|
||||
* documentation for class CheckedInt, this constructor checks that its
|
||||
* argument is valid.
|
||||
*/
|
||||
template<typename U>
|
||||
CheckedInt(U aValue)
|
||||
: mValue(T(aValue)),
|
||||
mIsValid(detail::IsInRange<T>(aValue))
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value &&
|
||||
detail::IsSupported<U>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
public:
|
||||
/**
|
||||
* Constructs a checked integer with given @a value. The checked integer is
|
||||
* initialized as valid or invalid depending on whether the @a value
|
||||
* is in range.
|
||||
*
|
||||
* This constructor is not explicit. Instead, the type of its argument is a
|
||||
* separate template parameter, ensuring that no conversion is performed
|
||||
* before this constructor is actually called. As explained in the above
|
||||
* documentation for class CheckedInt, this constructor checks that its
|
||||
* argument is valid.
|
||||
*/
|
||||
template<typename U>
|
||||
CheckedInt(U aValue)
|
||||
: mValue(T(aValue)),
|
||||
mIsValid(detail::IsInRange<T>(aValue))
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value &&
|
||||
detail::IsSupported<U>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
friend class CheckedInt;
|
||||
template<typename U>
|
||||
friend class CheckedInt;
|
||||
|
||||
template<typename U>
|
||||
CheckedInt<U> toChecked() const
|
||||
{
|
||||
CheckedInt<U> ret(mValue);
|
||||
ret.mIsValid = ret.mIsValid && mIsValid;
|
||||
return ret;
|
||||
}
|
||||
template<typename U>
|
||||
CheckedInt<U> toChecked() const
|
||||
{
|
||||
CheckedInt<U> ret(mValue);
|
||||
ret.mIsValid = ret.mIsValid && mIsValid;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Constructs a valid checked integer with initial value 0 */
|
||||
CheckedInt() : mValue(0), mIsValid(true)
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
/** Constructs a valid checked integer with initial value 0 */
|
||||
CheckedInt() : mValue(0), mIsValid(true)
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value,
|
||||
"This type is not supported by CheckedInt");
|
||||
}
|
||||
|
||||
/** @returns the actual value */
|
||||
T value() const
|
||||
{
|
||||
MOZ_ASSERT(mIsValid, "Invalid checked integer (division by zero or integer overflow)");
|
||||
return mValue;
|
||||
}
|
||||
/** @returns the actual value */
|
||||
T value() const
|
||||
{
|
||||
MOZ_ASSERT(mIsValid, "Invalid checked integer (division by zero or integer overflow)");
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* integer
|
||||
*/
|
||||
bool isValid() const
|
||||
{
|
||||
return mIsValid;
|
||||
}
|
||||
/**
|
||||
* @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
|
||||
* integer
|
||||
*/
|
||||
bool isValid() const
|
||||
{
|
||||
return mIsValid;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator +(const CheckedInt<U>& lhs,
|
||||
const CheckedInt<U>& rhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator +=(U rhs);
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator +(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator +=(U aRhs);
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator -(const CheckedInt<U>& lhs,
|
||||
const CheckedInt<U>& rhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator -=(U rhs);
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator -(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator -=(U aRhs);
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator *(const CheckedInt<U>& lhs,
|
||||
const CheckedInt<U>& rhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator *=(U rhs);
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator *(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator *=(U aRhs);
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator /(const CheckedInt<U>& lhs,
|
||||
const CheckedInt<U>& rhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator /=(U rhs);
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator /(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator /=(U aRhs);
|
||||
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator %(const CheckedInt<U>& lhs,
|
||||
const CheckedInt<U>& rhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator %=(U rhs);
|
||||
template<typename U>
|
||||
friend CheckedInt<U> operator %(const CheckedInt<U>& aLhs,
|
||||
const CheckedInt<U>& aRhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator %=(U aRhs);
|
||||
|
||||
CheckedInt operator -() const
|
||||
{
|
||||
return detail::NegateImpl<T>::negate(*this);
|
||||
}
|
||||
CheckedInt operator -() const
|
||||
{
|
||||
return detail::NegateImpl<T>::negate(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the left and right hand sides are valid
|
||||
* and have the same value.
|
||||
*
|
||||
* 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)
|
||||
* but that would mean that whenever a or b is invalid, a!=b
|
||||
* is always true, which would be very confusing.
|
||||
*
|
||||
* For similar reasons, operators <, >, <=, >= would be very tricky to
|
||||
* specify, so we just avoid offering them.
|
||||
*
|
||||
* Notice that these == semantics are made more reasonable by these facts:
|
||||
* 1. a==b implies equality at the raw data level
|
||||
* (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
|
||||
* means that a and b have the same value *and* neither is NaN.
|
||||
*/
|
||||
bool operator ==(const CheckedInt& other) const
|
||||
{
|
||||
return mIsValid && other.mIsValid && mValue == other.mValue;
|
||||
}
|
||||
/**
|
||||
* @returns true if the left and right hand sides are valid
|
||||
* and have the same value.
|
||||
*
|
||||
* 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)
|
||||
* but that would mean that whenever a or b is invalid, a!=b
|
||||
* is always true, which would be very confusing.
|
||||
*
|
||||
* For similar reasons, operators <, >, <=, >= would be very tricky to
|
||||
* specify, so we just avoid offering them.
|
||||
*
|
||||
* Notice that these == semantics are made more reasonable by these facts:
|
||||
* 1. a==b implies equality at the raw data level
|
||||
* (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
|
||||
* means that a and b have the same value *and* neither is NaN.
|
||||
*/
|
||||
bool operator ==(const CheckedInt& aOther) const
|
||||
{
|
||||
return mIsValid && aOther.mIsValid && mValue == aOther.mValue;
|
||||
}
|
||||
|
||||
/** prefix ++ */
|
||||
CheckedInt& operator++()
|
||||
{
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
/** prefix ++ */
|
||||
CheckedInt& operator++()
|
||||
{
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** postfix ++ */
|
||||
CheckedInt operator++(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this += 1;
|
||||
return tmp;
|
||||
}
|
||||
/** postfix ++ */
|
||||
CheckedInt operator++(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/** prefix -- */
|
||||
CheckedInt& operator--()
|
||||
{
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
/** prefix -- */
|
||||
CheckedInt& operator--()
|
||||
{
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** postfix -- */
|
||||
CheckedInt operator--(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this -= 1;
|
||||
return tmp;
|
||||
}
|
||||
/** postfix -- */
|
||||
CheckedInt operator--(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this -= 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* The !=, <, <=, >, >= operators are disabled:
|
||||
* see the comment on operator==.
|
||||
*/
|
||||
template<typename U>
|
||||
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;
|
||||
template<typename U>
|
||||
bool operator >(U other) const MOZ_DELETE;
|
||||
template<typename U>
|
||||
bool operator >=(U other) const MOZ_DELETE;
|
||||
private:
|
||||
/**
|
||||
* The !=, <, <=, >, >= operators are disabled:
|
||||
* see the comment on operator==.
|
||||
*/
|
||||
template<typename U> bool operator !=(U aOther) const MOZ_DELETE;
|
||||
template<typename U> bool operator < (U aOther) const MOZ_DELETE;
|
||||
template<typename U> bool operator <=(U aOther) const MOZ_DELETE;
|
||||
template<typename U> bool operator > (U aOther) const MOZ_DELETE;
|
||||
template<typename U> bool operator >=(U aOther) const MOZ_DELETE;
|
||||
};
|
||||
|
||||
#define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
|
||||
template<typename T> \
|
||||
inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, \
|
||||
const CheckedInt<T> &rhs) \
|
||||
{ \
|
||||
if (!detail::Is##NAME##Valid(lhs.mValue, rhs.mValue)) \
|
||||
return CheckedInt<T>(0, false); \
|
||||
\
|
||||
return CheckedInt<T>(lhs.mValue OP rhs.mValue, \
|
||||
lhs.mIsValid && rhs.mIsValid); \
|
||||
}
|
||||
#define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
|
||||
template<typename T> \
|
||||
inline CheckedInt<T> \
|
||||
operator OP(const CheckedInt<T> &aLhs, const CheckedInt<T> &aRhs) \
|
||||
{ \
|
||||
if (!detail::Is##NAME##Valid(aLhs.mValue, aRhs.mValue)) { \
|
||||
return CheckedInt<T>(0, false); \
|
||||
} \
|
||||
return CheckedInt<T>(aLhs.mValue OP aRhs.mValue, \
|
||||
aLhs.mIsValid && aRhs.mIsValid); \
|
||||
}
|
||||
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +)
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -)
|
||||
@ -698,47 +700,47 @@ namespace detail {
|
||||
template<typename T, typename U>
|
||||
struct CastToCheckedIntImpl
|
||||
{
|
||||
typedef CheckedInt<T> ReturnType;
|
||||
static CheckedInt<T> run(U u) { return u; }
|
||||
typedef CheckedInt<T> ReturnType;
|
||||
static CheckedInt<T> run(U aU) { return aU; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CastToCheckedIntImpl<T, CheckedInt<T> >
|
||||
{
|
||||
typedef const CheckedInt<T>& ReturnType;
|
||||
static const CheckedInt<T>& run(const CheckedInt<T>& u) { return u; }
|
||||
typedef const CheckedInt<T>& ReturnType;
|
||||
static const CheckedInt<T>& run(const CheckedInt<T>& aU) { return aU; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, typename U>
|
||||
inline typename detail::CastToCheckedIntImpl<T, U>::ReturnType
|
||||
castToCheckedInt(U u)
|
||||
castToCheckedInt(U aU)
|
||||
{
|
||||
static_assert(detail::IsSupported<T>::value &&
|
||||
detail::IsSupported<U>::value,
|
||||
"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) \
|
||||
template<typename T> \
|
||||
template<typename U> \
|
||||
CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U rhs) \
|
||||
{ \
|
||||
*this = *this OP castToCheckedInt<T>(rhs); \
|
||||
return *this; \
|
||||
} \
|
||||
template<typename T, typename U> \
|
||||
inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, U rhs) \
|
||||
{ \
|
||||
return lhs OP castToCheckedInt<T>(rhs); \
|
||||
} \
|
||||
template<typename T, typename U> \
|
||||
inline CheckedInt<T> operator OP(U lhs, const CheckedInt<T> &rhs) \
|
||||
{ \
|
||||
return castToCheckedInt<T>(lhs) OP rhs; \
|
||||
}
|
||||
#define MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \
|
||||
template<typename T> \
|
||||
template<typename U> \
|
||||
CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(U aRhs) \
|
||||
{ \
|
||||
*this = *this OP castToCheckedInt<T>(aRhs); \
|
||||
return *this; \
|
||||
} \
|
||||
template<typename T, typename U> \
|
||||
inline CheckedInt<T> operator OP(const CheckedInt<T> &aLhs, U aRhs) \
|
||||
{ \
|
||||
return aLhs OP castToCheckedInt<T>(aRhs); \
|
||||
} \
|
||||
template<typename T, typename U> \
|
||||
inline CheckedInt<T> operator OP(U aLhs, const CheckedInt<T> &aRhs) \
|
||||
{ \
|
||||
return castToCheckedInt<T>(aLhs) OP aRhs; \
|
||||
}
|
||||
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
|
||||
@ -750,16 +752,16 @@ MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(%, %=)
|
||||
|
||||
template<typename T, typename U>
|
||||
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>
|
||||
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.
|
||||
|
@ -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
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/Compression.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
using namespace mozilla::Compression;
|
||||
|
||||
namespace {
|
||||
@ -15,50 +18,51 @@ namespace {
|
||||
/* Our wrappers */
|
||||
|
||||
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;
|
||||
MOZ_ASSERT(inputSizeChecked.isValid());
|
||||
return LZ4_compress(source, dest, inputSizeChecked.value());
|
||||
CheckedInt<int> inputSizeChecked = aInputSize;
|
||||
MOZ_ASSERT(inputSizeChecked.isValid());
|
||||
return LZ4_compress(aSource, aDest, inputSizeChecked.value());
|
||||
}
|
||||
|
||||
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;
|
||||
MOZ_ASSERT(inputSizeChecked.isValid());
|
||||
CheckedInt<int> maxOutputSizeChecked = maxOutputSize;
|
||||
MOZ_ASSERT(maxOutputSizeChecked.isValid());
|
||||
return LZ4_compress_limitedOutput(source, dest, inputSizeChecked.value(),
|
||||
maxOutputSizeChecked.value());
|
||||
CheckedInt<int> inputSizeChecked = aInputSize;
|
||||
MOZ_ASSERT(inputSizeChecked.isValid());
|
||||
CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
|
||||
MOZ_ASSERT(maxOutputSizeChecked.isValid());
|
||||
return LZ4_compress_limitedOutput(aSource, aDest, inputSizeChecked.value(),
|
||||
maxOutputSizeChecked.value());
|
||||
}
|
||||
|
||||
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;
|
||||
MOZ_ASSERT(outputSizeChecked.isValid());
|
||||
int ret = LZ4_decompress_fast(source, dest, outputSizeChecked.value());
|
||||
return ret >= 0;
|
||||
CheckedInt<int> outputSizeChecked = aOutputSize;
|
||||
MOZ_ASSERT(outputSizeChecked.isValid());
|
||||
int ret = LZ4_decompress_fast(aSource, aDest, outputSizeChecked.value());
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
bool
|
||||
LZ4::decompress(const char* source, size_t inputSize, char* dest, size_t maxOutputSize,
|
||||
size_t *outputSize)
|
||||
LZ4::decompress(const char* aSource, size_t aInputSize, char* aDest,
|
||||
size_t aMaxOutputSize, size_t *aOutputSize)
|
||||
{
|
||||
CheckedInt<int> maxOutputSizeChecked = maxOutputSize;
|
||||
MOZ_ASSERT(maxOutputSizeChecked.isValid());
|
||||
CheckedInt<int> inputSizeChecked = inputSize;
|
||||
MOZ_ASSERT(inputSizeChecked.isValid());
|
||||
CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
|
||||
MOZ_ASSERT(maxOutputSizeChecked.isValid());
|
||||
CheckedInt<int> inputSizeChecked = aInputSize;
|
||||
MOZ_ASSERT(inputSizeChecked.isValid());
|
||||
|
||||
int ret = LZ4_decompress_safe(source, dest, inputSizeChecked.value(),
|
||||
maxOutputSizeChecked.value());
|
||||
if (ret >= 0) {
|
||||
*outputSize = ret;
|
||||
return true;
|
||||
} else {
|
||||
*outputSize = 0;
|
||||
return false;
|
||||
}
|
||||
int ret = LZ4_decompress_safe(aSource, aDest, inputSizeChecked.value(),
|
||||
maxOutputSizeChecked.value());
|
||||
if (ret >= 0) {
|
||||
*aOutputSize = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
*aOutputSize = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
* 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/. */
|
||||
@ -22,41 +23,41 @@ namespace Compression {
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
class LZ4
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Compresses 'inputSize' bytes from 'source' into 'dest'.
|
||||
* Destination buffer must be already allocated,
|
||||
* and must be sized to handle worst cases situations (input data not compressible)
|
||||
* Worst case size evaluation is provided by function maxCompressedSize()
|
||||
* Compresses |aInputSize| bytes from |aSource| into |aDest|. Destination
|
||||
* buffer must be already allocated, and must be sized to handle worst cases
|
||||
* situations (input data not compressible). Worst case size evaluation is
|
||||
* provided by function maxCompressedSize()
|
||||
*
|
||||
* @param inputSize is the input size. Max supported value is ~1.9GB
|
||||
* @param return the number of bytes written in buffer dest
|
||||
* @param aInputSize is the input size. Max supported value is ~1.9GB
|
||||
* @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
|
||||
* 'dest' of maximum size 'maxOutputSize'. If it cannot achieve it,
|
||||
* Compress |aInputSize| bytes from |aSource| into an output buffer
|
||||
* |aDest| of maximum size |aMaxOutputSize|. If it cannot achieve it,
|
||||
* 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.
|
||||
*
|
||||
* This function never writes outside of provided output buffer.
|
||||
*
|
||||
* @param inputSize is the input size. Max supported value is ~1.9GB
|
||||
* @param maxOutputSize is the size of the destination buffer (which must be already allocated)
|
||||
* @return the number of bytes written in buffer 'dest'
|
||||
or 0 if the compression fails
|
||||
*/
|
||||
static MFBT_API size_t compressLimitedOutput(const char* source, size_t inputSize, char* dest,
|
||||
size_t maxOutputSize);
|
||||
* @param aInputSize is the input size. Max supported value is ~1.9GB
|
||||
* @param aMaxOutputSize is the size of the destination buffer (which must
|
||||
* be already allocated)
|
||||
* @return the number of bytes written in buffer |aDest| or 0 if the
|
||||
* compression fails
|
||||
*/
|
||||
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
|
||||
@ -66,48 +67,50 @@ public:
|
||||
* This function never writes outside of provided buffers, and never
|
||||
* modifies input buffer.
|
||||
*
|
||||
* note : destination buffer must be already allocated.
|
||||
* its size must be a minimum of 'outputSize' bytes.
|
||||
* @param outputSize is the output size, therefore the original size
|
||||
* Note: destination buffer must be already allocated, and its size must be a
|
||||
* minimum of |aOutputSize| bytes.
|
||||
*
|
||||
* @param aOutputSize is the output size, therefore the original size
|
||||
* @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
|
||||
* 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.
|
||||
*
|
||||
* note : Destination buffer must be already allocated.
|
||||
* This version is slightly slower than the decompress
|
||||
* without the maxOutputSize
|
||||
* Note: Destination buffer must be already allocated. This version is
|
||||
* slightly slower than the decompress without the aMaxOutputSize.
|
||||
*
|
||||
* @param inputSize is the length of the input compressed data
|
||||
* @param maxOutputSize is the size of the destination buffer (which must be already allocated)
|
||||
* @param outputSize the actual number of bytes decoded in the destination buffer (necessarily <= maxOutputSize)
|
||||
|
||||
*/
|
||||
static MFBT_API bool decompress(const char* source, size_t inputSize, char* dest,
|
||||
size_t maxOutputSize, size_t *outputSize);
|
||||
* @param aInputSize is the length of the input compressed data
|
||||
* @param aMaxOutputSize is the size of the destination buffer (which must be
|
||||
* already allocated)
|
||||
* @param aOutputSize the actual number of bytes decoded in the destination
|
||||
* buffer (necessarily <= aMaxOutputSize)
|
||||
*/
|
||||
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"
|
||||
scenario (input data not compressible) primarily useful for memory
|
||||
allocation of output buffer.
|
||||
note : this function is limited by "int" range (2^31-1)
|
||||
|
||||
@param inputSize is the input size. Max supported value is ~1.9GB
|
||||
@return maximum output size in a "worst case" scenario
|
||||
*/
|
||||
static inline size_t maxCompressedSize(size_t inputSize)
|
||||
* Provides the maximum size that LZ4 may output in a "worst case"
|
||||
* scenario (input data not compressible) primarily useful for memory
|
||||
* allocation of output buffer.
|
||||
* note : this function is limited by "int" range (2^31-1)
|
||||
*
|
||||
* @param aInputSize is the input size. Max supported value is ~1.9GB
|
||||
* @return maximum output size in a "worst case" scenario
|
||||
*/
|
||||
static inline size_t maxCompressedSize(size_t aInputSize)
|
||||
{
|
||||
size_t max = ((inputSize) + ((inputSize)/255) + 16);
|
||||
MOZ_ASSERT(max > inputSize);
|
||||
return max;
|
||||
size_t max = (aInputSize + (aInputSize / 255) + 16);
|
||||
MOZ_ASSERT(max > aInputSize);
|
||||
return max;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} /* namespace Compression */
|
||||
|
@ -34,47 +34,44 @@ namespace mozilla {
|
||||
template<typename T>
|
||||
class DebugOnly
|
||||
{
|
||||
public:
|
||||
public:
|
||||
#ifdef DEBUG
|
||||
T value;
|
||||
T value;
|
||||
|
||||
DebugOnly() { }
|
||||
DebugOnly(const T& other) : value(other) { }
|
||||
DebugOnly(const DebugOnly& other) : value(other.value) { }
|
||||
DebugOnly& operator=(const T& rhs) {
|
||||
value = rhs;
|
||||
return *this;
|
||||
}
|
||||
void operator++(int) {
|
||||
value++;
|
||||
}
|
||||
void operator--(int) {
|
||||
value--;
|
||||
}
|
||||
DebugOnly() { }
|
||||
DebugOnly(const T& aOther) : value(aOther) { }
|
||||
DebugOnly(const DebugOnly& aOther) : value(aOther.value) { }
|
||||
DebugOnly& operator=(const T& aRhs) {
|
||||
value = aRhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* operator&() { return &value; }
|
||||
void operator++(int) { value++; }
|
||||
void operator--(int) { value--; }
|
||||
|
||||
operator T&() { return value; }
|
||||
operator const T&() const { return value; }
|
||||
T* operator&() { return &value; }
|
||||
|
||||
T& operator->() { return value; }
|
||||
const T& operator->() const { return value; }
|
||||
operator T&() { return value; }
|
||||
operator const T&() const { return value; }
|
||||
|
||||
T& operator->() { return value; }
|
||||
const T& operator->() const { return value; }
|
||||
|
||||
#else
|
||||
DebugOnly() { }
|
||||
DebugOnly(const T&) { }
|
||||
DebugOnly(const DebugOnly&) { }
|
||||
DebugOnly& operator=(const T&) { return *this; }
|
||||
void operator++(int) { }
|
||||
void operator--(int) { }
|
||||
DebugOnly() { }
|
||||
DebugOnly(const T&) { }
|
||||
DebugOnly(const DebugOnly&) { }
|
||||
DebugOnly& operator=(const T&) { return *this; }
|
||||
void operator++(int) { }
|
||||
void operator--(int) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* DebugOnly must always have a destructor or else it will
|
||||
* generate "unused variable" warnings, exactly what it's intended
|
||||
* to avoid!
|
||||
*/
|
||||
~DebugOnly() {}
|
||||
/*
|
||||
* DebugOnly must always have a destructor or else it will
|
||||
* generate "unused variable" warnings, exactly what it's intended
|
||||
* to avoid!
|
||||
*/
|
||||
~DebugOnly() {}
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user