123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /* A type for indices and sizes.
- Copyright (C) 2020-2024 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <https://www.gnu.org/licenses/>. */
- #ifndef _IDX_H
- #define _IDX_H
- /* Get ptrdiff_t. */
- #include <stddef.h>
- /* Get PTRDIFF_MAX. */
- #include <stdint.h>
- /* The type 'idx_t' holds an (array) index or an (object) size.
- Its implementation promotes to a signed integer type,
- which can hold the values
- 0..2^63-1 (on 64-bit platforms) or
- 0..2^31-1 (on 32-bit platforms).
- Why a signed integer type?
- * Security: Signed types can be checked for overflow via
- '-fsanitize=undefined', but unsigned types cannot.
- * Comparisons without surprises: ISO C99 § 6.3.1.8 specifies a few
- surprising results for comparisons, such as
- (int) -3 < (unsigned long) 7 => false
- (int) -3 < (unsigned int) 7 => false
- and on 32-bit machines:
- (long) -3 < (unsigned int) 7 => false
- This is surprising because the natural comparison order is by
- value in the realm of infinite-precision signed integers (ℤ).
- The best way to get rid of such surprises is to use signed types
- for numerical integer values, and use unsigned types only for
- bit masks and enums.
- Why not use 'size_t' directly?
- * Because 'size_t' is an unsigned type, and a signed type is better.
- See above.
- Why not use 'ssize_t'?
- * 'ptrdiff_t' is more portable; it is standardized by ISO C
- whereas 'ssize_t' is standardized only by POSIX.
- * 'ssize_t' is not required to be as wide as 'size_t', and some
- now-obsolete POSIX platforms had 'size_t' wider than 'ssize_t'.
- * Conversely, some now-obsolete platforms had 'ptrdiff_t' wider
- than 'size_t', which can be a win and conforms to POSIX.
- Won't this cause a problem with objects larger than PTRDIFF_MAX?
- * Typical modern or large platforms do not allocate such objects,
- so this is not much of a problem in practice; for example, you
- can safely write 'idx_t len = strlen (s);'. To port to older
- small platforms where allocations larger than PTRDIFF_MAX could
- in theory be a problem, you can use Gnulib's ialloc module, or
- functions like ximalloc in Gnulib's xalloc module.
- Why not use 'ptrdiff_t' directly?
- * Maintainability: When reading and modifying code, it helps to know that
- a certain variable cannot have negative values. For example, when you
- have a loop
- int n = ...;
- for (int i = 0; i < n; i++) ...
- or
- ptrdiff_t n = ...;
- for (ptrdiff_t i = 0; i < n; i++) ...
- you have to ask yourself "what if n < 0?". Whereas in
- idx_t n = ...;
- for (idx_t i = 0; i < n; i++) ...
- you know that this case cannot happen.
- Similarly, when a programmer writes
- idx_t = ptr2 - ptr1;
- there is an implied assertion that ptr1 and ptr2 point into the same
- object and that ptr1 <= ptr2.
- * Being future-proof: In the future, range types (integers which are
- constrained to a certain range of values) may be added to C compilers
- or to the C standard. Several programming languages (Ada, Haskell,
- Common Lisp, Pascal) already have range types. Such range types may
- help producing good code and good warnings. The type 'idx_t' could
- then be typedef'ed to a range type that is signed after promotion. */
- #ifdef __cplusplus
- extern "C" {
- #endif
- /* In the future, idx_t could be typedef'ed to a signed range type.
- The clang "extended integer types", supported in Clang 11 or newer
- <https://clang.llvm.org/docs/LanguageExtensions.html#extended-integer-types>,
- are a special case of range types. However, these types don't support binary
- operators with plain integer types (e.g. expressions such as x > 1).
- Therefore, they don't behave like signed types (and not like unsigned types
- either). So, we cannot use them here. */
- /* Use the signed type 'ptrdiff_t'. */
- /* Note: ISO C does not mandate that 'size_t' and 'ptrdiff_t' have the same
- size, but it is so on all platforms we have seen since 1990. */
- typedef ptrdiff_t idx_t;
- /* IDX_MAX is the maximum value of an idx_t. */
- #define IDX_MAX PTRDIFF_MAX
- /* So far no need has been found for an IDX_WIDTH macro.
- Perhaps there should be another macro IDX_VALUE_BITS that does not
- count the sign bit and is therefore one less than PTRDIFF_WIDTH. */
- #ifdef __cplusplus
- }
- #endif
- #endif /* _IDX_H */
|