зеркало из https://github.com/mozilla/gecko-dev.git
Backout 345ae68f15f4, b3b40121ac8d, 0d18b7a246d7, 9dbb6064ab58, dee9d7fa8eb6, 63eec6bfa948, 323c6be7cfe8 & f4aac7523a48 (bug 732875) for compilation failures
This commit is contained in:
Родитель
fa8d5e0469
Коммит
0f956d7307
|
@ -70,7 +70,7 @@ nsDOMMultipartFile::GetSize(PRUint64* aLength)
|
|||
length += l;
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(length.isValid(), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(length.valid(), NS_ERROR_FAILURE);
|
||||
|
||||
mLength = length.value();
|
||||
}
|
||||
|
|
|
@ -39,8 +39,9 @@
|
|||
#define nsDOMBlobBuilder_h
|
||||
|
||||
#include "nsDOMFile.h"
|
||||
#include "CheckedInt.h"
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/StandardInteger.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -127,10 +128,10 @@ protected:
|
|||
|
||||
// Start at 1 or we'll loop forever.
|
||||
CheckedUint32 bufferLen = NS_MAX<PRUint32>(mDataBufferLen, 1);
|
||||
while (bufferLen.isValid() && bufferLen.value() < mDataLen + aSize)
|
||||
while (bufferLen.valid() && bufferLen.value() < mDataLen + aSize)
|
||||
bufferLen *= 2;
|
||||
|
||||
if (!bufferLen.isValid())
|
||||
if (!bufferLen.valid())
|
||||
return false;
|
||||
|
||||
// PR_ memory functions are still fallible
|
||||
|
|
|
@ -61,8 +61,8 @@
|
|||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsBlobProtocolHandler.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "CheckedInt.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#include "plbase64.h"
|
||||
|
@ -227,7 +227,7 @@ ParseSize(PRInt64 aSize, PRInt64& aStart, PRInt64& aEnd)
|
|||
newEndOffset = aSize;
|
||||
}
|
||||
|
||||
if (!newStartOffset.isValid() || !newEndOffset.isValid() ||
|
||||
if (!newStartOffset.valid() || !newEndOffset.valid() ||
|
||||
newStartOffset.value() >= newEndOffset.value()) {
|
||||
aStart = aEnd = 0;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
#include "prtypes.h"
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "CheckedInt.h"
|
||||
|
||||
class nsHTMLCanvasElement;
|
||||
class nsIPrincipal;
|
||||
|
@ -63,9 +63,9 @@ inline bool CheckSaneSubrectSize(PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h,
|
|||
CheckedInt32 checked_ymost = CheckedInt32(y) + h;
|
||||
|
||||
return w >= 0 && h >= 0 && x >= 0 && y >= 0 &&
|
||||
checked_xmost.isValid() &&
|
||||
checked_xmost.valid() &&
|
||||
checked_xmost.value() <= realWidth &&
|
||||
checked_ymost.isValid() &&
|
||||
checked_ymost.valid() &&
|
||||
checked_ymost.value() <= realHeight;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
#include "nsDOMError.h"
|
||||
#include "nsIDOMCanvasRenderingContext2D.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "CheckedInt.h"
|
||||
#include "nsMathUtils.h"
|
||||
#include "CustomQS_Canvas.h"
|
||||
|
||||
|
@ -169,7 +169,7 @@ CreateImageData(JSContext* cx, JSObject* obj, uint32_t w, uint32_t h, jsval* vp)
|
|||
h = 1;
|
||||
|
||||
CheckedInt<uint32_t> len = CheckedInt<uint32_t>(w) * h * 4;
|
||||
if (!len.isValid()) {
|
||||
if (!len.valid()) {
|
||||
return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
}
|
||||
|
||||
|
|
|
@ -425,7 +425,7 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
|
|||
// If incrementing the generation would cause overflow,
|
||||
// don't allow it. Allowing this would allow us to use
|
||||
// resource handles created from older context generations.
|
||||
if (!(mGeneration + 1).isValid())
|
||||
if (!(mGeneration+1).valid())
|
||||
return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
|
||||
|
||||
gl::ContextFormat format(gl::ContextFormat::BasicRGBA32);
|
||||
|
|
|
@ -64,9 +64,9 @@
|
|||
#include "GLContextProvider.h"
|
||||
#include "Layers.h"
|
||||
|
||||
#include "CheckedInt.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/ImageData.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
|
@ -481,7 +481,7 @@ public:
|
|||
private:
|
||||
WebGLMonotonicHandle NextMonotonicHandle() {
|
||||
++mCurrentMonotonicHandle;
|
||||
if (!mCurrentMonotonicHandle.isValid())
|
||||
if (!mCurrentMonotonicHandle.valid())
|
||||
NS_RUNTIMEABORT("ran out of monotonic ids!");
|
||||
return mCurrentMonotonicHandle.value();
|
||||
}
|
||||
|
@ -1748,7 +1748,7 @@ public:
|
|||
|
||||
bool HasImageInfoAt(size_t level, size_t face) const {
|
||||
CheckedUint32 checked_index = CheckedUint32(level) * mFacesCount + face;
|
||||
return checked_index.isValid() &&
|
||||
return checked_index.valid() &&
|
||||
checked_index.value() < mImageInfos.Length() &&
|
||||
ImageInfoAt(level, face).mIsDefined;
|
||||
}
|
||||
|
@ -2328,7 +2328,7 @@ public:
|
|||
|
||||
bool NextGeneration()
|
||||
{
|
||||
if (!(mGeneration + 1).isValid())
|
||||
if (!(mGeneration+1).valid())
|
||||
return false; // must exit without changing mGeneration
|
||||
++mGeneration;
|
||||
return true;
|
||||
|
|
|
@ -687,7 +687,7 @@ WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
|
|||
return ErrorInvalidOperation("BufferData: no buffer bound!");
|
||||
|
||||
CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data->mLength;
|
||||
if (!checked_neededByteLength.isValid())
|
||||
if (!checked_neededByteLength.valid())
|
||||
return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
|
||||
|
||||
if (checked_neededByteLength.value() > boundBuffer->ByteLength())
|
||||
|
@ -726,7 +726,7 @@ WebGLContext::BufferSubData(WebGLenum target, WebGLsizeiptr byteOffset,
|
|||
return ErrorInvalidOperation("BufferSubData: no buffer bound!");
|
||||
|
||||
CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data.mLength;
|
||||
if (!checked_neededByteLength.isValid())
|
||||
if (!checked_neededByteLength.valid())
|
||||
return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
|
||||
|
||||
if (checked_neededByteLength.value() > boundBuffer->ByteLength())
|
||||
|
@ -941,7 +941,7 @@ WebGLContext::CopyTexSubImage2D_base(WebGLenum target,
|
|||
CheckedUint32 checked_neededByteLength =
|
||||
GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment);
|
||||
|
||||
if (!checked_neededByteLength.isValid())
|
||||
if (!checked_neededByteLength.valid())
|
||||
return ErrorInvalidOperation("%s: integer overflow computing the needed buffer size", info);
|
||||
|
||||
PRUint32 bytesNeeded = checked_neededByteLength.value();
|
||||
|
@ -1562,7 +1562,7 @@ WebGLContext::DoFakeVertexAttrib0(WebGLuint vertexCount)
|
|||
|
||||
CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(WebGLfloat);
|
||||
|
||||
if (!checked_dataSize.isValid()) {
|
||||
if (!checked_dataSize.valid()) {
|
||||
ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
|
||||
"with %d vertices. Try reducing the number of vertices.", vertexCount);
|
||||
return false;
|
||||
|
@ -1779,7 +1779,7 @@ WebGLContext::DrawArrays(GLenum mode, WebGLint first, WebGLsizei count)
|
|||
|
||||
CheckedInt32 checked_firstPlusCount = CheckedInt32(first) + count;
|
||||
|
||||
if (!checked_firstPlusCount.isValid())
|
||||
if (!checked_firstPlusCount.valid())
|
||||
return ErrorInvalidOperation("drawArrays: overflow in first+count");
|
||||
|
||||
if (checked_firstPlusCount.value() > maxAllowedCount)
|
||||
|
@ -1847,7 +1847,7 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type,
|
|||
return ErrorInvalidEnum("DrawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
|
||||
}
|
||||
|
||||
if (!checked_byteCount.isValid())
|
||||
if (!checked_byteCount.valid())
|
||||
return ErrorInvalidValue("DrawElements: overflow in byteCount");
|
||||
|
||||
// If there is no current program, this is silently ignored.
|
||||
|
@ -1863,7 +1863,7 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type,
|
|||
|
||||
CheckedUint32 checked_neededByteCount = checked_byteCount + byteOffset;
|
||||
|
||||
if (!checked_neededByteCount.isValid())
|
||||
if (!checked_neededByteCount.valid())
|
||||
return ErrorInvalidOperation("DrawElements: overflow in byteOffset+byteCount");
|
||||
|
||||
if (checked_neededByteCount.value() > mBoundElementArrayBuffer->ByteLength())
|
||||
|
@ -1880,7 +1880,7 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type,
|
|||
|
||||
CheckedInt32 checked_maxIndexPlusOne = CheckedInt32(maxIndex) + 1;
|
||||
|
||||
if (!checked_maxIndexPlusOne.isValid() ||
|
||||
if (!checked_maxIndexPlusOne.valid() ||
|
||||
checked_maxIndexPlusOne.value() > maxAllowedCount)
|
||||
{
|
||||
// the index array contains invalid indices for the current drawing state, but they
|
||||
|
@ -1893,7 +1893,7 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type,
|
|||
|
||||
CheckedInt32 checked_maxIndexInSubArrayPlusOne = CheckedInt32(maxIndexInSubArray) + 1;
|
||||
|
||||
if (!checked_maxIndexInSubArrayPlusOne.isValid() ||
|
||||
if (!checked_maxIndexInSubArrayPlusOne.valid() ||
|
||||
checked_maxIndexInSubArrayPlusOne.value() > maxAllowedCount)
|
||||
{
|
||||
return ErrorInvalidOperation(
|
||||
|
@ -3883,7 +3883,7 @@ WebGLContext::ReadPixels(WebGLint x, WebGLint y, WebGLsizei width,
|
|||
CheckedUint32 checked_alignedRowSize =
|
||||
RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment);
|
||||
|
||||
if (!checked_neededByteLength.isValid())
|
||||
if (!checked_neededByteLength.valid())
|
||||
return ErrorInvalidOperation("ReadPixels: integer overflow computing the needed buffer size");
|
||||
|
||||
if (checked_neededByteLength.value() > dataByteLen)
|
||||
|
@ -5601,7 +5601,7 @@ WebGLContext::TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum intern
|
|||
CheckedUint32 checked_alignedRowSize =
|
||||
RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
|
||||
|
||||
if (!checked_neededByteLength.isValid())
|
||||
if (!checked_neededByteLength.valid())
|
||||
return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
|
||||
|
||||
PRUint32 bytesNeeded = checked_neededByteLength.value();
|
||||
|
@ -5842,7 +5842,7 @@ WebGLContext::TexSubImage2D_base(WebGLenum target, WebGLint level,
|
|||
CheckedUint32 checked_alignedRowSize =
|
||||
RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
|
||||
|
||||
if (!checked_neededByteLength.isValid())
|
||||
if (!checked_neededByteLength.valid())
|
||||
return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
|
||||
|
||||
PRUint32 bytesNeeded = checked_neededByteLength.value();
|
||||
|
|
|
@ -40,7 +40,8 @@
|
|||
#include "WebGLContext.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
#include "CheckedInt.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
|
@ -142,8 +143,8 @@ WebGLContext::ValidateBuffers(PRInt32 *maxAllowedCount, const char *info)
|
|||
CheckedInt32 checked_sizeOfLastElement
|
||||
= CheckedInt32(vd.componentSize()) * vd.size;
|
||||
|
||||
if (!checked_byteLength.isValid() ||
|
||||
!checked_sizeOfLastElement.isValid())
|
||||
if (!checked_byteLength.valid() ||
|
||||
!checked_sizeOfLastElement.valid())
|
||||
{
|
||||
ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
|
||||
return false;
|
||||
|
@ -155,7 +156,7 @@ WebGLContext::ValidateBuffers(PRInt32 *maxAllowedCount, const char *info)
|
|||
CheckedInt32 checked_maxAllowedCount
|
||||
= ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
|
||||
|
||||
if (!checked_maxAllowedCount.isValid()) {
|
||||
if (!checked_maxAllowedCount.valid()) {
|
||||
ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
|
||||
return false;
|
||||
}
|
||||
|
@ -400,7 +401,7 @@ bool WebGLContext::ValidateCompressedTextureSize(WebGLint level, WebGLenum forma
|
|||
{
|
||||
CheckedUint32 calculated_byteLength = 0;
|
||||
CheckedUint32 checked_byteLength = byteLength;
|
||||
if (!checked_byteLength.isValid()) {
|
||||
if (!checked_byteLength.valid()) {
|
||||
ErrorInvalidValue("%s: data length out of bounds", info);
|
||||
return false;
|
||||
}
|
||||
|
@ -410,7 +411,7 @@ bool WebGLContext::ValidateCompressedTextureSize(WebGLint level, WebGLenum forma
|
|||
case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
||||
{
|
||||
calculated_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 8;
|
||||
if (!calculated_byteLength.isValid() || !(checked_byteLength == calculated_byteLength)) {
|
||||
if (!calculated_byteLength.valid() || !(checked_byteLength == calculated_byteLength)) {
|
||||
ErrorInvalidValue("%s: data size does not match dimensions", info);
|
||||
return false;
|
||||
}
|
||||
|
@ -420,7 +421,7 @@ bool WebGLContext::ValidateCompressedTextureSize(WebGLint level, WebGLenum forma
|
|||
case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
||||
{
|
||||
calculated_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 16;
|
||||
if (!calculated_byteLength.isValid() || !(checked_byteLength == calculated_byteLength)) {
|
||||
if (!calculated_byteLength.valid() || !(checked_byteLength == calculated_byteLength)) {
|
||||
ErrorInvalidValue("%s: data size does not match dimensions", info);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
#include "nsIMemoryReporter.h"
|
||||
#include "nsStyleUtil.h"
|
||||
#include "CanvasImageCache.h"
|
||||
#include "CheckedInt.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -115,7 +116,6 @@
|
|||
#include "jsfriendapi.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ImageData.h"
|
||||
#include "mozilla/dom/PBrowserParent.h"
|
||||
|
@ -3926,14 +3926,14 @@ nsCanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
|
|||
MOZ_ASSERT(aWidth && aHeight);
|
||||
|
||||
CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
|
||||
if (!len.isValid()) {
|
||||
if (!len.valid()) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
|
||||
CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
|
||||
|
||||
if (!rightMost.isValid() || !bottomMost.isValid()) {
|
||||
if (!rightMost.valid() || !bottomMost.valid()) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
|
||||
|
@ -4066,7 +4066,7 @@ nsCanvasRenderingContext2D::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32
|
|||
|
||||
CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
|
||||
|
||||
if (!checkedDirtyX.isValid())
|
||||
if (!checkedDirtyX.valid())
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
|
||||
dirtyX = checkedDirtyX.value();
|
||||
|
@ -4078,7 +4078,7 @@ nsCanvasRenderingContext2D::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32
|
|||
|
||||
CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
|
||||
|
||||
if (!checkedDirtyY.isValid())
|
||||
if (!checkedDirtyY.valid())
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
|
||||
dirtyY = checkedDirtyY.value();
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
#include "nsIMemoryReporter.h"
|
||||
#include "nsStyleUtil.h"
|
||||
#include "CanvasImageCache.h"
|
||||
#include "CheckedInt.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -111,7 +112,6 @@
|
|||
#include "jsfriendapi.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ImageData.h"
|
||||
#include "mozilla/dom/PBrowserParent.h"
|
||||
|
@ -4095,14 +4095,14 @@ nsCanvasRenderingContext2DAzure::GetImageDataArray(JSContext* aCx,
|
|||
MOZ_ASSERT(aWidth && aHeight);
|
||||
|
||||
CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
|
||||
if (!len.isValid()) {
|
||||
if (!len.valid()) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
|
||||
CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
|
||||
|
||||
if (!rightMost.isValid() || !bottomMost.isValid()) {
|
||||
if (!rightMost.valid() || !bottomMost.valid()) {
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
}
|
||||
|
||||
|
@ -4233,7 +4233,7 @@ nsCanvasRenderingContext2DAzure::PutImageData_explicit(PRInt32 x, PRInt32 y, PRU
|
|||
|
||||
CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
|
||||
|
||||
if (!checkedDirtyX.isValid())
|
||||
if (!checkedDirtyX.valid())
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
|
||||
dirtyX = checkedDirtyX.value();
|
||||
|
@ -4245,7 +4245,7 @@ nsCanvasRenderingContext2DAzure::PutImageData_explicit(PRInt32 x, PRInt32 y, PRU
|
|||
|
||||
CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
|
||||
|
||||
if (!checkedDirtyY.isValid())
|
||||
if (!checkedDirtyY.valid())
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
|
||||
dirtyY = checkedDirtyY.value();
|
||||
|
|
|
@ -38,10 +38,10 @@
|
|||
#include "nsHTMLCanvasElement.h"
|
||||
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "prmem.h"
|
||||
#include "nsDOMFile.h"
|
||||
#include "CheckedInt.h"
|
||||
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIXPConnect.h"
|
||||
|
|
|
@ -40,12 +40,13 @@
|
|||
#define VideoUtils_h
|
||||
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
#include "nsRect.h"
|
||||
#include "nsIThreadManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "CheckedInt.h"
|
||||
|
||||
using mozilla::CheckedInt64;
|
||||
using mozilla::CheckedUint64;
|
||||
using mozilla::CheckedInt32;
|
||||
|
|
|
@ -164,8 +164,8 @@ VideoData* VideoData::Create(nsVideoInfo& aInfo,
|
|||
// the frame we've been supplied without indexing out of bounds.
|
||||
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
|
||||
CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
|
||||
if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
|
||||
!yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
|
||||
if (!xLimit.valid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
|
||||
!yLimit.valid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
|
||||
{
|
||||
// The specified picture dimensions can't be contained inside the video
|
||||
// frame, we'll stomp memory if we try to copy it. Fail.
|
||||
|
@ -346,7 +346,7 @@ nsresult nsBuiltinDecoderReader::DecodeToTarget(PRInt64 aTarget)
|
|||
break;
|
||||
CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudioRate);
|
||||
CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudioRate);
|
||||
if (!startFrame.isValid() || !targetFrame.isValid()) {
|
||||
if (!startFrame.valid() || !targetFrame.valid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (startFrame.value() + audio->mFrames <= targetFrame.value()) {
|
||||
|
@ -390,7 +390,7 @@ nsresult nsBuiltinDecoderReader::DecodeToTarget(PRInt64 aTarget)
|
|||
audio->mAudioData.get() + (framesToPrune * channels),
|
||||
frames * channels * sizeof(AudioDataValue));
|
||||
CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudioRate);
|
||||
if (!duration.isValid()) {
|
||||
if (!duration.valid()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsAutoPtr<AudioData> data(new AudioData(audio->mOffset,
|
||||
|
|
|
@ -547,7 +547,7 @@ void nsBuiltinDecoderStateMachine::SendOutputStreamAudio(AudioData* aAudio,
|
|||
CheckedInt64 audioWrittenOffset = UsecsToFrames(mInfo.mAudioRate,
|
||||
aStream->mAudioFramesWrittenBaseTime + mStartTime) + aStream->mAudioFramesWritten;
|
||||
CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudioRate, aAudio->mTime);
|
||||
if (!audioWrittenOffset.isValid() || !frameOffset.isValid())
|
||||
if (!audioWrittenOffset.valid() || !frameOffset.valid())
|
||||
return;
|
||||
if (audioWrittenOffset.value() < frameOffset.value()) {
|
||||
// Write silence to catch up
|
||||
|
@ -1115,7 +1115,7 @@ void nsBuiltinDecoderStateMachine::AudioLoop()
|
|||
// samples.
|
||||
CheckedInt64 sampleTime = UsecsToFrames(s->mTime, rate);
|
||||
CheckedInt64 missingFrames = sampleTime - playedFrames;
|
||||
if (!missingFrames.isValid() || !sampleTime.isValid()) {
|
||||
if (!missingFrames.valid() || !sampleTime.valid()) {
|
||||
NS_WARNING("Int overflow adding in AudioLoop()");
|
||||
break;
|
||||
}
|
||||
|
@ -1139,7 +1139,7 @@ void nsBuiltinDecoderStateMachine::AudioLoop()
|
|||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
CheckedInt64 playedUsecs = FramesToUsecs(audioDuration, rate) + audioStartTime;
|
||||
if (!playedUsecs.isValid()) {
|
||||
if (!playedUsecs.valid()) {
|
||||
NS_WARNING("Int overflow calculating audio end time");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -353,10 +353,10 @@ PRInt64 nsTheoraState::Time(th_info* aInfo, PRInt64 aGranulepos)
|
|||
ogg_int64_t pframe = aGranulepos - (iframe << shift);
|
||||
PRInt64 frameno = iframe + pframe - TH_VERSION_CHECK(aInfo, 3, 2, 1);
|
||||
CheckedInt64 t = ((CheckedInt64(frameno) + 1) * USECS_PER_S) * aInfo->fps_denominator;
|
||||
if (!t.isValid())
|
||||
if (!t.valid())
|
||||
return -1;
|
||||
t /= aInfo->fps_numerator;
|
||||
return t.isValid() ? t.value() : -1;
|
||||
return t.valid() ? t.value() : -1;
|
||||
}
|
||||
|
||||
PRInt64 nsTheoraState::StartTime(PRInt64 granulepos) {
|
||||
|
@ -364,7 +364,7 @@ PRInt64 nsTheoraState::StartTime(PRInt64 granulepos) {
|
|||
return -1;
|
||||
}
|
||||
CheckedInt64 t = (CheckedInt64(th_granule_frame(mCtx, granulepos)) * USECS_PER_S) * mInfo.fps_denominator;
|
||||
if (!t.isValid())
|
||||
if (!t.valid())
|
||||
return -1;
|
||||
return t.value() / mInfo.fps_numerator;
|
||||
}
|
||||
|
@ -622,7 +622,7 @@ PRInt64 nsVorbisState::Time(vorbis_info* aInfo, PRInt64 aGranulepos)
|
|||
return -1;
|
||||
}
|
||||
CheckedInt64 t = CheckedInt64(aGranulepos) * USECS_PER_S;
|
||||
if (!t.isValid())
|
||||
if (!t.valid())
|
||||
t = 0;
|
||||
return t.value() / aInfo->rate;
|
||||
}
|
||||
|
@ -884,7 +884,7 @@ PRInt64 nsOpusState::Time(PRInt64 granulepos)
|
|||
|
||||
// Ogg Opus always runs at a granule rate of 48 kHz.
|
||||
CheckedInt64 t = CheckedInt64(granulepos - mPreSkip) * USECS_PER_S;
|
||||
return t.isValid() ? t.value() / mRate : -1;
|
||||
return t.valid() ? t.value() / mRate : -1;
|
||||
}
|
||||
|
||||
bool nsOpusState::IsHeader(ogg_packet* aPacket)
|
||||
|
@ -1048,7 +1048,7 @@ bool nsSkeletonState::DecodeIndex(ogg_packet* aPacket)
|
|||
|
||||
// Extract the start time.
|
||||
CheckedInt64 t = CheckedInt64(LEInt64(p + INDEX_FIRST_NUMER_OFFSET)) * USECS_PER_S;
|
||||
if (!t.isValid()) {
|
||||
if (!t.valid()) {
|
||||
return (mActive = false);
|
||||
} else {
|
||||
startTime = t.value() / timeDenom;
|
||||
|
@ -1056,7 +1056,7 @@ bool nsSkeletonState::DecodeIndex(ogg_packet* aPacket)
|
|||
|
||||
// Extract the end time.
|
||||
t = LEInt64(p + INDEX_LAST_NUMER_OFFSET) * USECS_PER_S;
|
||||
if (!t.isValid()) {
|
||||
if (!t.valid()) {
|
||||
return (mActive = false);
|
||||
} else {
|
||||
endTime = t.value() / timeDenom;
|
||||
|
@ -1065,7 +1065,7 @@ bool nsSkeletonState::DecodeIndex(ogg_packet* aPacket)
|
|||
// Check the numKeyPoints value read, ensure we're not going to run out of
|
||||
// memory while trying to decode the index packet.
|
||||
CheckedInt64 minPacketSize = (CheckedInt64(numKeyPoints) * MIN_KEY_POINT_SIZE) + INDEX_KEYPOINT_OFFSET;
|
||||
if (!minPacketSize.isValid())
|
||||
if (!minPacketSize.valid())
|
||||
{
|
||||
return (mActive = false);
|
||||
}
|
||||
|
@ -1103,7 +1103,7 @@ bool nsSkeletonState::DecodeIndex(ogg_packet* aPacket)
|
|||
p = ReadVariableLengthInt(p, limit, delta);
|
||||
offset += delta;
|
||||
if (p == limit ||
|
||||
!offset.isValid() ||
|
||||
!offset.valid() ||
|
||||
offset.value() > mLength ||
|
||||
offset.value() < 0)
|
||||
{
|
||||
|
@ -1111,14 +1111,14 @@ bool nsSkeletonState::DecodeIndex(ogg_packet* aPacket)
|
|||
}
|
||||
p = ReadVariableLengthInt(p, limit, delta);
|
||||
time += delta;
|
||||
if (!time.isValid() ||
|
||||
if (!time.valid() ||
|
||||
time.value() > endTime ||
|
||||
time.value() < startTime)
|
||||
{
|
||||
return (mActive = false);
|
||||
}
|
||||
CheckedInt64 timeUsecs = time * USECS_PER_S;
|
||||
if (!timeUsecs.isValid())
|
||||
if (!timeUsecs.valid())
|
||||
return mActive = false;
|
||||
timeUsecs /= timeDenom;
|
||||
keyPoints->Add(offset.value(), timeUsecs.value());
|
||||
|
@ -1228,8 +1228,8 @@ nsresult nsSkeletonState::GetDuration(const nsTArray<PRUint32>& aTracks,
|
|||
}
|
||||
NS_ASSERTION(endTime > startTime, "Duration must be positive");
|
||||
CheckedInt64 duration = CheckedInt64(endTime) - startTime;
|
||||
aDuration = duration.isValid() ? duration.value() : 0;
|
||||
return duration.isValid() ? NS_OK : NS_ERROR_FAILURE;
|
||||
aDuration = duration.valid() ? duration.value() : 0;
|
||||
return duration.valid() ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool nsSkeletonState::DecodeHeader(ogg_packet* aPacket)
|
||||
|
|
|
@ -87,7 +87,7 @@ nsresult nsRawReader::ReadMetadata(nsVideoInfo* aInfo)
|
|||
|
||||
CheckedUint32 dummy = CheckedUint32(static_cast<PRUint32>(mMetadata.frameWidth)) *
|
||||
static_cast<PRUint32>(mMetadata.frameHeight);
|
||||
NS_ENSURE_TRUE(dummy.isValid(), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(dummy.valid(), NS_ERROR_FAILURE);
|
||||
|
||||
if (mMetadata.aspectDenominator == 0 ||
|
||||
mMetadata.framerateDenominator == 0)
|
||||
|
@ -268,7 +268,7 @@ nsresult nsRawReader::Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime,
|
|||
|
||||
CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize;
|
||||
offset += sizeof(nsRawVideoHeader);
|
||||
NS_ENSURE_TRUE(offset.isValid(), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(offset.valid(), NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -448,12 +448,12 @@ bool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket, PRInt64 aOffset)
|
|||
// from after the gap.
|
||||
CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, rate);
|
||||
CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, rate);
|
||||
if (!tstamp_frames.isValid() || !decoded_frames.isValid()) {
|
||||
if (!tstamp_frames.valid() || !decoded_frames.valid()) {
|
||||
NS_WARNING("Int overflow converting WebM times to frames");
|
||||
return false;
|
||||
}
|
||||
decoded_frames += mAudioFrames;
|
||||
if (!decoded_frames.isValid()) {
|
||||
if (!decoded_frames.valid()) {
|
||||
NS_WARNING("Int overflow adding decoded_frames");
|
||||
return false;
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ bool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket, PRInt64 aOffset)
|
|||
#ifdef DEBUG
|
||||
CheckedInt64 usecs = FramesToUsecs(tstamp_frames.value() - decoded_frames.value(), rate);
|
||||
LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio stream\n",
|
||||
usecs.isValid() ? usecs.value() : -1,
|
||||
usecs.valid() ? usecs.value(): -1,
|
||||
tstamp_frames.value() - decoded_frames.value()));
|
||||
#endif
|
||||
mPacketCount++;
|
||||
|
@ -501,18 +501,18 @@ bool nsWebMReader::DecodeAudioPacket(nestegg_packet* aPacket, PRInt64 aOffset)
|
|||
}
|
||||
|
||||
CheckedInt64 duration = FramesToUsecs(frames, rate);
|
||||
if (!duration.isValid()) {
|
||||
if (!duration.valid()) {
|
||||
NS_WARNING("Int overflow converting WebM audio duration");
|
||||
return false;
|
||||
}
|
||||
CheckedInt64 total_duration = FramesToUsecs(total_frames, rate);
|
||||
if (!total_duration.isValid()) {
|
||||
if (!total_duration.valid()) {
|
||||
NS_WARNING("Int overflow converting WebM audio total_duration");
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckedInt64 time = total_duration + tstamp_usecs;
|
||||
if (!time.isValid()) {
|
||||
if (!time.valid()) {
|
||||
NS_WARNING("Int overflow adding total_duration and tstamp_usecs");
|
||||
nestegg_free_packet(aPacket);
|
||||
return false;
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "CheckedInt.h"
|
||||
#include "mozilla/Util.h"
|
||||
|
||||
#ifndef M_PI
|
||||
|
@ -81,7 +81,7 @@ BoxBlurHorizontal(unsigned char* aInput,
|
|||
memcpy(aOutput, aInput, aWidth*aRows);
|
||||
return;
|
||||
}
|
||||
uint32_t reciprocal = (uint64_t(1) << 32) / boxSize;
|
||||
PRUint32 reciprocal = (PRUint64(1) << 32)/boxSize;
|
||||
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
// Check whether the skip rect intersects this row. If the skip
|
||||
|
@ -128,7 +128,7 @@ BoxBlurHorizontal(unsigned char* aInput,
|
|||
int32_t last = max(tmp, 0);
|
||||
int32_t next = min(tmp + boxSize, aWidth - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
|
||||
aOutput[aWidth * y + x] = (PRUint64(alphaSum)*reciprocal) >> 32;
|
||||
|
||||
alphaSum += aInput[aWidth * y + next] -
|
||||
aInput[aWidth * y + last];
|
||||
|
@ -159,7 +159,7 @@ BoxBlurVertical(unsigned char* aInput,
|
|||
memcpy(aOutput, aInput, aWidth*aRows);
|
||||
return;
|
||||
}
|
||||
uint32_t reciprocal = (uint64_t(1) << 32) / boxSize;
|
||||
PRUint32 reciprocal = (PRUint64(1) << 32)/boxSize;
|
||||
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
|
@ -199,7 +199,7 @@ BoxBlurVertical(unsigned char* aInput,
|
|||
int32_t last = max(tmp, 0);
|
||||
int32_t next = min(tmp + boxSize, aRows - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
|
||||
aOutput[aWidth * y + x] = (PRUint64(alphaSum)*reciprocal) >> 32;
|
||||
|
||||
alphaSum += aInput[aWidth * next + x] -
|
||||
aInput[aWidth * last + x];
|
||||
|
@ -409,12 +409,12 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
|
|||
mRect = IntRect(rect.x, rect.y, rect.width, rect.height);
|
||||
|
||||
CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
|
||||
if (stride.isValid()) {
|
||||
if (stride.valid()) {
|
||||
mStride = stride.value();
|
||||
|
||||
CheckedInt<int32_t> size = CheckedInt<int32_t>(mStride) * mRect.height *
|
||||
sizeof(unsigned char);
|
||||
if (size.isValid()) {
|
||||
if (size.valid()) {
|
||||
mData = static_cast<unsigned char*>(malloc(size.value()));
|
||||
memset(mData, 0, size.value());
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsMemory.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "CheckedInt.h"
|
||||
|
||||
#include "gfxASurface.h"
|
||||
#include "gfxContext.h"
|
||||
|
@ -378,7 +378,7 @@ gfxASurface::CheckSurfaceSize(const gfxIntSize& sz, PRInt32 limit)
|
|||
// make sure the surface area doesn't overflow a PRInt32
|
||||
CheckedInt<PRInt32> tmp = sz.width;
|
||||
tmp *= sz.height;
|
||||
if (!tmp.isValid()) {
|
||||
if (!tmp.valid()) {
|
||||
NS_WARNING("Surface size too large (would overflow)!");
|
||||
return false;
|
||||
}
|
||||
|
@ -386,7 +386,7 @@ gfxASurface::CheckSurfaceSize(const gfxIntSize& sz, PRInt32 limit)
|
|||
// assuming 4-byte stride, make sure the allocation size
|
||||
// doesn't overflow a PRInt32 either
|
||||
tmp *= 4;
|
||||
if (!tmp.isValid()) {
|
||||
if (!tmp.valid()) {
|
||||
NS_WARNING("Allocation too large (would overflow)!");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,789 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
/* Provides checked integers, detecting integer overflow and divide-by-0. */
|
||||
|
||||
#ifndef mozilla_CheckedInt_h_
|
||||
#define mozilla_CheckedInt_h_
|
||||
|
||||
/*
|
||||
* Build options. Comment out these #defines to disable the corresponding
|
||||
* optional feature. Disabling features may be useful for code using
|
||||
* CheckedInt outside of Mozilla (e.g. WebKit)
|
||||
*/
|
||||
|
||||
// Enable usage of MOZ_STATIC_ASSERT to check for unsupported types.
|
||||
// If disabled, static asserts are replaced by regular assert().
|
||||
#define MOZ_CHECKEDINT_ENABLE_MOZ_ASSERTS
|
||||
|
||||
/*
|
||||
* End of build options
|
||||
*/
|
||||
|
||||
|
||||
#ifdef MOZ_CHECKEDINT_ENABLE_MOZ_ASSERTS
|
||||
# include "mozilla/Assertions.h"
|
||||
#else
|
||||
# ifndef MOZ_STATIC_ASSERT
|
||||
# include <cassert>
|
||||
# define MOZ_STATIC_ASSERT(cond, reason) assert((cond) && reason)
|
||||
# define MOZ_ASSERT(cond, reason) assert((cond) && reason)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "mozilla/StandardInteger.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* Step 1: manually record supported types
|
||||
*
|
||||
* What's nontrivial here is that there are different families of integer
|
||||
* types: basic integer types and stdint types. It is merrily undefined which
|
||||
* types from one family may be just typedefs for a type from another family.
|
||||
*
|
||||
* For example, on GCC 4.6, aside from the basic integer types, the only other
|
||||
* type that isn't just a typedef for some of them, is int8_t.
|
||||
*/
|
||||
|
||||
struct UnsupportedType {};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct IsSupportedPass2
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct IsSupported
|
||||
{
|
||||
static const bool value = IsSupportedPass2<IntegerType>::value;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct IsSupported<int8_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<uint8_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<int16_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<uint16_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<int32_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<uint32_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<int64_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupported<uint64_t>
|
||||
{ static const bool value = true; };
|
||||
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<char>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned char>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<short>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned short>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<int>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned int>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<long>
|
||||
{ static const bool value = true; };
|
||||
|
||||
template<>
|
||||
struct IsSupportedPass2<unsigned long>
|
||||
{ static const bool value = true; };
|
||||
|
||||
|
||||
/*
|
||||
* Step 2: some integer-traits kind of stuff.
|
||||
*/
|
||||
|
||||
template<size_t Size, bool Signedness>
|
||||
struct StdintTypeForSizeAndSignedness
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<1, true>
|
||||
{ typedef int8_t Type; };
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<1, false>
|
||||
{ typedef uint8_t Type; };
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<2, true>
|
||||
{ typedef int16_t Type; };
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<2, false>
|
||||
{ typedef uint16_t Type; };
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<4, true>
|
||||
{ typedef int32_t Type; };
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<4, false>
|
||||
{ typedef uint32_t Type; };
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<8, true>
|
||||
{ typedef int64_t Type; };
|
||||
|
||||
template<>
|
||||
struct StdintTypeForSizeAndSignedness<8, false>
|
||||
{ typedef uint64_t Type; };
|
||||
|
||||
template<typename IntegerType>
|
||||
struct UnsignedType
|
||||
{
|
||||
typedef typename StdintTypeForSizeAndSignedness<sizeof(IntegerType),
|
||||
false>::Type Type;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct IsSigned
|
||||
{
|
||||
static const bool value = IntegerType(-1) <= IntegerType(0);
|
||||
};
|
||||
|
||||
template<typename IntegerType, size_t Size = sizeof(IntegerType)>
|
||||
struct TwiceBiggerType
|
||||
{
|
||||
typedef typename StdintTypeForSizeAndSignedness<
|
||||
sizeof(IntegerType) * 2,
|
||||
IsSigned<IntegerType>::value
|
||||
>::Type Type;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct TwiceBiggerType<IntegerType, 8>
|
||||
{
|
||||
typedef UnsupportedType Type;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct PositionOfSignBit
|
||||
{
|
||||
static const size_t value = CHAR_BIT * sizeof(IntegerType) - 1;
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct MinValue
|
||||
{
|
||||
static IntegerType value()
|
||||
{
|
||||
// Bitwise ops may return a larger type, that's why we cast explicitly.
|
||||
// In C++, left bit shifts on signed values is undefined by the standard
|
||||
// unless the shifted value is representable.
|
||||
// Notice that signed-to-unsigned conversions are always well-defined in
|
||||
// the standard as the value congruent to 2**n, as expected. By contrast,
|
||||
// unsigned-to-signed is only well-defined if the value is representable.
|
||||
return IsSigned<IntegerType>::value
|
||||
? IntegerType(typename UnsignedType<IntegerType>::Type(1)
|
||||
<< PositionOfSignBit<IntegerType>::value)
|
||||
: IntegerType(0);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct MaxValue
|
||||
{
|
||||
static IntegerType value()
|
||||
{
|
||||
// Tricksy, but covered by the unit test.
|
||||
// Relies heavily on the return type of MinValue<IntegerType>::value()
|
||||
// being IntegerType.
|
||||
return ~MinValue<IntegerType>::value();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Step 3: Implement the actual validity checks.
|
||||
*
|
||||
* Ideas taken from IntegerLib, code different.
|
||||
*/
|
||||
|
||||
// Bitwise ops may return a larger type, so it's good to use these inline
|
||||
// helpers guaranteeing that the result is really of type T.
|
||||
|
||||
template<typename T>
|
||||
inline T
|
||||
HasSignBit(T x)
|
||||
{
|
||||
// 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.
|
||||
// Here the unsigned-to-signed conversion is OK because the value
|
||||
// (the result of the shift) is 0 or 1.
|
||||
return T(typename UnsignedType<T>::Type(x)
|
||||
>> PositionOfSignBit<T>::value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T
|
||||
BinaryComplement(T x)
|
||||
{
|
||||
return ~x;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
typename U,
|
||||
bool IsTSigned = IsSigned<T>::value,
|
||||
bool IsUSigned = IsSigned<U>::value>
|
||||
struct IsInRangeImpl {};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, true, true>
|
||||
{
|
||||
static bool run(U x)
|
||||
{
|
||||
return x <= MaxValue<T>::value() &&
|
||||
x >= MinValue<T>::value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, false, false>
|
||||
{
|
||||
static bool run(U x)
|
||||
{
|
||||
return x <= MaxValue<T>::value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, true, false>
|
||||
{
|
||||
static bool run(U x)
|
||||
{
|
||||
return sizeof(T) > sizeof(U)
|
||||
? true
|
||||
: x <= U(MaxValue<T>::value());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct IsInRangeImpl<T, U, false, true>
|
||||
{
|
||||
static bool run(U x)
|
||||
{
|
||||
return sizeof(T) >= sizeof(U)
|
||||
? x >= 0
|
||||
: x >= 0 && x <= U(MaxValue<T>::value());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool
|
||||
IsInRange(U x)
|
||||
{
|
||||
return IsInRangeImpl<T, U>::run(x);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsAddValid(T x, T y, T result)
|
||||
{
|
||||
// Addition is valid if the sign of x+y is equal to either that of x or that
|
||||
// of y. 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.
|
||||
return IsSigned<T>::value
|
||||
? HasSignBit(BinaryComplement(T((result ^ x) & (result ^ y))))
|
||||
: BinaryComplement(x) >= y;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsSubValid(T x, T y, T result)
|
||||
{
|
||||
// Subtraction is valid if either x and y have same sign, or x-y and x have
|
||||
// same sign.
|
||||
return IsSigned<T>::value
|
||||
? HasSignBit(BinaryComplement(T((result ^ x) & (x ^ y))))
|
||||
: x >= y;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
bool IsSigned = IsSigned<T>::value,
|
||||
bool TwiceBiggerTypeIsSupported =
|
||||
IsSupported<typename TwiceBiggerType<T>::Type>::value>
|
||||
struct IsMulValidImpl {};
|
||||
|
||||
template<typename T, bool IsSigned>
|
||||
struct IsMulValidImpl<T, IsSigned, true>
|
||||
{
|
||||
static bool run(T x, T y)
|
||||
{
|
||||
typedef typename TwiceBiggerType<T>::Type TwiceBiggerType;
|
||||
TwiceBiggerType product = TwiceBiggerType(x) * TwiceBiggerType(y);
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsMulValidImpl<T, false, false>
|
||||
{
|
||||
static bool run(T x, T y)
|
||||
{
|
||||
return y == 0 ||
|
||||
x <= MaxValue<T>::value() / y;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsMulValid(T x, T y, T /* result not used */)
|
||||
{
|
||||
return IsMulValidImpl<T>::run(x, y);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
IsDivValid(T x, T y)
|
||||
{
|
||||
// Keep in mind that in the signed case, min/-1 is invalid because abs(min)>max.
|
||||
return IsSigned<T>::value
|
||||
? (y != 0) && (x != MinValue<T>::value() || y != T(-1))
|
||||
: y != 0;
|
||||
}
|
||||
|
||||
// This is just to shut up msvc warnings about negating unsigned ints.
|
||||
template<typename T, bool IsSigned = IsSigned<T>::value>
|
||||
struct OppositeIfSignedImpl
|
||||
{
|
||||
static T run(T x) { return -x; }
|
||||
};
|
||||
template<typename T>
|
||||
struct OppositeIfSignedImpl<T, false>
|
||||
{
|
||||
static T run(T x) { return x; }
|
||||
};
|
||||
template<typename T>
|
||||
inline T
|
||||
OppositeIfSigned(T x)
|
||||
{
|
||||
return OppositeIfSignedImpl<T>::run(x);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/*
|
||||
* Step 4: Now define the CheckedInt class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class CheckedInt
|
||||
* @brief Integer wrapper class checking for integer overflow and other errors
|
||||
* @param T the integer type to wrap. Can be any type among the following:
|
||||
* - any basic integer type such as |int|
|
||||
* - any stdint type such as |int8_t|
|
||||
*
|
||||
* This class implements guarded integer arithmetic. Do a computation, check
|
||||
* that isValid() returns true, you then have a guarantee that no problem, such
|
||||
* as integer overflow, happened during this computation, and you can call
|
||||
* value() to get the plain integer value.
|
||||
*
|
||||
* The arithmetic operators in this class are guaranteed not to raise a signal
|
||||
* (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
|
||||
* zero or integer overflow). You could code it as follows:
|
||||
@code
|
||||
bool computeXPlusYOverZ(int x, int y, int z, int *result)
|
||||
{
|
||||
CheckedInt<int> checkedResult = (CheckedInt<int>(x) + y) / z;
|
||||
if (checkedResult.isValid()) {
|
||||
*result = checkedResult.value();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*
|
||||
* Implicit conversion from plain integers to checked integers is allowed. The
|
||||
* plain integer is checked to be in range before being casted to the
|
||||
* destination type. This means that the following lines all compile, and the
|
||||
* resulting CheckedInts are correctly detected as valid or invalid:
|
||||
* @code
|
||||
// 1 is of type int, is found to be in range for uint8_t, x is valid
|
||||
CheckedInt<uint8_t> x(1);
|
||||
// -1 is of type int, is found not to be in range for uint8_t, x is invalid
|
||||
CheckedInt<uint8_t> x(-1);
|
||||
// -1 is of type int, is found to be in range for int8_t, x is valid
|
||||
CheckedInt<int8_t> x(-1);
|
||||
// 1000 is of type int16_t, is found not to be in range for int8_t,
|
||||
// x is invalid
|
||||
CheckedInt<int8_t> x(int16_t(1000));
|
||||
// 3123456789 is of type uint32_t, is found not to be in range for int32_t,
|
||||
// x is invalid
|
||||
CheckedInt<int32_t> x(uint32_t(3123456789));
|
||||
* @endcode
|
||||
* Implicit conversion from
|
||||
* checked integers to plain integers is not allowed. As shown in the
|
||||
* above example, to get the value of a checked integer as a normal integer,
|
||||
* call value().
|
||||
*
|
||||
* Arithmetic operations between checked and plain integers is allowed; the
|
||||
* result type is the type of the checked integer.
|
||||
*
|
||||
* Checked integers of different types cannot be used in the same arithmetic
|
||||
* expression.
|
||||
*
|
||||
* There are convenience typedefs for all stdint types, of the following form
|
||||
* (these are just 2 examples):
|
||||
@code
|
||||
typedef CheckedInt<int32_t> CheckedInt32;
|
||||
typedef CheckedInt<uint16_t> CheckedUint16;
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
class CheckedInt
|
||||
{
|
||||
protected:
|
||||
T mValue;
|
||||
bool mIsValid;
|
||||
|
||||
template<typename U>
|
||||
CheckedInt(U value, bool isValid) : mValue(value), mIsValid(isValid)
|
||||
{
|
||||
MOZ_STATIC_ASSERT(detail::IsSupported<T>::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 value)
|
||||
: mValue(T(value)),
|
||||
mIsValid(detail::IsInRange<T>(value))
|
||||
{
|
||||
MOZ_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)
|
||||
{
|
||||
MOZ_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 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>& lhs,
|
||||
const CheckedInt<U> &rhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator -=(U rhs);
|
||||
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>& lhs,
|
||||
const CheckedInt<U> &rhs);
|
||||
template<typename U>
|
||||
CheckedInt& operator /=(U rhs);
|
||||
|
||||
CheckedInt operator -() const
|
||||
{
|
||||
// Circumvent msvc warning about - applied to unsigned int.
|
||||
// if we're unsigned, the only valid case anyway is 0
|
||||
// in which case - is a no-op.
|
||||
T result = detail::OppositeIfSigned(mValue);
|
||||
/* Help the compiler perform RVO (return value optimization). */
|
||||
return CheckedInt(result,
|
||||
mIsValid && detail::IsSubValid(T(0),
|
||||
mValue,
|
||||
result));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/** prefix ++ */
|
||||
CheckedInt& operator++()
|
||||
{
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** postfix ++ */
|
||||
CheckedInt operator++(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/** prefix -- */
|
||||
CheckedInt& operator--()
|
||||
{
|
||||
*this -= 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
};
|
||||
|
||||
#define MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
|
||||
template<typename T> \
|
||||
inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, \
|
||||
const CheckedInt<T> &rhs) \
|
||||
{ \
|
||||
T x = lhs.mValue; \
|
||||
T y = rhs.mValue; \
|
||||
T result = x OP y; \
|
||||
T isOpValid \
|
||||
= detail::Is##NAME##Valid(x, y, result); \
|
||||
/* Help the compiler perform RVO (return value optimization). */ \
|
||||
return CheckedInt<T>(result, \
|
||||
lhs.mIsValid && rhs.mIsValid && isOpValid); \
|
||||
}
|
||||
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Add, +)
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Sub, -)
|
||||
MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR(Mul, *)
|
||||
|
||||
#undef MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
|
||||
|
||||
// Division can't be implemented by MOZ_CHECKEDINT_BASIC_BINARY_OPERATOR
|
||||
// because if rhs == 0, we are not allowed to even try to compute the quotient.
|
||||
template<typename T>
|
||||
inline CheckedInt<T> operator /(const CheckedInt<T> &lhs,
|
||||
const CheckedInt<T> &rhs)
|
||||
{
|
||||
T x = lhs.mValue;
|
||||
T y = rhs.mValue;
|
||||
bool isOpValid = detail::IsDivValid(x, y);
|
||||
T result = isOpValid ? (x / y) : 0;
|
||||
/* give the compiler a good chance to perform RVO */
|
||||
return CheckedInt<T>(result,
|
||||
lhs.mIsValid && rhs.mIsValid && isOpValid);
|
||||
}
|
||||
|
||||
// Implement castToCheckedInt<T>(x), making sure that
|
||||
// - it allows x to be either a CheckedInt<T> or any integer type
|
||||
// that can be casted to T
|
||||
// - if x is already a CheckedInt<T>, we just return a reference to it,
|
||||
// instead of copying it (optimization)
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T, typename U>
|
||||
struct CastToCheckedIntImpl
|
||||
{
|
||||
typedef CheckedInt<T> ReturnType;
|
||||
static CheckedInt<T> run(U u) { return u; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CastToCheckedIntImpl<T, CheckedInt<T> >
|
||||
{
|
||||
typedef const CheckedInt<T>& ReturnType;
|
||||
static const CheckedInt<T>& run(const CheckedInt<T>& u) { return u; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, typename U>
|
||||
inline typename detail::CastToCheckedIntImpl<T, U>::ReturnType
|
||||
castToCheckedInt(U u)
|
||||
{
|
||||
return detail::CastToCheckedIntImpl<T, U>::run(u);
|
||||
}
|
||||
|
||||
#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; \
|
||||
}
|
||||
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
|
||||
MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
|
||||
|
||||
#undef MOZ_CHECKEDINT_CONVENIENCE_BINARY_OPERATORS
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool
|
||||
operator ==(const CheckedInt<T> &lhs, U rhs)
|
||||
{
|
||||
return lhs == castToCheckedInt<T>(rhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool
|
||||
operator ==(U lhs, const CheckedInt<T> &rhs)
|
||||
{
|
||||
return castToCheckedInt<T>(lhs) == rhs;
|
||||
}
|
||||
|
||||
// Convenience typedefs.
|
||||
typedef CheckedInt<int8_t> CheckedInt8;
|
||||
typedef CheckedInt<uint8_t> CheckedUint8;
|
||||
typedef CheckedInt<int16_t> CheckedInt16;
|
||||
typedef CheckedInt<uint16_t> CheckedUint16;
|
||||
typedef CheckedInt<int32_t> CheckedInt32;
|
||||
typedef CheckedInt<uint32_t> CheckedUint32;
|
||||
typedef CheckedInt<int64_t> CheckedInt64;
|
||||
typedef CheckedInt<uint64_t> CheckedUint64;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_CheckedInt_h_ */
|
|
@ -47,9 +47,7 @@ LIBRARY_NAME = mfbt
|
|||
FORCE_STATIC_LIB = 1
|
||||
STL_FLAGS =
|
||||
|
||||
TEST_DIRS = \
|
||||
tests \
|
||||
$(NULL)
|
||||
DIRS =
|
||||
|
||||
# exported_headers.mk defines the headers exported by mfbt. It is included by
|
||||
# mfbt itself and by the JS engine, which, when built standalone, must do the
|
||||
|
|
|
@ -45,7 +45,6 @@ EXPORTS_mozilla += \
|
|||
Assertions.h \
|
||||
Attributes.h \
|
||||
BloomFilter.h \
|
||||
CheckedInt.h \
|
||||
FloatingPoint.h \
|
||||
GuardObjects.h \
|
||||
HashFunctions.h \
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
# 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/.
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
STL_FLAGS =
|
||||
|
||||
CPP_UNIT_TESTS = \
|
||||
TestCheckedInt.cpp \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LIBS=
|
||||
MOZ_GLUE_PROGRAM_LDFLAGS=
|
|
@ -1,492 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/CheckedInt.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <climits>
|
||||
|
||||
#ifndef MOZ_CHECKEDINT_ENABLE_MOZ_ASSERTS
|
||||
# error MOZ_CHECKEDINT_ENABLE_MOZ_ASSERTS should be defined by CheckedInt.h
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
int gIntegerTypesTested = 0;
|
||||
int gTestsPassed = 0;
|
||||
int gTestsFailed = 0;
|
||||
|
||||
void verifyImplFunction(bool x, bool expected,
|
||||
const char* file, int line,
|
||||
int size, bool isTSigned)
|
||||
{
|
||||
if (x == expected) {
|
||||
gTestsPassed++;
|
||||
} else {
|
||||
gTestsFailed++;
|
||||
std::cerr << "Test failed at " << file << ":" << line;
|
||||
std::cerr << " with T a ";
|
||||
if (isTSigned)
|
||||
std::cerr << "signed";
|
||||
else
|
||||
std::cerr << "unsigned";
|
||||
std::cerr << " " << CHAR_BIT*size << "-bit integer type" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_IMPL(x, expected) \
|
||||
verifyImplFunction((x), \
|
||||
(expected), \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
sizeof(T), \
|
||||
detail::IsSigned<T>::value)
|
||||
|
||||
#define VERIFY(x) VERIFY_IMPL(x, true)
|
||||
#define VERIFY_IS_FALSE(x) VERIFY_IMPL(x, false)
|
||||
#define VERIFY_IS_VALID(x) VERIFY_IMPL((x).isValid(), true)
|
||||
#define VERIFY_IS_INVALID(x) VERIFY_IMPL((x).isValid(), false)
|
||||
#define VERIFY_IS_VALID_IF(x,condition) VERIFY_IMPL((x).isValid(), (condition))
|
||||
|
||||
template<typename T, size_t Size = sizeof(T)>
|
||||
struct testTwiceBiggerType
|
||||
{
|
||||
static void run()
|
||||
{
|
||||
VERIFY(detail::IsSupported<typename detail::TwiceBiggerType<T>::Type>::value);
|
||||
VERIFY(sizeof(typename detail::TwiceBiggerType<T>::Type)
|
||||
== 2 * sizeof(T));
|
||||
VERIFY(bool(detail::IsSigned<typename detail::TwiceBiggerType<T>::Type>::value)
|
||||
== bool(detail::IsSigned<T>::value));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct testTwiceBiggerType<T, 8>
|
||||
{
|
||||
static void run()
|
||||
{
|
||||
VERIFY_IS_FALSE(detail::IsSupported<
|
||||
typename detail::TwiceBiggerType<T>::Type
|
||||
>::value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
void test()
|
||||
{
|
||||
static bool alreadyRun = false;
|
||||
// Integer types from different families may just be typedefs for types from other families.
|
||||
// e.g. int32_t might be just a typedef for int. No point re-running the same tests then.
|
||||
if (alreadyRun)
|
||||
return;
|
||||
alreadyRun = true;
|
||||
|
||||
VERIFY(detail::IsSupported<T>::value);
|
||||
const bool isTSigned = detail::IsSigned<T>::value;
|
||||
VERIFY(bool(isTSigned) == !bool(T(-1) > T(0)));
|
||||
|
||||
testTwiceBiggerType<T>::run();
|
||||
|
||||
typedef typename detail::UnsignedType<T>::Type unsignedT;
|
||||
|
||||
VERIFY(sizeof(unsignedT) == sizeof(T));
|
||||
VERIFY(detail::IsSigned<unsignedT>::value == false);
|
||||
|
||||
const CheckedInt<T> max(detail::MaxValue<T>::value());
|
||||
const CheckedInt<T> min(detail::MinValue<T>::value());
|
||||
|
||||
// Check min() and max(), since they are custom implementations and a mistake there
|
||||
// could potentially NOT be caught by any other tests... while making everything wrong!
|
||||
|
||||
T bit = 1;
|
||||
for (size_t i = 0; i < sizeof(T) * CHAR_BIT - 1; i++)
|
||||
{
|
||||
VERIFY((min.value() & bit) == 0);
|
||||
bit <<= 1;
|
||||
}
|
||||
VERIFY((min.value() & bit) == (isTSigned ? bit : T(0)));
|
||||
VERIFY(max.value() == T(~(min.value())));
|
||||
|
||||
const CheckedInt<T> zero(0);
|
||||
const CheckedInt<T> one(1);
|
||||
const CheckedInt<T> two(2);
|
||||
const CheckedInt<T> three(3);
|
||||
const CheckedInt<T> four(4);
|
||||
|
||||
/* Addition / substraction checks */
|
||||
|
||||
VERIFY_IS_VALID(zero + zero);
|
||||
VERIFY(zero + zero == zero);
|
||||
VERIFY_IS_FALSE(zero + zero == one); // Check that == doesn't always return true
|
||||
VERIFY_IS_VALID(zero + one);
|
||||
VERIFY(zero + one == one);
|
||||
VERIFY_IS_VALID(one + one);
|
||||
VERIFY(one + one == two);
|
||||
|
||||
const CheckedInt<T> maxMinusOne = max - one;
|
||||
const CheckedInt<T> maxMinusTwo = max - two;
|
||||
VERIFY_IS_VALID(maxMinusOne);
|
||||
VERIFY_IS_VALID(maxMinusTwo);
|
||||
VERIFY_IS_VALID(maxMinusOne + one);
|
||||
VERIFY_IS_VALID(maxMinusTwo + one);
|
||||
VERIFY_IS_VALID(maxMinusTwo + two);
|
||||
VERIFY(maxMinusOne + one == max);
|
||||
VERIFY(maxMinusTwo + one == maxMinusOne);
|
||||
VERIFY(maxMinusTwo + two == max);
|
||||
|
||||
VERIFY_IS_VALID(max + zero);
|
||||
VERIFY_IS_VALID(max - zero);
|
||||
VERIFY_IS_INVALID(max + one);
|
||||
VERIFY_IS_INVALID(max + two);
|
||||
VERIFY_IS_INVALID(max + maxMinusOne);
|
||||
VERIFY_IS_INVALID(max + max);
|
||||
|
||||
const CheckedInt<T> minPlusOne = min + one;
|
||||
const CheckedInt<T> minPlusTwo = min + two;
|
||||
VERIFY_IS_VALID(minPlusOne);
|
||||
VERIFY_IS_VALID(minPlusTwo);
|
||||
VERIFY_IS_VALID(minPlusOne - one);
|
||||
VERIFY_IS_VALID(minPlusTwo - one);
|
||||
VERIFY_IS_VALID(minPlusTwo - two);
|
||||
VERIFY(minPlusOne - one == min);
|
||||
VERIFY(minPlusTwo - one == minPlusOne);
|
||||
VERIFY(minPlusTwo - two == min);
|
||||
|
||||
const CheckedInt<T> minMinusOne = min - one;
|
||||
VERIFY_IS_VALID(min + zero);
|
||||
VERIFY_IS_VALID(min - zero);
|
||||
VERIFY_IS_INVALID(min - one);
|
||||
VERIFY_IS_INVALID(min - two);
|
||||
VERIFY_IS_INVALID(min - minMinusOne);
|
||||
VERIFY_IS_VALID(min - min);
|
||||
|
||||
const CheckedInt<T> maxOverTwo = max / two;
|
||||
VERIFY_IS_VALID(maxOverTwo + maxOverTwo);
|
||||
VERIFY_IS_VALID(maxOverTwo + one);
|
||||
VERIFY((maxOverTwo + one) - one == maxOverTwo);
|
||||
VERIFY_IS_VALID(maxOverTwo - maxOverTwo);
|
||||
VERIFY(maxOverTwo - maxOverTwo == zero);
|
||||
|
||||
const CheckedInt<T> minOverTwo = min / two;
|
||||
VERIFY_IS_VALID(minOverTwo + minOverTwo);
|
||||
VERIFY_IS_VALID(minOverTwo + one);
|
||||
VERIFY((minOverTwo + one) - one == minOverTwo);
|
||||
VERIFY_IS_VALID(minOverTwo - minOverTwo);
|
||||
VERIFY(minOverTwo - minOverTwo == zero);
|
||||
|
||||
VERIFY_IS_INVALID(min - one);
|
||||
VERIFY_IS_INVALID(min - two);
|
||||
|
||||
if (isTSigned) {
|
||||
VERIFY_IS_INVALID(min + min);
|
||||
VERIFY_IS_INVALID(minOverTwo + minOverTwo + minOverTwo);
|
||||
VERIFY_IS_INVALID(zero - min + min);
|
||||
VERIFY_IS_INVALID(one - min + min);
|
||||
}
|
||||
|
||||
/* Unary operator- checks */
|
||||
|
||||
const CheckedInt<T> negOne = -one;
|
||||
const CheckedInt<T> negTwo = -two;
|
||||
|
||||
if (isTSigned) {
|
||||
VERIFY_IS_VALID(-max);
|
||||
VERIFY_IS_VALID(-max - one);
|
||||
VERIFY_IS_VALID(negOne);
|
||||
VERIFY_IS_VALID(-max + negOne);
|
||||
VERIFY_IS_VALID(negOne + one);
|
||||
VERIFY(negOne + one == zero);
|
||||
VERIFY_IS_VALID(negTwo);
|
||||
VERIFY_IS_VALID(negOne + negOne);
|
||||
VERIFY(negOne + negOne == negTwo);
|
||||
} else {
|
||||
VERIFY_IS_INVALID(negOne);
|
||||
}
|
||||
|
||||
/* multiplication checks */
|
||||
|
||||
VERIFY_IS_VALID(zero * zero);
|
||||
VERIFY(zero * zero == zero);
|
||||
VERIFY_IS_VALID(zero * one);
|
||||
VERIFY(zero * one == zero);
|
||||
VERIFY_IS_VALID(one * zero);
|
||||
VERIFY(one * zero == zero);
|
||||
VERIFY_IS_VALID(one * one);
|
||||
VERIFY(one * one == one);
|
||||
VERIFY_IS_VALID(one * three);
|
||||
VERIFY(one * three == three);
|
||||
VERIFY_IS_VALID(two * two);
|
||||
VERIFY(two * two == four);
|
||||
|
||||
VERIFY_IS_INVALID(max * max);
|
||||
VERIFY_IS_INVALID(maxOverTwo * max);
|
||||
VERIFY_IS_INVALID(maxOverTwo * maxOverTwo);
|
||||
|
||||
const CheckedInt<T> maxApproxSqrt(T(T(1) << (CHAR_BIT*sizeof(T)/2)));
|
||||
|
||||
VERIFY_IS_VALID(maxApproxSqrt);
|
||||
VERIFY_IS_VALID(maxApproxSqrt * two);
|
||||
VERIFY_IS_INVALID(maxApproxSqrt * maxApproxSqrt);
|
||||
VERIFY_IS_INVALID(maxApproxSqrt * maxApproxSqrt * maxApproxSqrt);
|
||||
|
||||
if (isTSigned) {
|
||||
VERIFY_IS_INVALID(min * min);
|
||||
VERIFY_IS_INVALID(minOverTwo * min);
|
||||
VERIFY_IS_INVALID(minOverTwo * minOverTwo);
|
||||
|
||||
const CheckedInt<T> minApproxSqrt = -maxApproxSqrt;
|
||||
|
||||
VERIFY_IS_VALID(minApproxSqrt);
|
||||
VERIFY_IS_VALID(minApproxSqrt * two);
|
||||
VERIFY_IS_INVALID(minApproxSqrt * maxApproxSqrt);
|
||||
VERIFY_IS_INVALID(minApproxSqrt * minApproxSqrt);
|
||||
}
|
||||
|
||||
// make sure to check all 4 paths in signed multiplication validity check.
|
||||
// test positive * positive
|
||||
VERIFY_IS_VALID(max * one);
|
||||
VERIFY(max * one == max);
|
||||
VERIFY_IS_INVALID(max * two);
|
||||
VERIFY_IS_VALID(maxOverTwo * two);
|
||||
VERIFY((maxOverTwo + maxOverTwo) == (maxOverTwo * two));
|
||||
|
||||
if (isTSigned) {
|
||||
// test positive * negative
|
||||
VERIFY_IS_VALID(max * negOne);
|
||||
VERIFY_IS_VALID(-max);
|
||||
VERIFY(max * negOne == -max);
|
||||
VERIFY_IS_VALID(one * min);
|
||||
VERIFY_IS_INVALID(max * negTwo);
|
||||
VERIFY_IS_VALID(maxOverTwo * negTwo);
|
||||
VERIFY_IS_VALID(two * minOverTwo);
|
||||
VERIFY_IS_VALID((maxOverTwo + one) * negTwo);
|
||||
VERIFY_IS_INVALID((maxOverTwo + two) * negTwo);
|
||||
VERIFY_IS_INVALID(two * (minOverTwo - one));
|
||||
|
||||
// test negative * positive
|
||||
VERIFY_IS_VALID(min * one);
|
||||
VERIFY_IS_VALID(minPlusOne * one);
|
||||
VERIFY_IS_INVALID(min * two);
|
||||
VERIFY_IS_VALID(minOverTwo * two);
|
||||
VERIFY(minOverTwo * two == min);
|
||||
VERIFY_IS_INVALID((minOverTwo - one) * negTwo);
|
||||
VERIFY_IS_INVALID(negTwo * max);
|
||||
VERIFY_IS_VALID(minOverTwo * two);
|
||||
VERIFY(minOverTwo * two == min);
|
||||
VERIFY_IS_VALID(negTwo * maxOverTwo);
|
||||
VERIFY_IS_INVALID((minOverTwo - one) * two);
|
||||
VERIFY_IS_VALID(negTwo * (maxOverTwo + one));
|
||||
VERIFY_IS_INVALID(negTwo * (maxOverTwo + two));
|
||||
|
||||
// test negative * negative
|
||||
VERIFY_IS_INVALID(min * negOne);
|
||||
VERIFY_IS_VALID(minPlusOne * negOne);
|
||||
VERIFY(minPlusOne * negOne == max);
|
||||
VERIFY_IS_INVALID(min * negTwo);
|
||||
VERIFY_IS_INVALID(minOverTwo * negTwo);
|
||||
VERIFY_IS_INVALID(negOne * min);
|
||||
VERIFY_IS_VALID(negOne * minPlusOne);
|
||||
VERIFY(negOne * minPlusOne == max);
|
||||
VERIFY_IS_INVALID(negTwo * min);
|
||||
VERIFY_IS_INVALID(negTwo * minOverTwo);
|
||||
}
|
||||
|
||||
/* Division checks */
|
||||
|
||||
VERIFY_IS_VALID(one / one);
|
||||
VERIFY(one / one == one);
|
||||
VERIFY_IS_VALID(three / three);
|
||||
VERIFY(three / three == one);
|
||||
VERIFY_IS_VALID(four / two);
|
||||
VERIFY(four / two == two);
|
||||
VERIFY((four*three)/four == three);
|
||||
|
||||
// Check that div by zero is invalid
|
||||
VERIFY_IS_INVALID(zero / zero);
|
||||
VERIFY_IS_INVALID(one / zero);
|
||||
VERIFY_IS_INVALID(two / zero);
|
||||
VERIFY_IS_INVALID(negOne / zero);
|
||||
VERIFY_IS_INVALID(max / zero);
|
||||
VERIFY_IS_INVALID(min / zero);
|
||||
|
||||
if (isTSigned) {
|
||||
// Check that min / -1 is invalid
|
||||
VERIFY_IS_INVALID(min / negOne);
|
||||
|
||||
// Check that the test for div by -1 isn't banning other numerators than min
|
||||
VERIFY_IS_VALID(one / negOne);
|
||||
VERIFY_IS_VALID(zero / negOne);
|
||||
VERIFY_IS_VALID(negOne / negOne);
|
||||
VERIFY_IS_VALID(max / negOne);
|
||||
}
|
||||
|
||||
/* Check that invalidity is correctly preserved by arithmetic ops */
|
||||
|
||||
const CheckedInt<T> someInvalid = max + max;
|
||||
VERIFY_IS_INVALID(someInvalid + zero);
|
||||
VERIFY_IS_INVALID(someInvalid - zero);
|
||||
VERIFY_IS_INVALID(zero + someInvalid);
|
||||
VERIFY_IS_INVALID(zero - someInvalid);
|
||||
VERIFY_IS_INVALID(-someInvalid);
|
||||
VERIFY_IS_INVALID(someInvalid * zero);
|
||||
VERIFY_IS_INVALID(someInvalid * one);
|
||||
VERIFY_IS_INVALID(zero * someInvalid);
|
||||
VERIFY_IS_INVALID(one * someInvalid);
|
||||
VERIFY_IS_INVALID(someInvalid / zero);
|
||||
VERIFY_IS_INVALID(someInvalid / one);
|
||||
VERIFY_IS_INVALID(zero / someInvalid);
|
||||
VERIFY_IS_INVALID(one / someInvalid);
|
||||
VERIFY_IS_INVALID(someInvalid + someInvalid);
|
||||
VERIFY_IS_INVALID(someInvalid - someInvalid);
|
||||
VERIFY_IS_INVALID(someInvalid * someInvalid);
|
||||
VERIFY_IS_INVALID(someInvalid / someInvalid);
|
||||
|
||||
/* Check that mixing checked integers with plain integers in expressions is allowed */
|
||||
|
||||
VERIFY(one + T(2) == three);
|
||||
VERIFY(2 + one == three);
|
||||
{
|
||||
CheckedInt<T> x = one;
|
||||
x += 2;
|
||||
VERIFY(x == three);
|
||||
}
|
||||
VERIFY(two - 1 == one);
|
||||
VERIFY(2 - one == one);
|
||||
{
|
||||
CheckedInt<T> x = two;
|
||||
x -= 1;
|
||||
VERIFY(x == one);
|
||||
}
|
||||
VERIFY(one * 2 == two);
|
||||
VERIFY(2 * one == two);
|
||||
{
|
||||
CheckedInt<T> x = one;
|
||||
x *= 2;
|
||||
VERIFY(x == two);
|
||||
}
|
||||
VERIFY(four / 2 == two);
|
||||
VERIFY(4 / two == two);
|
||||
{
|
||||
CheckedInt<T> x = four;
|
||||
x /= 2;
|
||||
VERIFY(x == two);
|
||||
}
|
||||
|
||||
VERIFY(one == 1);
|
||||
VERIFY(1 == one);
|
||||
VERIFY_IS_FALSE(two == 1);
|
||||
VERIFY_IS_FALSE(1 == two);
|
||||
VERIFY_IS_FALSE(someInvalid == 1);
|
||||
VERIFY_IS_FALSE(1 == someInvalid);
|
||||
|
||||
/* Check that construction of CheckedInt from an integer value of a mismatched type is checked */
|
||||
|
||||
#define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(U) \
|
||||
{ \
|
||||
bool isUSigned = detail::IsSigned<U>::value; \
|
||||
VERIFY_IS_VALID(CheckedInt<T>(U(0))); \
|
||||
VERIFY_IS_VALID(CheckedInt<T>(U(1))); \
|
||||
VERIFY_IS_VALID(CheckedInt<T>(U(100))); \
|
||||
if (isUSigned) \
|
||||
VERIFY_IS_VALID_IF(CheckedInt<T>(U(-1)), isTSigned); \
|
||||
if (sizeof(U) > sizeof(T)) \
|
||||
VERIFY_IS_INVALID(CheckedInt<T>(U(detail::MaxValue<T>::value())+1)); \
|
||||
VERIFY_IS_VALID_IF(CheckedInt<T>(detail::MaxValue<U>::value()), \
|
||||
(sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (isUSigned || !isTSigned)))); \
|
||||
VERIFY_IS_VALID_IF(CheckedInt<T>(detail::MinValue<U>::value()), \
|
||||
isUSigned == false ? 1 : \
|
||||
bool(isTSigned) == false ? 0 : \
|
||||
sizeof(T) >= sizeof(U)); \
|
||||
}
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int8_t)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint8_t)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int16_t)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint16_t)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int32_t)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint32_t)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int64_t)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint64_t)
|
||||
|
||||
typedef unsigned char unsignedChar;
|
||||
typedef unsigned short unsignedShort;
|
||||
typedef unsigned int unsignedInt;
|
||||
typedef unsigned long unsignedLong;
|
||||
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(char)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedChar)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(short)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedShort)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedInt)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(long)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedLong)
|
||||
|
||||
/* Test increment/decrement operators */
|
||||
|
||||
CheckedInt<T> x, y;
|
||||
x = one;
|
||||
y = x++;
|
||||
VERIFY(x == two);
|
||||
VERIFY(y == one);
|
||||
x = one;
|
||||
y = ++x;
|
||||
VERIFY(x == two);
|
||||
VERIFY(y == two);
|
||||
x = one;
|
||||
y = x--;
|
||||
VERIFY(x == zero);
|
||||
VERIFY(y == one);
|
||||
x = one;
|
||||
y = --x;
|
||||
VERIFY(x == zero);
|
||||
VERIFY(y == zero);
|
||||
x = max;
|
||||
VERIFY_IS_VALID(x++);
|
||||
x = max;
|
||||
VERIFY_IS_INVALID(++x);
|
||||
x = min;
|
||||
VERIFY_IS_VALID(x--);
|
||||
x = min;
|
||||
VERIFY_IS_INVALID(--x);
|
||||
|
||||
gIntegerTypesTested++;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test<int8_t>();
|
||||
test<uint8_t>();
|
||||
test<int16_t>();
|
||||
test<uint16_t>();
|
||||
test<int32_t>();
|
||||
test<uint32_t>();
|
||||
test<int64_t>();
|
||||
test<uint64_t>();
|
||||
|
||||
test<char>();
|
||||
test<unsigned char>();
|
||||
test<short>();
|
||||
test<unsigned short>();
|
||||
test<int>();
|
||||
test<unsigned int>();
|
||||
test<long>();
|
||||
test<unsigned long>();
|
||||
|
||||
if (gIntegerTypesTested < 8) {
|
||||
std::cerr << "Only " << gIntegerTypesTested << " have been tested. "
|
||||
<< "This should not be less than 8." << std::endl;
|
||||
gTestsFailed++;
|
||||
}
|
||||
|
||||
std::cerr << gTestsFailed << " tests failed, "
|
||||
<< gTestsPassed << " tests passed out of "
|
||||
<< gTestsFailed + gTestsPassed
|
||||
<< " tests, covering " << gIntegerTypesTested
|
||||
<< " distinct integer types." << std::endl;
|
||||
|
||||
return gTestsFailed > 0;
|
||||
}
|
|
@ -0,0 +1,595 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Benoit Jacob <bjacob@mozilla.com>
|
||||
* Jeff Muizelaar <jmuizelaar@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_CheckedInt_h
|
||||
#define mozilla_CheckedInt_h
|
||||
|
||||
#include "prtypes.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace CheckedInt_internal {
|
||||
|
||||
/* we don't want to use std::numeric_limits here because PRInt... types may not support it,
|
||||
* depending on the platform, e.g. on certain platforms they use nonstandard built-in types
|
||||
*/
|
||||
|
||||
/*** Step 1: manually record information for all the types that we want to support
|
||||
***/
|
||||
|
||||
struct unsupported_type {};
|
||||
|
||||
template<typename T> struct integer_type_manually_recorded_info
|
||||
{
|
||||
enum { is_supported = 0 };
|
||||
typedef unsupported_type twice_bigger_type;
|
||||
typedef unsupported_type unsigned_type;
|
||||
};
|
||||
|
||||
|
||||
#define CHECKEDINT_REGISTER_SUPPORTED_TYPE(T,_twice_bigger_type,_unsigned_type) \
|
||||
template<> struct integer_type_manually_recorded_info<T> \
|
||||
{ \
|
||||
enum { is_supported = 1 }; \
|
||||
typedef _twice_bigger_type twice_bigger_type; \
|
||||
typedef _unsigned_type unsigned_type; \
|
||||
static void TYPE_NOT_SUPPORTED_BY_CheckedInt() {} \
|
||||
};
|
||||
|
||||
// Type Twice Bigger Type Unsigned Type
|
||||
CHECKEDINT_REGISTER_SUPPORTED_TYPE(PRInt8, PRInt16, PRUint8)
|
||||
CHECKEDINT_REGISTER_SUPPORTED_TYPE(PRUint8, PRUint16, PRUint8)
|
||||
CHECKEDINT_REGISTER_SUPPORTED_TYPE(PRInt16, PRInt32, PRUint16)
|
||||
CHECKEDINT_REGISTER_SUPPORTED_TYPE(PRUint16, PRUint32, PRUint16)
|
||||
CHECKEDINT_REGISTER_SUPPORTED_TYPE(PRInt32, PRInt64, PRUint32)
|
||||
CHECKEDINT_REGISTER_SUPPORTED_TYPE(PRUint32, PRUint64, PRUint32)
|
||||
CHECKEDINT_REGISTER_SUPPORTED_TYPE(PRInt64, unsupported_type, PRUint64)
|
||||
CHECKEDINT_REGISTER_SUPPORTED_TYPE(PRUint64, unsupported_type, PRUint64)
|
||||
|
||||
|
||||
/*** Step 2: record some info about a given integer type,
|
||||
*** including whether it is supported, whether a twice bigger integer type
|
||||
*** is supported, what that twice bigger type is, and some stuff as found
|
||||
*** in std::numeric_limits (which we don't use because PRInt.. types may
|
||||
*** not support it, if they are defined directly from compiler built-in types).
|
||||
*** We use function names min_value() and max_value() instead of min() and max()
|
||||
*** because of stupid min/max macros in Windows headers.
|
||||
***/
|
||||
|
||||
template<typename T> struct is_unsupported_type { enum { answer = 0 }; };
|
||||
template<> struct is_unsupported_type<unsupported_type> { enum { answer = 1 }; };
|
||||
|
||||
template<typename T> struct integer_traits
|
||||
{
|
||||
typedef typename integer_type_manually_recorded_info<T>::twice_bigger_type twice_bigger_type;
|
||||
typedef typename integer_type_manually_recorded_info<T>::unsigned_type unsigned_type;
|
||||
|
||||
enum {
|
||||
is_supported = integer_type_manually_recorded_info<T>::is_supported,
|
||||
twice_bigger_type_is_supported
|
||||
= is_unsupported_type<
|
||||
typename integer_type_manually_recorded_info<T>::twice_bigger_type
|
||||
>::answer ? 0 : 1,
|
||||
size = sizeof(T),
|
||||
position_of_sign_bit = CHAR_BIT * size - 1,
|
||||
is_signed = (T(-1) > T(0)) ? 0 : 1
|
||||
};
|
||||
|
||||
static T min_value()
|
||||
{
|
||||
// bitwise ops may return a larger type, that's why we cast explicitly to T
|
||||
// in C++, left bit shifts on signed values is undefined by the standard unless the shifted value is representable.
|
||||
// notice that signed-to-unsigned conversions are always well-defined in the standard,
|
||||
// as the value congruent to 2^n as expected. By contrast, unsigned-to-signed is only well-defined if the value is
|
||||
// representable.
|
||||
return is_signed ? T(unsigned_type(1) << position_of_sign_bit) : T(0);
|
||||
}
|
||||
|
||||
static T max_value()
|
||||
{
|
||||
return ~min_value();
|
||||
}
|
||||
};
|
||||
|
||||
/*** Step 3: Implement the actual validity checks --- ideas taken from IntegerLib, code different.
|
||||
***/
|
||||
|
||||
// bitwise ops may return a larger type, so it's good to use these inline helpers guaranteeing that
|
||||
// the result is really of type T
|
||||
|
||||
template<typename T> inline T has_sign_bit(T x)
|
||||
{
|
||||
// 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. Here the unsigned-to-signed conversion is OK because the value (the result of the shift) is 0 or 1.
|
||||
typedef typename integer_traits<T>::unsigned_type unsigned_T;
|
||||
return T(unsigned_T(x) >> integer_traits<T>::position_of_sign_bit);
|
||||
}
|
||||
|
||||
template<typename T> inline T binary_complement(T x)
|
||||
{
|
||||
return ~x;
|
||||
}
|
||||
|
||||
template<typename T, typename U,
|
||||
bool is_T_signed = integer_traits<T>::is_signed,
|
||||
bool is_U_signed = integer_traits<U>::is_signed>
|
||||
struct is_in_range_impl {};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct is_in_range_impl<T, U, true, true>
|
||||
{
|
||||
static T run(U x)
|
||||
{
|
||||
return (x <= integer_traits<T>::max_value()) &&
|
||||
(x >= integer_traits<T>::min_value());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct is_in_range_impl<T, U, false, false>
|
||||
{
|
||||
static T run(U x)
|
||||
{
|
||||
return x <= integer_traits<T>::max_value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct is_in_range_impl<T, U, true, false>
|
||||
{
|
||||
static T run(U x)
|
||||
{
|
||||
if (sizeof(T) > sizeof(U))
|
||||
return 1;
|
||||
else
|
||||
return x <= U(integer_traits<T>::max_value());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct is_in_range_impl<T, U, false, true>
|
||||
{
|
||||
static T run(U x)
|
||||
{
|
||||
if (sizeof(T) >= sizeof(U))
|
||||
return x >= 0;
|
||||
else
|
||||
return (x >= 0) && (x <= U(integer_traits<T>::max_value()));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U> inline T is_in_range(U x)
|
||||
{
|
||||
return is_in_range_impl<T, U>::run(x);
|
||||
}
|
||||
|
||||
template<typename T> inline T is_add_valid(T x, T y, T result)
|
||||
{
|
||||
return integer_traits<T>::is_signed ?
|
||||
// addition is valid if the sign of x+y is equal to either that of x or that of y.
|
||||
// Beware! These bitwise operations can return a larger integer type, if T was a
|
||||
// small type like int8, so we explicitly cast to T.
|
||||
has_sign_bit(binary_complement(T((result^x) & (result^y))))
|
||||
:
|
||||
binary_complement(x) >= y;
|
||||
}
|
||||
|
||||
template<typename T> inline T is_sub_valid(T x, T y, T result)
|
||||
{
|
||||
return integer_traits<T>::is_signed ?
|
||||
// substraction is valid if either x and y have same sign, or x-y and x have same sign
|
||||
has_sign_bit(binary_complement(T((result^x) & (x^y))))
|
||||
:
|
||||
x >= y;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
bool is_signed = integer_traits<T>::is_signed,
|
||||
bool twice_bigger_type_is_supported = integer_traits<T>::twice_bigger_type_is_supported>
|
||||
struct is_mul_valid_impl {};
|
||||
|
||||
template<typename T, bool is_signed>
|
||||
struct is_mul_valid_impl<T, is_signed, true>
|
||||
{
|
||||
static T run(T x, T y)
|
||||
{
|
||||
typedef typename integer_traits<T>::twice_bigger_type twice_bigger_type;
|
||||
twice_bigger_type product = twice_bigger_type(x) * twice_bigger_type(y);
|
||||
return is_in_range<T>(product);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_mul_valid_impl<T, true, false>
|
||||
{
|
||||
static T run(T x, T y)
|
||||
{
|
||||
const T max_value = integer_traits<T>::max_value();
|
||||
const T min_value = integer_traits<T>::min_value();
|
||||
|
||||
if (x == 0 || y == 0) return true;
|
||||
|
||||
if (x > 0) {
|
||||
if (y > 0)
|
||||
return x <= max_value / y;
|
||||
else
|
||||
return y >= min_value / x;
|
||||
} else {
|
||||
if (y > 0)
|
||||
return x >= min_value / y;
|
||||
else
|
||||
return y >= max_value / x;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_mul_valid_impl<T, false, false>
|
||||
{
|
||||
static T run(T x, T y)
|
||||
{
|
||||
const T max_value = integer_traits<T>::max_value();
|
||||
if (x == 0 || y == 0) return true;
|
||||
return x <= max_value / y;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> inline T is_mul_valid(T x, T y, T /*result not used*/)
|
||||
{
|
||||
return is_mul_valid_impl<T>::run(x, y);
|
||||
}
|
||||
|
||||
template<typename T> inline T is_div_valid(T x, T y)
|
||||
{
|
||||
return integer_traits<T>::is_signed ?
|
||||
// keep in mind that min/-1 is invalid because abs(min)>max
|
||||
(y != 0) && (x != integer_traits<T>::min_value() || y != T(-1))
|
||||
:
|
||||
y != 0;
|
||||
}
|
||||
|
||||
// this is just to shut up msvc warnings about negating unsigned ints.
|
||||
template<typename T, bool is_signed = integer_traits<T>::is_signed>
|
||||
struct opposite_if_signed_impl
|
||||
{
|
||||
static T run(T x) { return -x; }
|
||||
};
|
||||
template<typename T>
|
||||
struct opposite_if_signed_impl<T, false>
|
||||
{
|
||||
static T run(T x) { return x; }
|
||||
};
|
||||
template<typename T>
|
||||
inline T opposite_if_signed(T x) { return opposite_if_signed_impl<T>::run(x); }
|
||||
|
||||
|
||||
|
||||
} // end namespace CheckedInt_internal
|
||||
|
||||
|
||||
/*** Step 4: Now define the CheckedInt class.
|
||||
***/
|
||||
|
||||
/** \class CheckedInt
|
||||
* \brief Integer wrapper class checking for integer overflow and other errors
|
||||
* \param T the integer type to wrap. Can be any of PRInt8, PRUint8, PRInt16, PRUint16,
|
||||
* PRInt32, PRUint32, PRInt64, PRUint64.
|
||||
*
|
||||
* This class implements guarded integer arithmetic. Do a computation, check that
|
||||
* valid() returns true, you then have a guarantee that no problem, such as integer overflow,
|
||||
* happened during this computation.
|
||||
*
|
||||
* The arithmetic operators in this class are guaranteed not to crash your app
|
||||
* 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 zero or integer overflow).
|
||||
* You could code it as follows:
|
||||
\code
|
||||
bool compute_x_plus_y_over_z(PRInt32 x, PRInt32 y, PRInt32 z, PRInt32 *result)
|
||||
{
|
||||
CheckedInt<PRInt32> checked_result = (CheckedInt<PRInt32>(x) + y) / z;
|
||||
*result = checked_result.value();
|
||||
return checked_result.valid();
|
||||
}
|
||||
\endcode
|
||||
*
|
||||
* Implicit conversion from plain integers to checked integers is allowed. The plain integer
|
||||
* is checked to be in range before being casted to the destination type. This means that the following
|
||||
* lines all compile, and the resulting CheckedInts are correctly detected as valid or invalid:
|
||||
* \code
|
||||
CheckedInt<PRUint8> x(1); // 1 is of type int, is found to be in range for PRUint8, x is valid
|
||||
CheckedInt<PRUint8> x(-1); // -1 is of type int, is found not to be in range for PRUint8, x is invalid
|
||||
CheckedInt<PRInt8> x(-1); // -1 is of type int, is found to be in range for PRInt8, x is valid
|
||||
CheckedInt<PRInt8> x(PRInt16(1000)); // 1000 is of type PRInt16, is found not to be in range for PRInt8, x is invalid
|
||||
CheckedInt<PRInt32> x(PRUint32(3123456789)); // 3123456789 is of type PRUint32, is found not to be in range
|
||||
// for PRInt32, x is invalid
|
||||
* \endcode
|
||||
* Implicit conversion from
|
||||
* checked integers to plain integers is not allowed. As shown in the
|
||||
* above example, to get the value of a checked integer as a normal integer, call value().
|
||||
*
|
||||
* Arithmetic operations between checked and plain integers is allowed; the result type
|
||||
* is the type of the checked integer.
|
||||
*
|
||||
* Checked integers of different types cannot be used in the same arithmetic expression.
|
||||
*
|
||||
* There are convenience typedefs for all PR integer types, of the following form (these are just 2 examples):
|
||||
\code
|
||||
typedef CheckedInt<PRInt32> CheckedInt32;
|
||||
typedef CheckedInt<PRUint16> CheckedUint16;
|
||||
\endcode
|
||||
*/
|
||||
template<typename T>
|
||||
class CheckedInt
|
||||
{
|
||||
protected:
|
||||
T mValue;
|
||||
T mIsValid; // stored as a T to limit the number of integer conversions when
|
||||
// evaluating nested arithmetic expressions.
|
||||
|
||||
template<typename U>
|
||||
CheckedInt(U value, T isValid) : mValue(value), mIsValid(isValid)
|
||||
{
|
||||
CheckedInt_internal::integer_type_manually_recorded_info<T>
|
||||
::TYPE_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 value)
|
||||
: mValue(T(value)),
|
||||
mIsValid(CheckedInt_internal::is_in_range<T>(value))
|
||||
{
|
||||
CheckedInt_internal::integer_type_manually_recorded_info<T>
|
||||
::TYPE_NOT_SUPPORTED_BY_CheckedInt();
|
||||
}
|
||||
|
||||
/** Constructs a valid checked integer with initial value 0 */
|
||||
CheckedInt() : mValue(0), mIsValid(1)
|
||||
{
|
||||
CheckedInt_internal::integer_type_manually_recorded_info<T>
|
||||
::TYPE_NOT_SUPPORTED_BY_CheckedInt();
|
||||
}
|
||||
|
||||
/** \returns the actual value */
|
||||
T value() const { 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 valid() const
|
||||
{
|
||||
return bool(mIsValid);
|
||||
}
|
||||
|
||||
/** \returns the sum. Checks for overflow. */
|
||||
template<typename U> friend CheckedInt<U> operator +(const CheckedInt<U>& lhs, const CheckedInt<U>& rhs);
|
||||
/** Adds. Checks for overflow. \returns self reference */
|
||||
template<typename U> CheckedInt& operator +=(U rhs);
|
||||
/** \returns the difference. Checks for overflow. */
|
||||
template<typename U> friend CheckedInt<U> operator -(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs);
|
||||
/** Substracts. Checks for overflow. \returns self reference */
|
||||
template<typename U> CheckedInt& operator -=(U rhs);
|
||||
/** \returns the product. Checks for overflow. */
|
||||
template<typename U> friend CheckedInt<U> operator *(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs);
|
||||
/** Multiplies. Checks for overflow. \returns self reference */
|
||||
template<typename U> CheckedInt& operator *=(U rhs);
|
||||
/** \returns the quotient. Checks for overflow and for divide-by-zero. */
|
||||
template<typename U> friend CheckedInt<U> operator /(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs);
|
||||
/** Divides. Checks for overflow and for divide-by-zero. \returns self reference */
|
||||
template<typename U> CheckedInt& operator /=(U rhs);
|
||||
|
||||
/** \returns the opposite value. Checks for overflow. */
|
||||
CheckedInt operator -() const
|
||||
{
|
||||
// circumvent msvc warning about - applied to unsigned int.
|
||||
// if we're unsigned, the only valid case anyway is 0 in which case - is a no-op.
|
||||
T result = CheckedInt_internal::opposite_if_signed(value());
|
||||
/* give the compiler a good chance to perform RVO */
|
||||
return CheckedInt(result,
|
||||
mIsValid & CheckedInt_internal::is_sub_valid(T(0), value(), result));
|
||||
}
|
||||
|
||||
/** \returns true if the left and right hand sides are valid and have the same value. */
|
||||
bool operator ==(const CheckedInt& other) const
|
||||
{
|
||||
return bool(mIsValid & other.mIsValid & (value() == other.mValue));
|
||||
}
|
||||
|
||||
/** prefix ++ */
|
||||
CheckedInt& operator++()
|
||||
{
|
||||
*this = *this + 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** postfix ++ */
|
||||
CheckedInt operator++(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this = *this + 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/** prefix -- */
|
||||
CheckedInt& operator--()
|
||||
{
|
||||
*this = *this - 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** postfix -- */
|
||||
CheckedInt operator--(int)
|
||||
{
|
||||
CheckedInt tmp = *this;
|
||||
*this = *this - 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
/** operator!= is disabled. Indeed, (a!=b) should be the same as !(a==b) but that
|
||||
* would mean that if a or b is invalid, (a!=b) is always true, which is very tricky.
|
||||
*/
|
||||
template<typename U>
|
||||
bool operator !=(U other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
#define CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \
|
||||
template<typename T> \
|
||||
inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, const CheckedInt<T> &rhs) \
|
||||
{ \
|
||||
T x = lhs.mValue; \
|
||||
T y = rhs.mValue; \
|
||||
T result = x OP y; \
|
||||
T is_op_valid \
|
||||
= CheckedInt_internal::is_##NAME##_valid(x, y, result); \
|
||||
/* give the compiler a good chance to perform RVO */ \
|
||||
return CheckedInt<T>(result, \
|
||||
lhs.mIsValid & rhs.mIsValid & is_op_valid); \
|
||||
}
|
||||
|
||||
CHECKEDINT_BASIC_BINARY_OPERATOR(add, +)
|
||||
CHECKEDINT_BASIC_BINARY_OPERATOR(sub, -)
|
||||
CHECKEDINT_BASIC_BINARY_OPERATOR(mul, *)
|
||||
|
||||
// division can't be implemented by CHECKEDINT_BASIC_BINARY_OPERATOR
|
||||
// because if rhs == 0, we are not allowed to even try to compute the quotient.
|
||||
template<typename T>
|
||||
inline CheckedInt<T> operator /(const CheckedInt<T> &lhs, const CheckedInt<T> &rhs)
|
||||
{
|
||||
T x = lhs.mValue;
|
||||
T y = rhs.mValue;
|
||||
T is_op_valid = CheckedInt_internal::is_div_valid(x, y);
|
||||
T result = is_op_valid ? (x / y) : 0;
|
||||
/* give the compiler a good chance to perform RVO */
|
||||
return CheckedInt<T>(result,
|
||||
lhs.mIsValid & rhs.mIsValid & is_op_valid);
|
||||
}
|
||||
|
||||
// implement cast_to_CheckedInt<T>(x), making sure that
|
||||
// - it allows x to be either a CheckedInt<T> or any integer type that can be casted to T
|
||||
// - if x is already a CheckedInt<T>, we just return a reference to it, instead of copying it (optimization)
|
||||
|
||||
template<typename T, typename U>
|
||||
struct cast_to_CheckedInt_impl
|
||||
{
|
||||
typedef CheckedInt<T> return_type;
|
||||
static CheckedInt<T> run(U u) { return u; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct cast_to_CheckedInt_impl<T, CheckedInt<T> >
|
||||
{
|
||||
typedef const CheckedInt<T>& return_type;
|
||||
static const CheckedInt<T>& run(const CheckedInt<T>& u) { return u; }
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
inline typename cast_to_CheckedInt_impl<T, U>::return_type
|
||||
cast_to_CheckedInt(U u)
|
||||
{
|
||||
return cast_to_CheckedInt_impl<T, U>::run(u);
|
||||
}
|
||||
|
||||
#define 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 cast_to_CheckedInt<T>(rhs); \
|
||||
return *this; \
|
||||
} \
|
||||
template<typename T, typename U> \
|
||||
inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, U rhs) \
|
||||
{ \
|
||||
return lhs OP cast_to_CheckedInt<T>(rhs); \
|
||||
} \
|
||||
template<typename T, typename U> \
|
||||
inline CheckedInt<T> operator OP(U lhs, const CheckedInt<T> &rhs) \
|
||||
{ \
|
||||
return cast_to_CheckedInt<T>(lhs) OP rhs; \
|
||||
}
|
||||
|
||||
CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=)
|
||||
CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=)
|
||||
CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=)
|
||||
CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=)
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool operator ==(const CheckedInt<T> &lhs, U rhs)
|
||||
{
|
||||
return lhs == cast_to_CheckedInt<T>(rhs);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline bool operator ==(U lhs, const CheckedInt<T> &rhs)
|
||||
{
|
||||
return cast_to_CheckedInt<T>(lhs) == rhs;
|
||||
}
|
||||
|
||||
// convenience typedefs.
|
||||
// the use of a macro here helps make sure that we don't let a typo slip into some of these.
|
||||
#define CHECKEDINT_MAKE_TYPEDEF(Type) \
|
||||
typedef CheckedInt<PR##Type> Checked##Type;
|
||||
|
||||
CHECKEDINT_MAKE_TYPEDEF(Int8)
|
||||
CHECKEDINT_MAKE_TYPEDEF(Uint8)
|
||||
CHECKEDINT_MAKE_TYPEDEF(Int16)
|
||||
CHECKEDINT_MAKE_TYPEDEF(Uint16)
|
||||
CHECKEDINT_MAKE_TYPEDEF(Int32)
|
||||
CHECKEDINT_MAKE_TYPEDEF(Uint32)
|
||||
CHECKEDINT_MAKE_TYPEDEF(Int64)
|
||||
CHECKEDINT_MAKE_TYPEDEF(Uint64)
|
||||
|
||||
} // end namespace mozilla
|
||||
|
||||
#endif /* mozilla_CheckedInt_h */
|
|
@ -110,6 +110,7 @@ EXPORTS = \
|
|||
nsHashPropertyBag.h \
|
||||
nsWhitespaceTokenizer.h \
|
||||
nsCharSeparatedTokenizer.h \
|
||||
CheckedInt.h \
|
||||
$(NULL)
|
||||
|
||||
XPIDLSRCS = \
|
||||
|
|
|
@ -105,6 +105,7 @@ CPP_UNIT_TESTS = \
|
|||
TestRefPtr.cpp \
|
||||
TestSettingsAPI.cpp \
|
||||
TestTextFormatter.cpp \
|
||||
TestCheckedInt.cpp \
|
||||
TestTArray.cpp \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,488 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Benoit Jacob <bjacob@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
#include "CheckedInt.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace CheckedInt_test {
|
||||
|
||||
using namespace mozilla::CheckedInt_internal;
|
||||
using mozilla::CheckedInt;
|
||||
|
||||
int g_tests_passed = 0;
|
||||
int g_tests_failed = 0;
|
||||
|
||||
void verify_impl_function(bool x, bool expected,
|
||||
const char* file, int line,
|
||||
int T_size, bool T_is_signed)
|
||||
{
|
||||
if (x == expected) {
|
||||
g_tests_passed++;
|
||||
} else {
|
||||
g_tests_failed++;
|
||||
std::cerr << "Test failed at " << file << ":" << line;
|
||||
std::cerr << " with T a ";
|
||||
if(T_is_signed)
|
||||
std::cerr << "signed";
|
||||
else
|
||||
std::cerr << "unsigned";
|
||||
std::cerr << " " << CHAR_BIT*T_size << "-bit integer type" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_IMPL(x, expected) \
|
||||
verify_impl_function((x), (expected), __FILE__, __LINE__, sizeof(T), integer_traits<T>::is_signed)
|
||||
|
||||
#define VERIFY(x) VERIFY_IMPL(x, true)
|
||||
#define VERIFY_IS_FALSE(x) VERIFY_IMPL(x, false)
|
||||
#define VERIFY_IS_VALID(x) VERIFY_IMPL((x).valid(), true)
|
||||
#define VERIFY_IS_INVALID(x) VERIFY_IMPL((x).valid(), false)
|
||||
#define VERIFY_IS_VALID_IF(x,condition) VERIFY_IMPL((x).valid(), (condition))
|
||||
|
||||
template<typename T, unsigned int size = sizeof(T)>
|
||||
struct test_twice_bigger_type
|
||||
{
|
||||
static void run()
|
||||
{
|
||||
VERIFY(integer_traits<T>::twice_bigger_type_is_supported);
|
||||
VERIFY(sizeof(typename integer_traits<T>::twice_bigger_type)
|
||||
== 2 * sizeof(T));
|
||||
VERIFY(bool(integer_traits<
|
||||
typename integer_traits<T>::twice_bigger_type
|
||||
>::is_signed) == bool(integer_traits<T>::is_signed));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct test_twice_bigger_type<T, 8>
|
||||
{
|
||||
static void run()
|
||||
{
|
||||
VERIFY_IS_FALSE(integer_traits<T>::twice_bigger_type_is_supported);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
void test()
|
||||
{
|
||||
static bool already_run = false;
|
||||
if (already_run) {
|
||||
g_tests_failed++;
|
||||
std::cerr << "You already tested this type. Copy/paste typo??" << std::endl;
|
||||
return;
|
||||
}
|
||||
already_run = true;
|
||||
|
||||
VERIFY(integer_traits<T>::is_supported);
|
||||
VERIFY(integer_traits<T>::size == sizeof(T));
|
||||
enum{ is_signed = integer_traits<T>::is_signed };
|
||||
VERIFY(bool(is_signed) == !bool(T(-1) > T(0)));
|
||||
|
||||
test_twice_bigger_type<T>::run();
|
||||
|
||||
typedef typename integer_traits<T>::unsigned_type unsigned_T;
|
||||
|
||||
VERIFY(sizeof(unsigned_T) == sizeof(T));
|
||||
VERIFY(integer_traits<unsigned_T>::is_signed == false);
|
||||
|
||||
CheckedInt<T> max_value(integer_traits<T>::max_value());
|
||||
CheckedInt<T> min_value(integer_traits<T>::min_value());
|
||||
|
||||
// check min_value() and max_value(), since they are custom implementations and a mistake there
|
||||
// could potentially NOT be caught by any other tests... while making everything wrong!
|
||||
|
||||
T bit = 1;
|
||||
for(unsigned int i = 0; i < sizeof(T) * CHAR_BIT - 1; i++)
|
||||
{
|
||||
VERIFY((min_value.value() & bit) == 0);
|
||||
bit <<= 1;
|
||||
}
|
||||
VERIFY((min_value.value() & bit) == (is_signed ? bit : T(0)));
|
||||
VERIFY(max_value.value() == T(~(min_value.value())));
|
||||
|
||||
CheckedInt<T> zero(0);
|
||||
CheckedInt<T> one(1);
|
||||
CheckedInt<T> two(2);
|
||||
CheckedInt<T> three(3);
|
||||
CheckedInt<T> four(4);
|
||||
|
||||
/* addition / substraction checks */
|
||||
|
||||
VERIFY_IS_VALID(zero+zero);
|
||||
VERIFY(zero+zero == zero);
|
||||
VERIFY_IS_FALSE(zero+zero == one); // check that == doesn't always return true
|
||||
VERIFY_IS_VALID(zero+one);
|
||||
VERIFY(zero+one == one);
|
||||
VERIFY_IS_VALID(one+one);
|
||||
VERIFY(one+one == two);
|
||||
|
||||
CheckedInt<T> max_value_minus_one = max_value - one;
|
||||
CheckedInt<T> max_value_minus_two = max_value - two;
|
||||
VERIFY_IS_VALID(max_value_minus_one);
|
||||
VERIFY_IS_VALID(max_value_minus_two);
|
||||
VERIFY_IS_VALID(max_value_minus_one + one);
|
||||
VERIFY_IS_VALID(max_value_minus_two + one);
|
||||
VERIFY_IS_VALID(max_value_minus_two + two);
|
||||
VERIFY(max_value_minus_one + one == max_value);
|
||||
VERIFY(max_value_minus_two + one == max_value_minus_one);
|
||||
VERIFY(max_value_minus_two + two == max_value);
|
||||
|
||||
VERIFY_IS_VALID(max_value + zero);
|
||||
VERIFY_IS_VALID(max_value - zero);
|
||||
VERIFY_IS_INVALID(max_value + one);
|
||||
VERIFY_IS_INVALID(max_value + two);
|
||||
VERIFY_IS_INVALID(max_value + max_value_minus_one);
|
||||
VERIFY_IS_INVALID(max_value + max_value);
|
||||
|
||||
CheckedInt<T> min_value_plus_one = min_value + one;
|
||||
CheckedInt<T> min_value_plus_two = min_value + two;
|
||||
VERIFY_IS_VALID(min_value_plus_one);
|
||||
VERIFY_IS_VALID(min_value_plus_two);
|
||||
VERIFY_IS_VALID(min_value_plus_one - one);
|
||||
VERIFY_IS_VALID(min_value_plus_two - one);
|
||||
VERIFY_IS_VALID(min_value_plus_two - two);
|
||||
VERIFY(min_value_plus_one - one == min_value);
|
||||
VERIFY(min_value_plus_two - one == min_value_plus_one);
|
||||
VERIFY(min_value_plus_two - two == min_value);
|
||||
|
||||
CheckedInt<T> min_value_minus_one = min_value - one;
|
||||
VERIFY_IS_VALID(min_value + zero);
|
||||
VERIFY_IS_VALID(min_value - zero);
|
||||
VERIFY_IS_INVALID(min_value - one);
|
||||
VERIFY_IS_INVALID(min_value - two);
|
||||
VERIFY_IS_INVALID(min_value - min_value_minus_one);
|
||||
VERIFY_IS_VALID(min_value - min_value);
|
||||
|
||||
CheckedInt<T> max_value_over_two = max_value / two;
|
||||
VERIFY_IS_VALID(max_value_over_two + max_value_over_two);
|
||||
VERIFY_IS_VALID(max_value_over_two + one);
|
||||
VERIFY((max_value_over_two + one) - one == max_value_over_two);
|
||||
VERIFY_IS_VALID(max_value_over_two - max_value_over_two);
|
||||
VERIFY(max_value_over_two - max_value_over_two == zero);
|
||||
|
||||
CheckedInt<T> min_value_over_two = min_value / two;
|
||||
VERIFY_IS_VALID(min_value_over_two + min_value_over_two);
|
||||
VERIFY_IS_VALID(min_value_over_two + one);
|
||||
VERIFY((min_value_over_two + one) - one == min_value_over_two);
|
||||
VERIFY_IS_VALID(min_value_over_two - min_value_over_two);
|
||||
VERIFY(min_value_over_two - min_value_over_two == zero);
|
||||
|
||||
VERIFY_IS_INVALID(min_value - one);
|
||||
VERIFY_IS_INVALID(min_value - two);
|
||||
|
||||
if (is_signed) {
|
||||
VERIFY_IS_INVALID(min_value + min_value);
|
||||
VERIFY_IS_INVALID(min_value_over_two + min_value_over_two + min_value_over_two);
|
||||
VERIFY_IS_INVALID(zero - min_value + min_value);
|
||||
VERIFY_IS_INVALID(one - min_value + min_value);
|
||||
}
|
||||
|
||||
/* unary operator- checks */
|
||||
|
||||
CheckedInt<T> neg_one = -one;
|
||||
CheckedInt<T> neg_two = -two;
|
||||
|
||||
if (is_signed) {
|
||||
VERIFY_IS_VALID(-max_value);
|
||||
VERIFY_IS_VALID(-max_value - one);
|
||||
VERIFY_IS_VALID(neg_one);
|
||||
VERIFY_IS_VALID(-max_value + neg_one);
|
||||
VERIFY_IS_VALID(neg_one + one);
|
||||
VERIFY(neg_one + one == zero);
|
||||
VERIFY_IS_VALID(neg_two);
|
||||
VERIFY_IS_VALID(neg_one + neg_one);
|
||||
VERIFY(neg_one + neg_one == neg_two);
|
||||
} else {
|
||||
VERIFY_IS_INVALID(neg_one);
|
||||
}
|
||||
|
||||
/* multiplication checks */
|
||||
|
||||
VERIFY_IS_VALID(zero*zero);
|
||||
VERIFY(zero*zero == zero);
|
||||
VERIFY_IS_VALID(zero*one);
|
||||
VERIFY(zero*one == zero);
|
||||
VERIFY_IS_VALID(one*zero);
|
||||
VERIFY(one*zero == zero);
|
||||
VERIFY_IS_VALID(one*one);
|
||||
VERIFY(one*one == one);
|
||||
VERIFY_IS_VALID(one*three);
|
||||
VERIFY(one*three == three);
|
||||
VERIFY_IS_VALID(two*two);
|
||||
VERIFY(two*two == four);
|
||||
|
||||
VERIFY_IS_INVALID(max_value * max_value);
|
||||
VERIFY_IS_INVALID(max_value_over_two * max_value);
|
||||
VERIFY_IS_INVALID(max_value_over_two * max_value_over_two);
|
||||
|
||||
CheckedInt<T> max_value_approx_sqrt(T(T(1) << (CHAR_BIT*sizeof(T)/2)));
|
||||
|
||||
VERIFY_IS_VALID(max_value_approx_sqrt);
|
||||
VERIFY_IS_VALID(max_value_approx_sqrt * two);
|
||||
VERIFY_IS_INVALID(max_value_approx_sqrt * max_value_approx_sqrt);
|
||||
VERIFY_IS_INVALID(max_value_approx_sqrt * max_value_approx_sqrt * max_value_approx_sqrt);
|
||||
|
||||
if (is_signed) {
|
||||
VERIFY_IS_INVALID(min_value * min_value);
|
||||
VERIFY_IS_INVALID(min_value_over_two * min_value);
|
||||
VERIFY_IS_INVALID(min_value_over_two * min_value_over_two);
|
||||
|
||||
CheckedInt<T> min_value_approx_sqrt = -max_value_approx_sqrt;
|
||||
|
||||
VERIFY_IS_VALID(min_value_approx_sqrt);
|
||||
VERIFY_IS_VALID(min_value_approx_sqrt * two);
|
||||
VERIFY_IS_INVALID(min_value_approx_sqrt * max_value_approx_sqrt);
|
||||
VERIFY_IS_INVALID(min_value_approx_sqrt * min_value_approx_sqrt);
|
||||
}
|
||||
|
||||
// make sure to check all 4 paths in signed multiplication validity check.
|
||||
// test positive * positive
|
||||
VERIFY_IS_VALID(max_value * one);
|
||||
VERIFY(max_value * one == max_value);
|
||||
VERIFY_IS_INVALID(max_value * two);
|
||||
VERIFY_IS_VALID(max_value_over_two * two);
|
||||
VERIFY((max_value_over_two + max_value_over_two) == (max_value_over_two * two));
|
||||
|
||||
if (is_signed) {
|
||||
// test positive * negative
|
||||
VERIFY_IS_VALID(max_value * neg_one);
|
||||
VERIFY_IS_VALID(-max_value);
|
||||
VERIFY(max_value * neg_one == -max_value);
|
||||
VERIFY_IS_VALID(one * min_value);
|
||||
VERIFY_IS_INVALID(max_value * neg_two);
|
||||
VERIFY_IS_VALID(max_value_over_two * neg_two);
|
||||
VERIFY_IS_VALID(two * min_value_over_two);
|
||||
VERIFY_IS_VALID((max_value_over_two + one) * neg_two);
|
||||
VERIFY_IS_INVALID((max_value_over_two + two) * neg_two);
|
||||
VERIFY_IS_INVALID(two * (min_value_over_two - one));
|
||||
|
||||
// test negative * positive
|
||||
VERIFY_IS_VALID(min_value * one);
|
||||
VERIFY_IS_VALID(min_value_plus_one * one);
|
||||
VERIFY_IS_INVALID(min_value * two);
|
||||
VERIFY_IS_VALID(min_value_over_two * two);
|
||||
VERIFY(min_value_over_two * two == min_value);
|
||||
VERIFY_IS_INVALID((min_value_over_two - one) * neg_two);
|
||||
VERIFY_IS_INVALID(neg_two * max_value);
|
||||
VERIFY_IS_VALID(min_value_over_two * two);
|
||||
VERIFY(min_value_over_two * two == min_value);
|
||||
VERIFY_IS_VALID(neg_two * max_value_over_two);
|
||||
VERIFY_IS_INVALID((min_value_over_two - one) * two);
|
||||
VERIFY_IS_VALID(neg_two * (max_value_over_two + one));
|
||||
VERIFY_IS_INVALID(neg_two * (max_value_over_two + two));
|
||||
|
||||
// test negative * negative
|
||||
VERIFY_IS_INVALID(min_value * neg_one);
|
||||
VERIFY_IS_VALID(min_value_plus_one * neg_one);
|
||||
VERIFY(min_value_plus_one * neg_one == max_value);
|
||||
VERIFY_IS_INVALID(min_value * neg_two);
|
||||
VERIFY_IS_INVALID(min_value_over_two * neg_two);
|
||||
VERIFY_IS_INVALID(neg_one * min_value);
|
||||
VERIFY_IS_VALID(neg_one * min_value_plus_one);
|
||||
VERIFY(neg_one * min_value_plus_one == max_value);
|
||||
VERIFY_IS_INVALID(neg_two * min_value);
|
||||
VERIFY_IS_INVALID(neg_two * min_value_over_two);
|
||||
}
|
||||
|
||||
/* division checks */
|
||||
|
||||
VERIFY_IS_VALID(one / one);
|
||||
VERIFY(one / one == one);
|
||||
VERIFY_IS_VALID(three / three);
|
||||
VERIFY(three / three == one);
|
||||
VERIFY_IS_VALID(four / two);
|
||||
VERIFY(four / two == two);
|
||||
VERIFY((four*three)/four == three);
|
||||
|
||||
// check that div by zero is invalid
|
||||
VERIFY_IS_INVALID(zero / zero);
|
||||
VERIFY_IS_INVALID(one / zero);
|
||||
VERIFY_IS_INVALID(two / zero);
|
||||
VERIFY_IS_INVALID(neg_one / zero);
|
||||
VERIFY_IS_INVALID(max_value / zero);
|
||||
VERIFY_IS_INVALID(min_value / zero);
|
||||
|
||||
if (is_signed) {
|
||||
// check that min_value / -1 is invalid
|
||||
VERIFY_IS_INVALID(min_value / neg_one);
|
||||
|
||||
// check that the test for div by -1 isn't banning other numerators than min_value
|
||||
VERIFY_IS_VALID(one / neg_one);
|
||||
VERIFY_IS_VALID(zero / neg_one);
|
||||
VERIFY_IS_VALID(neg_one / neg_one);
|
||||
VERIFY_IS_VALID(max_value / neg_one);
|
||||
}
|
||||
|
||||
/* check that invalidity is correctly preserved by arithmetic ops */
|
||||
|
||||
CheckedInt<T> some_invalid = max_value + max_value;
|
||||
VERIFY_IS_INVALID(some_invalid + zero);
|
||||
VERIFY_IS_INVALID(some_invalid - zero);
|
||||
VERIFY_IS_INVALID(zero + some_invalid);
|
||||
VERIFY_IS_INVALID(zero - some_invalid);
|
||||
VERIFY_IS_INVALID(-some_invalid);
|
||||
VERIFY_IS_INVALID(some_invalid * zero);
|
||||
VERIFY_IS_INVALID(some_invalid * one);
|
||||
VERIFY_IS_INVALID(zero * some_invalid);
|
||||
VERIFY_IS_INVALID(one * some_invalid);
|
||||
VERIFY_IS_INVALID(some_invalid / zero);
|
||||
VERIFY_IS_INVALID(some_invalid / one);
|
||||
VERIFY_IS_INVALID(zero / some_invalid);
|
||||
VERIFY_IS_INVALID(one / some_invalid);
|
||||
VERIFY_IS_INVALID(some_invalid + some_invalid);
|
||||
VERIFY_IS_INVALID(some_invalid - some_invalid);
|
||||
VERIFY_IS_INVALID(some_invalid * some_invalid);
|
||||
VERIFY_IS_INVALID(some_invalid / some_invalid);
|
||||
|
||||
/* check that mixing checked integers with plain integers in expressions is allowed */
|
||||
|
||||
VERIFY(one + T(2) == three);
|
||||
VERIFY(2 + one == three);
|
||||
{
|
||||
CheckedInt<T> x = one;
|
||||
x += 2;
|
||||
VERIFY(x == three);
|
||||
}
|
||||
VERIFY(two - 1 == one);
|
||||
VERIFY(2 - one == one);
|
||||
{
|
||||
CheckedInt<T> x = two;
|
||||
x -= 1;
|
||||
VERIFY(x == one);
|
||||
}
|
||||
VERIFY(one * 2 == two);
|
||||
VERIFY(2 * one == two);
|
||||
{
|
||||
CheckedInt<T> x = one;
|
||||
x *= 2;
|
||||
VERIFY(x == two);
|
||||
}
|
||||
VERIFY(four / 2 == two);
|
||||
VERIFY(4 / two == two);
|
||||
{
|
||||
CheckedInt<T> x = four;
|
||||
x /= 2;
|
||||
VERIFY(x == two);
|
||||
}
|
||||
|
||||
VERIFY(one == 1);
|
||||
VERIFY(1 == one);
|
||||
VERIFY_IS_FALSE(two == 1);
|
||||
VERIFY_IS_FALSE(1 == two);
|
||||
VERIFY_IS_FALSE(some_invalid == 1);
|
||||
VERIFY_IS_FALSE(1 == some_invalid);
|
||||
|
||||
/* Check that construction of CheckedInt from an integer value of a mismatched type is checked */
|
||||
|
||||
#define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(U) \
|
||||
{ \
|
||||
bool is_U_signed = integer_traits<U>::is_signed; \
|
||||
VERIFY_IS_VALID(CheckedInt<T>(U(0))); \
|
||||
VERIFY_IS_VALID(CheckedInt<T>(U(1))); \
|
||||
VERIFY_IS_VALID(CheckedInt<T>(U(100))); \
|
||||
if (is_U_signed) \
|
||||
VERIFY_IS_VALID_IF(CheckedInt<T>(U(-1)), is_signed); \
|
||||
if (sizeof(U) > sizeof(T)) \
|
||||
VERIFY_IS_INVALID(CheckedInt<T>(U(integer_traits<T>::max_value())+1)); \
|
||||
VERIFY_IS_VALID_IF(CheckedInt<T>(integer_traits<U>::max_value()), \
|
||||
(sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (is_U_signed || !is_signed)))); \
|
||||
VERIFY_IS_VALID_IF(CheckedInt<T>(integer_traits<U>::min_value()), \
|
||||
is_U_signed == false ? 1 : \
|
||||
bool(is_signed) == false ? 0 : \
|
||||
sizeof(T) >= sizeof(U)); \
|
||||
}
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt8)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint8)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt16)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint16)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt32)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint32)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRInt64)
|
||||
VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(PRUint64)
|
||||
|
||||
/* Test increment/decrement operators */
|
||||
|
||||
CheckedInt<T> x, y;
|
||||
x = one;
|
||||
y = x++;
|
||||
VERIFY(x == two);
|
||||
VERIFY(y == one);
|
||||
x = one;
|
||||
y = ++x;
|
||||
VERIFY(x == two);
|
||||
VERIFY(y == two);
|
||||
x = one;
|
||||
y = x--;
|
||||
VERIFY(x == zero);
|
||||
VERIFY(y == one);
|
||||
x = one;
|
||||
y = --x;
|
||||
VERIFY(x == zero);
|
||||
VERIFY(y == zero);
|
||||
x = max_value;
|
||||
VERIFY_IS_VALID(x++);
|
||||
x = max_value;
|
||||
VERIFY_IS_INVALID(++x);
|
||||
x = min_value;
|
||||
VERIFY_IS_VALID(x--);
|
||||
x = min_value;
|
||||
VERIFY_IS_INVALID(--x);
|
||||
}
|
||||
|
||||
} // end namespace CheckedInt_test
|
||||
|
||||
int main()
|
||||
{
|
||||
CheckedInt_test::test<PRInt8>();
|
||||
CheckedInt_test::test<PRUint8>();
|
||||
CheckedInt_test::test<PRInt16>();
|
||||
CheckedInt_test::test<PRUint16>();
|
||||
CheckedInt_test::test<PRInt32>();
|
||||
CheckedInt_test::test<PRUint32>();
|
||||
CheckedInt_test::test<PRInt64>();
|
||||
CheckedInt_test::test<PRUint64>();
|
||||
|
||||
std::cerr << CheckedInt_test::g_tests_failed << " tests failed, "
|
||||
<< CheckedInt_test::g_tests_passed << " tests passed out of "
|
||||
<< CheckedInt_test::g_tests_failed + CheckedInt_test::g_tests_passed
|
||||
<< " tests." << std::endl;
|
||||
|
||||
return CheckedInt_test::g_tests_failed > 0;
|
||||
}
|
Загрузка…
Ссылка в новой задаче