Skip to content

Commit 1c007a3

Browse files
committed
Add safe integral comparison functions
1 parent 7d32b8e commit 1c007a3

File tree

7 files changed

+512
-0
lines changed

7 files changed

+512
-0
lines changed

src/libbson/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ set (HEADERS
181181
${PROJECT_SOURCE_DIR}/src/bson/bcon.h
182182
${PROJECT_SOURCE_DIR}/src/bson/bson-atomic.h
183183
${PROJECT_SOURCE_DIR}/src/bson/bson-clock.h
184+
${PROJECT_SOURCE_DIR}/src/bson/bson-cmp.h
184185
${PROJECT_SOURCE_DIR}/src/bson/bson-compat.h
185186
${PROJECT_SOURCE_DIR}/src/bson/bson-context.h
186187
${PROJECT_SOURCE_DIR}/src/bson/bson-decimal128.h

src/libbson/src/bson/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ set (src_libbson_src_bson_DIST_hs
1616
bson-memory.h
1717
bson-oid.h
1818
bson-reader.h
19+
bson-cmp.h
1920
bson-string.h
2021
bson-types.h
2122
bson-utf8.h

src/libbson/src/bson/bson-cmp.h

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
* Copyright 2022 MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "bson-prelude.h"
18+
19+
20+
#ifndef BSON_CMP_H
21+
#define BSON_CMP_H
22+
23+
24+
#include "bson-compat.h" /* ssize_t */
25+
#include "bson-macros.h" /* BSON_CONCAT */
26+
27+
#include <limits.h>
28+
#include <stdbool.h>
29+
#include <stdint.h>
30+
31+
32+
BSON_BEGIN_DECLS
33+
34+
35+
/* Based on the "Safe Integral Comparisons" proposal merged in C++20:
36+
* http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0586r2.html
37+
*
38+
* Due to lack of type deduction in C, relational comparison functions (e.g.
39+
* `cmp_less`) are defined in sets of four "functions" according to the
40+
* signedness of each value argument, e.g.:
41+
* - bson_cmp_less_ss (signed-value, signed-value)
42+
* - bson_cmp_less_uu (unsigned-value, unsigned-value)
43+
* - bson_cmp_less_su (signed-value, unsigned-value)
44+
* - bson_cmp_less_us (unsigned-value, signed-value)
45+
*
46+
* Similarly, the `in_range` function is defined as a set of two "functions"
47+
* according to the signedness of the value argument:
48+
* - bson_in_range_signed (Type, signed-value)
49+
* - bson_in_range_unsigned (Type, unsigned-value)
50+
*
51+
* The user must take care to use the correct signedness for the provided
52+
* argument(s). Enabling compiler warnings for implicit sign conversions is
53+
* recommended.
54+
*/
55+
56+
57+
#define BSON_CMP_SET(op, ss, uu, su, us) \
58+
static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _ss) (int64_t t, \
59+
int64_t u) \
60+
{ \
61+
return (ss); \
62+
} \
63+
\
64+
static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _uu) (uint64_t t, \
65+
uint64_t u) \
66+
{ \
67+
return (uu); \
68+
} \
69+
\
70+
static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _su) (int64_t t, \
71+
uint64_t u) \
72+
{ \
73+
return (su); \
74+
} \
75+
\
76+
static BSON_INLINE bool BSON_CONCAT3 (bson_cmp_, op, _us) (uint64_t t, \
77+
int64_t u) \
78+
{ \
79+
return (us); \
80+
}
81+
82+
BSON_CMP_SET (equal,
83+
t == u,
84+
t == u,
85+
t < 0 ? false : (uint64_t) (t) == u,
86+
u < 0 ? false : t == (uint64_t) (u))
87+
88+
BSON_CMP_SET (not_equal,
89+
!bson_cmp_equal_ss (t, u),
90+
!bson_cmp_equal_uu (t, u),
91+
!bson_cmp_equal_su (t, u),
92+
!bson_cmp_equal_us (t, u))
93+
94+
BSON_CMP_SET (less,
95+
t < u,
96+
t < u,
97+
t < 0 ? true : (uint64_t) (t) < u,
98+
u < 0 ? false : t < (uint64_t) (u))
99+
100+
BSON_CMP_SET (greater,
101+
bson_cmp_less_ss (u, t),
102+
bson_cmp_less_uu (u, t),
103+
bson_cmp_less_us (u, t),
104+
bson_cmp_less_su (u, t))
105+
106+
BSON_CMP_SET (less_equal,
107+
!bson_cmp_greater_ss (t, u),
108+
!bson_cmp_greater_uu (t, u),
109+
!bson_cmp_greater_su (t, u),
110+
!bson_cmp_greater_us (t, u))
111+
112+
BSON_CMP_SET (greater_equal,
113+
!bson_cmp_less_ss (t, u),
114+
!bson_cmp_less_uu (t, u),
115+
!bson_cmp_less_su (t, u),
116+
!bson_cmp_less_us (t, u))
117+
118+
#undef BSON_CMP_SET
119+
120+
121+
/* Define in_range functions for *signed* type Type. */
122+
#define BSON_IN_RANGE_SET_SIGNED(Type, min, max) \
123+
static BSON_INLINE bool BSON_CONCAT3 (bson_in_range_, Type, _signed) ( \
124+
int64_t value) \
125+
{ \
126+
return bson_cmp_greater_equal_ss (value, min) && \
127+
bson_cmp_less_equal_ss (value, max); \
128+
} \
129+
\
130+
static BSON_INLINE bool BSON_CONCAT3 (bson_in_range_, Type, _unsigned) ( \
131+
uint64_t value) \
132+
{ \
133+
return bson_cmp_greater_equal_us (value, min) && \
134+
bson_cmp_less_equal_us (value, max); \
135+
}
136+
137+
/* Define in_range functions for *unsigned* type Type. */
138+
#define BSON_IN_RANGE_SET_UNSIGNED(Type, max) \
139+
static BSON_INLINE bool BSON_CONCAT3 (bson_in_range_, Type, _signed) ( \
140+
int64_t value) \
141+
{ \
142+
return bson_cmp_greater_equal_su (value, 0u) && \
143+
bson_cmp_less_equal_su (value, max); \
144+
} \
145+
\
146+
static BSON_INLINE bool BSON_CONCAT3 (bson_in_range_, Type, _unsigned) ( \
147+
uint64_t value) \
148+
{ \
149+
return bson_cmp_greater_equal_uu (value, 0u) && \
150+
bson_cmp_less_equal_uu (value, max); \
151+
}
152+
153+
BSON_IN_RANGE_SET_SIGNED (signed_char, SCHAR_MIN, SCHAR_MAX)
154+
BSON_IN_RANGE_SET_SIGNED (short, SHRT_MIN, SHRT_MAX)
155+
BSON_IN_RANGE_SET_SIGNED (int, INT_MIN, INT_MAX)
156+
BSON_IN_RANGE_SET_SIGNED (long, LONG_MIN, LONG_MAX)
157+
BSON_IN_RANGE_SET_SIGNED (long_long, LLONG_MIN, LLONG_MAX)
158+
159+
BSON_IN_RANGE_SET_UNSIGNED (unsigned_char, UCHAR_MAX)
160+
BSON_IN_RANGE_SET_UNSIGNED (unsigned_short, USHRT_MAX)
161+
BSON_IN_RANGE_SET_UNSIGNED (unsigned_int, UINT_MAX)
162+
BSON_IN_RANGE_SET_UNSIGNED (unsigned_long, ULONG_MAX)
163+
BSON_IN_RANGE_SET_UNSIGNED (unsigned_long_long, ULLONG_MAX)
164+
165+
BSON_IN_RANGE_SET_SIGNED (int8_t, INT8_MIN, INT8_MAX)
166+
BSON_IN_RANGE_SET_SIGNED (int16_t, INT16_MIN, INT16_MAX)
167+
BSON_IN_RANGE_SET_SIGNED (int32_t, INT32_MIN, INT32_MAX)
168+
BSON_IN_RANGE_SET_SIGNED (int64_t, INT64_MIN, INT64_MAX)
169+
170+
BSON_IN_RANGE_SET_UNSIGNED (uint8_t, UINT8_MAX)
171+
BSON_IN_RANGE_SET_UNSIGNED (uint16_t, UINT16_MAX)
172+
BSON_IN_RANGE_SET_UNSIGNED (uint32_t, UINT32_MAX)
173+
BSON_IN_RANGE_SET_UNSIGNED (uint64_t, UINT64_MAX)
174+
175+
BSON_IN_RANGE_SET_SIGNED (ssize_t, SSIZE_MIN, SSIZE_MAX)
176+
BSON_IN_RANGE_SET_UNSIGNED (size_t, SIZE_MAX)
177+
178+
#undef BSON_IN_RANGE_SET_SIGNED
179+
#undef BSON_IN_RANGE_SET_UNSIGNED
180+
181+
182+
/* Return true if the value with *signed* type is in the representable range of
183+
* Type and false otherwise. */
184+
#define bson_in_range_signed(Type, value) \
185+
BSON_CONCAT3 (bson_in_range_, Type, _signed) (value)
186+
187+
/* Return true if the value with *unsigned* type is in the representable range
188+
* of Type and false otherwise. */
189+
#define bson_in_range_unsigned(Type, value) \
190+
BSON_CONCAT3 (bson_in_range_, Type, _unsigned) (value)
191+
192+
193+
BSON_END_DECLS
194+
195+
196+
#endif /* BSON_CMP_H */

src/libbson/src/bson/bson.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "bson-macros.h"
2929
#include "bson-config.h"
3030
#include "bson-atomic.h"
31+
#include "bson-cmp.h"
3132
#include "bson-context.h"
3233
#include "bson-clock.h"
3334
#include "bson-decimal128.h"

0 commit comments

Comments
 (0)