]>
Commit | Line | Data |
---|---|---|
9c9d63b1 | 1 | /* A type for indices and sizes. |
dc6c21da | 2 | Copyright (C) 2020-2022 Free Software Foundation, Inc. |
9c9d63b1 PM |
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 | |
dc6c21da | 6 | modify it under the terms of the GNU Lesser General Public |
9c9d63b1 | 7 | License as published by the Free Software Foundation; either |
dc6c21da | 8 | version 2.1 of the License, or (at your option) any later version. |
9c9d63b1 PM |
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 | |
dc6c21da | 13 | Lesser General Public License for more details. |
9c9d63b1 | 14 | |
dc6c21da | 15 | You should have received a copy of the GNU Lesser General Public |
9c9d63b1 PM |
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 | ||
dc6c21da TT |
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 | ||
9c9d63b1 PM |
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 */ |