1 /* A type for indices and sizes. 2 Copyright (C) 2020-2023 Free Software Foundation, Inc. 3 This file is part of the GNU C Library. 4 5 The GNU C Library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 The GNU C Library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with the GNU C Library; if not, see 17 <https://www.gnu.org/licenses/>. */ 18 19 #ifndef _IDX_H 20 #define _IDX_H 21 22 /* Get ptrdiff_t. */ 23 #include <stddef.h> 24 25 /* Get PTRDIFF_MAX. */ 26 #include <stdint.h> 27 28 /* The type 'idx_t' holds an (array) index or an (object) size. 29 Its implementation promotes to a signed integer type, 30 which can hold the values 31 0..2^63-1 (on 64-bit platforms) or 32 0..2^31-1 (on 32-bit platforms). 33 34 Why a signed integer type? 35 36 * Security: Signed types can be checked for overflow via 37 '-fsanitize=undefined', but unsigned types cannot. 38 39 * Comparisons without surprises: ISO C99 § 6.3.1.8 specifies a few 40 surprising results for comparisons, such as 41 42 (int) -3 < (unsigned long) 7 => false 43 (int) -3 < (unsigned int) 7 => false 44 and on 32-bit machines: 45 (long) -3 < (unsigned int) 7 => false 46 47 This is surprising because the natural comparison order is by 48 value in the realm of infinite-precision signed integers (ℤ). 49 50 The best way to get rid of such surprises is to use signed types 51 for numerical integer values, and use unsigned types only for 52 bit masks and enums. 53 54 Why not use 'size_t' directly? 55 56 * Because 'size_t' is an unsigned type, and a signed type is better. 57 See above. 58 59 Why not use 'ssize_t'? 60 61 * 'ptrdiff_t' is more portable; it is standardized by ISO C 62 whereas 'ssize_t' is standardized only by POSIX. 63 64 * 'ssize_t' is not required to be as wide as 'size_t', and some 65 now-obsolete POSIX platforms had 'size_t' wider than 'ssize_t'. 66 67 * Conversely, some now-obsolete platforms had 'ptrdiff_t' wider 68 than 'size_t', which can be a win and conforms to POSIX. 69 70 Won't this cause a problem with objects larger than PTRDIFF_MAX? 71 72 * Typical modern or large platforms do not allocate such objects, 73 so this is not much of a problem in practice; for example, you 74 can safely write 'idx_t len = strlen (s);'. To port to older 75 small platforms where allocations larger than PTRDIFF_MAX could 76 in theory be a problem, you can use Gnulib's ialloc module, or 77 functions like ximalloc in Gnulib's xalloc module. 78 79 Why not use 'ptrdiff_t' directly? 80 81 * Maintainability: When reading and modifying code, it helps to know that 82 a certain variable cannot have negative values. For example, when you 83 have a loop 84 85 int n = ...; 86 for (int i = 0; i < n; i++) ... 87 88 or 89 90 ptrdiff_t n = ...; 91 for (ptrdiff_t i = 0; i < n; i++) ... 92 93 you have to ask yourself "what if n < 0?". Whereas in 94 95 idx_t n = ...; 96 for (idx_t i = 0; i < n; i++) ... 97 98 you know that this case cannot happen. 99 100 Similarly, when a programmer writes 101 102 idx_t = ptr2 - ptr1; 103 104 there is an implied assertion that ptr1 and ptr2 point into the same 105 object and that ptr1 <= ptr2. 106 107 * Being future-proof: In the future, range types (integers which are 108 constrained to a certain range of values) may be added to C compilers 109 or to the C standard. Several programming languages (Ada, Haskell, 110 Common Lisp, Pascal) already have range types. Such range types may 111 help producing good code and good warnings. The type 'idx_t' could 112 then be typedef'ed to a range type that is signed after promotion. */ 113 114 /* In the future, idx_t could be typedef'ed to a signed range type. 115 The clang "extended integer types", supported in Clang 11 or newer 116 <https://clang.llvm.org/docs/LanguageExtensions.html#extended-integer-types>, 117 are a special case of range types. However, these types don't support binary 118 operators with plain integer types (e.g. expressions such as x > 1). 119 Therefore, they don't behave like signed types (and not like unsigned types 120 either). So, we cannot use them here. */ 121 122 /* Use the signed type 'ptrdiff_t'. */ 123 /* Note: ISO C does not mandate that 'size_t' and 'ptrdiff_t' have the same 124 size, but it is so on all platforms we have seen since 1990. */ 125 typedef ptrdiff_t idx_t; 126 127 /* IDX_MAX is the maximum value of an idx_t. */ 128 #define IDX_MAX PTRDIFF_MAX 129 130 /* So far no need has been found for an IDX_WIDTH macro. 131 Perhaps there should be another macro IDX_VALUE_BITS that does not 132 count the sign bit and is therefore one less than PTRDIFF_WIDTH. */ 133 134 #endif /* _IDX_H */