Skip to content

Reducing Warnings - Safe Integral Comparisons #960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libbson/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ set (HEADERS
${PROJECT_SOURCE_DIR}/src/bson/bcon.h
${PROJECT_SOURCE_DIR}/src/bson/bson-atomic.h
${PROJECT_SOURCE_DIR}/src/bson/bson-clock.h
${PROJECT_SOURCE_DIR}/src/bson/bson-cmp.h
${PROJECT_SOURCE_DIR}/src/bson/bson-compat.h
${PROJECT_SOURCE_DIR}/src/bson/bson-context.h
${PROJECT_SOURCE_DIR}/src/bson/bson-decimal128.h
Expand Down
1 change: 1 addition & 0 deletions src/libbson/src/bson/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ set (src_libbson_src_bson_DIST_hs
bson-memory.h
bson-oid.h
bson-reader.h
bson-cmp.h
bson-string.h
bson-types.h
bson-utf8.h
Expand Down
195 changes: 195 additions & 0 deletions src/libbson/src/bson/bson-cmp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright 2022 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "bson-prelude.h"


#ifndef BSON_CMP_H
#define BSON_CMP_H


#include "bson-compat.h" /* ssize_t */
#include "bson-macros.h" /* BSON_CONCAT */

#include <limits.h>
#include <stdbool.h>
#include <stdint.h>


BSON_BEGIN_DECLS


/* Based on the "Safe Integral Comparisons" proposal merged in C++20:
* http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0586r2.html
*
* Due to lack of type deduction in C, relational comparison functions (e.g.
* `cmp_less`) are defined in sets of four "functions" according to the
* signedness of each value argument, e.g.:
* - bson_cmp_less_ss (signed-value, signed-value)
* - bson_cmp_less_uu (unsigned-value, unsigned-value)
* - bson_cmp_less_su (signed-value, unsigned-value)
* - bson_cmp_less_us (unsigned-value, signed-value)
*
* Similarly, the `in_range` function is defined as a set of two "functions"
* according to the signedness of the value argument:
* - bson_in_range_signed (Type, signed-value)
* - bson_in_range_unsigned (Type, unsigned-value)
*
* The user must take care to use the correct signedness for the provided
* argument(s). Enabling compiler warnings for implicit sign conversions is
* recommended.
*/


#define BSON_CMP_SET(op, ss, uu, su, us) \
static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _ss) (int64_t t, \
int64_t u) \
{ \
return (ss); \
} \
\
static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _uu) (uint64_t t, \
uint64_t u) \
{ \
return (uu); \
} \
\
static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _su) (int64_t t, \
uint64_t u) \
{ \
return (su); \
} \
\
static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _us) (uint64_t t, \
int64_t u) \
{ \
return (us); \
}

BSON_CMP_SET (equal,
t == u,
t == u,
t < 0 ? false : (uint64_t) (t) == u,
u < 0 ? false : t == (uint64_t) (u))

BSON_CMP_SET (not_equal,
!bson_cmp_equal_ss (t, u),
!bson_cmp_equal_uu (t, u),
!bson_cmp_equal_su (t, u),
!bson_cmp_equal_us (t, u))

BSON_CMP_SET (less,
t < u,
t < u,
t < 0 ? true : (uint64_t) (t) < u,
u < 0 ? false : t < (uint64_t) (u))

BSON_CMP_SET (greater,
bson_cmp_less_ss (u, t),
bson_cmp_less_uu (u, t),
bson_cmp_less_us (u, t),
bson_cmp_less_su (u, t))

BSON_CMP_SET (less_equal,
!bson_cmp_greater_ss (t, u),
!bson_cmp_greater_uu (t, u),
!bson_cmp_greater_su (t, u),
!bson_cmp_greater_us (t, u))

BSON_CMP_SET (greater_equal,
!bson_cmp_less_ss (t, u),
!bson_cmp_less_uu (t, u),
!bson_cmp_less_su (t, u),
!bson_cmp_less_us (t, u))

#undef BSON_CMP_SET


/* Define in_range functions for *signed* type Type. */
#define BSON_IN_RANGE_SET_SIGNED(Type, min, max) \
static BSON_INLINE bool BSON_CONCAT3 (bson_in_range_, Type, _signed) ( \
int64_t value) \
{ \
return bson_cmp_greater_equal_ss (value, min) && \
bson_cmp_less_equal_ss (value, max); \
} \
\
static BSON_INLINE bool BSON_CONCAT3 (bson_in_range_, Type, _unsigned) ( \
uint64_t value) \
{ \
return bson_cmp_greater_equal_us (value, min) && \
bson_cmp_less_equal_us (value, max); \
}

/* Define in_range functions for *unsigned* type Type. */
#define BSON_IN_RANGE_SET_UNSIGNED(Type, max) \
static BSON_INLINE bool BSON_CONCAT3 (bson_in_range_, Type, _signed) ( \
int64_t value) \
{ \
return bson_cmp_greater_equal_su (value, 0u) && \
bson_cmp_less_equal_su (value, max); \
} \
\
static BSON_INLINE bool BSON_CONCAT3 (bson_in_range_, Type, _unsigned) ( \
uint64_t value) \
{ \
return bson_cmp_less_equal_uu (value, max); \
}

BSON_IN_RANGE_SET_SIGNED (signed_char, SCHAR_MIN, SCHAR_MAX)
BSON_IN_RANGE_SET_SIGNED (short, SHRT_MIN, SHRT_MAX)
BSON_IN_RANGE_SET_SIGNED (int, INT_MIN, INT_MAX)
BSON_IN_RANGE_SET_SIGNED (long, LONG_MIN, LONG_MAX)
BSON_IN_RANGE_SET_SIGNED (long_long, LLONG_MIN, LLONG_MAX)

BSON_IN_RANGE_SET_UNSIGNED (unsigned_char, UCHAR_MAX)
BSON_IN_RANGE_SET_UNSIGNED (unsigned_short, USHRT_MAX)
BSON_IN_RANGE_SET_UNSIGNED (unsigned_int, UINT_MAX)
BSON_IN_RANGE_SET_UNSIGNED (unsigned_long, ULONG_MAX)
BSON_IN_RANGE_SET_UNSIGNED (unsigned_long_long, ULLONG_MAX)

BSON_IN_RANGE_SET_SIGNED (int8_t, INT8_MIN, INT8_MAX)
BSON_IN_RANGE_SET_SIGNED (int16_t, INT16_MIN, INT16_MAX)
BSON_IN_RANGE_SET_SIGNED (int32_t, INT32_MIN, INT32_MAX)
BSON_IN_RANGE_SET_SIGNED (int64_t, INT64_MIN, INT64_MAX)

BSON_IN_RANGE_SET_UNSIGNED (uint8_t, UINT8_MAX)
BSON_IN_RANGE_SET_UNSIGNED (uint16_t, UINT16_MAX)
BSON_IN_RANGE_SET_UNSIGNED (uint32_t, UINT32_MAX)
BSON_IN_RANGE_SET_UNSIGNED (uint64_t, UINT64_MAX)

BSON_IN_RANGE_SET_SIGNED (ssize_t, SSIZE_MIN, SSIZE_MAX)
BSON_IN_RANGE_SET_UNSIGNED (size_t, SIZE_MAX)

#undef BSON_IN_RANGE_SET_SIGNED
#undef BSON_IN_RANGE_SET_UNSIGNED


/* Return true if the value with *signed* type is in the representable range of
* Type and false otherwise. */
#define bson_in_range_signed(Type, value) \
BSON_CONCAT3 (bson_in_range_, Type, _signed) (value)

/* Return true if the value with *unsigned* type is in the representable range
* of Type and false otherwise. */
#define bson_in_range_unsigned(Type, value) \
BSON_CONCAT3 (bson_in_range_, Type, _unsigned) (value)


BSON_END_DECLS


#endif /* BSON_CMP_H */
151 changes: 151 additions & 0 deletions src/libbson/src/bson/bson-compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,157 @@ typedef SSIZE_T ssize_t;
#endif
#endif

/* Derive the maximum representable value of signed integer type T using the
* formula 2^(N - 1) - 1 where N is the number of bits in type T. This assumes
* T is represented using two's complement. */
#define BSON_NUMERIC_LIMITS_MAX_SIGNED(T) \
((T) ((((size_t) 0x01u) << (sizeof (T) * (size_t) CHAR_BIT - 1u)) - 1u))

/* Derive the minimum representable value of signed integer type T as one less
* than the negation of its maximum representable value. This assumes T is
* represented using two's complement. */
#define BSON_NUMERIC_LIMITS_MIN_SIGNED(T, max) ((T) ((-(max)) - 1))

/* Derive the maximum representable value of unsigned integer type T by flipping
* all its bits to 1. */
#define BSON_NUMERIC_LIMITS_MAX_UNSIGNED(T) ((T) (~((T) 0)))

/* Define numeric limit constants if not already available for C90
* compatibility. These can be removed once C99 is declared the minimum
* supported C standard. */
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L

#ifndef SCHAR_MAX
#define SCHAR_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (signed char)
#endif

#ifndef SHRT_MAX
#define SHRT_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (short)
#endif

#ifndef INT_MAX
#define INT_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (int)
#endif

#ifndef LONG_MAX
#define LONG_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (long)
#endif

#ifndef LLONG_MAX
#define LLONG_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (long long)
#endif

#ifndef UCHAR_MAX
#define UCHAR_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (unsigned char)
#endif

#ifndef USHRT_MAX
#define USHRT_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (unsigned short)
#endif

#ifndef UINT_MAX
#define UINT_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (unsigned int)
#endif

#ifndef ULONG_MAX
#define ULONG_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (unsigned long)
#endif

#ifndef ULLONG_MAX
#define ULLONG_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (unsigned long long)
#endif

#ifndef INT8_MAX
#define INT8_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (int8_t)
#endif

#ifndef INT16_MAX
#define INT16_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (int16_t)
#endif

#ifndef INT32_MAX
#define INT32_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (int32_t)
#endif

#ifndef INT64_MAX
#define INT64_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (int64_t)
#endif

#ifndef UINT8_MAX
#define UINT8_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (uint8_t)
#endif

#ifndef UINT16_MAX
#define UINT16_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (uint16_t)
#endif

#ifndef UINT32_MAX
#define UINT32_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (uint32_t)
#endif

#ifndef UINT64_MAX
#define UINT64_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (uint64_t)
#endif

#ifndef SIZE_MAX
#define SIZE_MAX BSON_NUMERIC_LIMITS_MAX_UNSIGNED (size_t)
#endif

#ifndef PTRDIFF_MAX
#define PTRDIFF_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (ptrdiff_t)
#endif

#ifndef SCHAR_MIN
#define SCHAR_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (signed char, SCHAR_MAX)
#endif

#ifndef SHRT_MIN
#define SHRT_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (short, SHRT_MAX)
#endif

#ifndef INT_MIN
#define INT_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (int, INT_MAX)
#endif

#ifndef LONG_MIN
#define LONG_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (long, LONG_MAX)
#endif

#ifndef LLONG_MIN
#define LLONG_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (long long, LLONG_MAX)
#endif

#ifndef INT8_MIN
#define INT8_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (int8_t, INT8_MAX)
#endif

#ifndef INT16_MIN
#define INT16_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (int16_t, INT16_MAX)
#endif

#ifndef INT32_MIN
#define INT32_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (int32_t, INT32_MAX)
#endif

#ifndef INT64_MIN
#define INT64_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (int64_t, INT64_MAX)
#endif

#ifndef PTRDIFF_MIN
#define PTRDIFF_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (ptrdiff_t, PTRDIFF_MAX)
#endif

#endif /* !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L */


#ifndef SSIZE_MAX
#define SSIZE_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (ssize_t)
#endif

#ifndef SSIZE_MIN
#define SSIZE_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED (ssize_t, SSIZE_MAX)
#endif

#if defined(__MINGW32__) && !defined(INIT_ONCE_STATIC_INIT)
#define INIT_ONCE_STATIC_INIT RTL_RUN_ONCE_INIT
typedef RTL_RUN_ONCE INIT_ONCE;
Expand Down
1 change: 1 addition & 0 deletions src/libbson/src/bson/bson.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "bson-macros.h"
#include "bson-config.h"
#include "bson-atomic.h"
#include "bson-cmp.h"
#include "bson-context.h"
#include "bson-clock.h"
#include "bson-decimal128.h"
Expand Down
Loading