Skip to content

util: introduce and use ARRAY_SIZE macro#1824

Merged
real-or-random merged 1 commit intobitcoin-core:masterfrom
theStack:util-add_array_size
Mar 3, 2026
Merged

util: introduce and use ARRAY_SIZE macro#1824
real-or-random merged 1 commit intobitcoin-core:masterfrom
theStack:util-add_array_size

Conversation

@theStack
Copy link
Contributor

This PR is another tiny improvement found while working on #1765, with the goal to avoid code repetition.

The ARRAY_SIZE macro definition is pretty wide-spread in C projects and e.g. matches the one used in the Linux Kernel (without the additional check to reject pointers, as we would need GNU C for that, see e.g. https://stackoverflow.com/a/19455169; not sure if a useful counterpart exists that only relies on C89). Replacement instances were identified via $ git grep sizeof.*/.*sizeof.

The macro definition matches the one used in Linux, see e.g.
https://github.com/torvalds/linux/blob/9702969978695d9a699a1f34771580cdbb153b33/include/linux/array_size.h#L11
(without the additional check rejecting pointers, as we would need
 GNU C for that, see e.g. https://stackoverflow.com/a/19455169)
@kevkevinpal
Copy link
Contributor

I think overall the ARRAY_SIZE macro makes the codebase overall cleaner.

I'm just unsure because we can pass pointers into ARRAY_SIZE and have it silently pass.

I would add a comment above the macro to say to avoid using pointers, so if someone were to use the macro, they would be aware.

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 921b971

@real-or-random
Copy link
Contributor

(without the additional check to reject pointers, as we would need GNU C for that, see e.g. stackoverflow.com/a/19455169;

GNU C is not an issue if we sandwich it in a #if SECP256K1_GNUC_PREREQ. A check that works only on GCC will be enough for us; we'll always test on GCC.

And we don't need BUILD_BUG_ON_ZERO because we have a similar STATIC_ASSERT macro. (Though I really like the implementation of BUILD_BUG_ON_ZERO and that it is an expression and not a statement; our macro is a statement that cannot be used anywhere BUILD_BUG_ON_ZERO can be used.)

@real-or-random
Copy link
Contributor

Concept ACK

@theStack
Copy link
Contributor Author

(without the additional check to reject pointers, as we would need GNU C for that, see e.g. stackoverflow.com/a/19455169;

GNU C is not an issue if we sandwich it in a #if SECP256K1_GNUC_PREREQ. A check that works only on GCC will be enough for us; we'll always test on GCC.

Ah good point, didn't consider that. I found that the needed typeof keyword is only available if the GNU extensions are explicitly enabled via e.g. -std=gnu90 (as opposed to -std=c90 what we use) though, but the alternative keyword __typeof__ seems to work.

And we don't need BUILD_BUG_ON_ZERO because we have a similar STATIC_ASSERT macro. (Though I really like the implementation of BUILD_BUG_ON_ZERO and that it is an expression and not a statement; our macro is a statement that cannot be used anywhere BUILD_BUG_ON_ZERO can be used.)

Isn't it being an expression necessary for most our use cases? We primarily take use of it in the condition of for loops (i.e. for (i = 0; i < ARRAY_SIZE(...); i++), I assume STATIC_ASSERT wouldn't work there.

Will take a deeper look soon and put the PR into draft state in any case until we figure out how to best add a "reject pointers" check.

@theStack theStack marked this pull request as draft February 17, 2026 18:34
@real-or-random
Copy link
Contributor

real-or-random commented Feb 18, 2026

Isn't it being an expression necessary for most our use cases? We primarily take use of it in the condition of for loops (i.e. for (i = 0; i < ARRAY_SIZE(...); i++), I assume STATIC_ASSERT wouldn't work there.

Good point. Then we'll need to port the macro.

Or perhaps we should just drop -std=c90, which would give us _Static_assert and make some of the C hacks obsolete (see https://github.com/torvalds/linux/blob/75a452d31ba697fc986609dd4905294e07687992/include/linux/compiler.h#L203). We could do a preprocessor check on __STDC_VERSION__ to determine if we have _Static_assert.

The reason we pass -std=c90 is to make sure that we only use C90 features. We could drop it and add a single CI job to test compilation with -std=c90 for that purpose.

@theStack
Copy link
Contributor Author

Or perhaps we should just drop -std=c90, which would give us _Static_assert and make some of the C hacks obsolete (see https://github.com/torvalds/linux/blob/75a452d31ba697fc986609dd4905294e07687992/include/linux/compiler.h#L203). We could do a preprocessor check on STDC_VERSION to determine if we have _Static_assert.

The following works in my environment with gcc 14.2.0, after bumping the standard to C11 (needed to get _Static_assert) and removing -pedantic (needed to avoid a struct has no members [-Wpedantic] warning in the BUILD_BUG_ON_ZERO macro): theStack@d02a5de

Can be tested by e.g.

--- a/src/modules/schnorrsig/bench_impl.h
+++ b/src/modules/schnorrsig/bench_impl.h
@@ -52,6 +52,7 @@ static void run_schnorrsig_bench(int iters, int argc, char** argv) {

     data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
     data.keypairs = malloc(iters * sizeof(secp256k1_keypair *));
+    i = ARRAY_SIZE(data.keypairs); /* this should fail */
     data.pk = malloc(iters * sizeof(unsigned char *));
     data.msgs = malloc(iters * sizeof(unsigned char *));
     data.sigs = malloc(iters * sizeof(unsigned char *));

It seems though that even without the must_be_array addition, the compiler is able to detect misusage of ARRAY_SIZE, and there is a dedicated warning category (-Wsizeof-pointer-div):

/home/thestack/secp256k1/src/util.h:191:39: warning: division ‘sizeof (const secp256k1_keypair **) / sizeof (const secp256k1_keypair *)’ does not compute the number of array elements [-Wsizeof-pointer-div]
  191 | # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
      |                                       ^
/home/thestack/secp256k1/src/modules/schnorrsig/bench_impl.h:55:9: note: in expansion of macro ‘ARRAY_SIZE’
   55 |     i = ARRAY_SIZE(data.keypairs); /* this should fail */
      |         ^~~~~~~~~~

@real-or-random
Copy link
Contributor

after bumping the standard to C11 (needed to get _Static_assert)

What if you don't pass -std at all?

And removing -pedantic (needed to avoid a struct has no members [-Wpedantic] warning in the BUILD_BUG_ON_ZERO macro): theStack@d02a5de

I guess you could just add a member.

@theStack
Copy link
Contributor Author

after bumping the standard to C11 (needed to get _Static_assert)

What if you don't pass -std at all?

And removing -pedantic (needed to avoid a struct has no members [-Wpedantic] warning in the BUILD_BUG_ON_ZERO macro): theStack@d02a5de

I guess you could just add a member.

@real-or-random: Good points. Removed the explicit setting of a C standard version (for some reason I assumed that setting CMAKE_C_STANDARD is mandatory in CMake, but apparently it isn't) and added a dummy member in the struct, so the -pedantic flag can be kept: theStack@62947de

I wonder if its worth it implementing this? Seems to be quite some code for something that can be detected by modern compilers anyways (though I didn't look into the details of -Wsizeof-pointer-div, maybe it doesn't catch as much).

@real-or-random
Copy link
Contributor

I wonder if its worth it implementing this? Seems to be quite some code for something that can be detected by modern compilers anyways (though I didn't look into the details of -Wsizeof-pointer-div, maybe it doesn't catch as much).

Oh, I wasn't aware of the warning at all. Seems to work: https://godbolt.org/z/vxehGPznG

Yes, then the warning is better... I mean, there could be specific circumstances under which this won't work, but this would be rather strange because the expression will always look the same if generated by the macro.

Copy link
Contributor

@real-or-random real-or-random left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 921b971

@real-or-random real-or-random marked this pull request as ready for review March 2, 2026 08:49
@real-or-random
Copy link
Contributor

@theStack Are you okay with this conclusion? And, if yes, is the PR ready from your side? (Sorry, I shouldn't have marked it ready for you.)

@theStack
Copy link
Contributor Author

theStack commented Mar 3, 2026

@theStack Are you okay with this conclusion? And, if yes, is the PR ready from your side? (Sorry, I shouldn't have marked it ready for you.)

Yes, I agree with the conclusion and think the PR is ready 👌 Thanks for verifying on godbolt! (and no worries, I think unmarking draft state was fine)

@real-or-random real-or-random merged commit b9cb1cb into bitcoin-core:master Mar 3, 2026
122 checks passed
fanquake added a commit to fanquake/bitcoin that referenced this pull request Mar 3, 2026
b9cb1cbfd7 Merge bitcoin-core/secp256k1#1824: util: introduce and use `ARRAY_SIZE` macro
c0a2aba088 Merge bitcoin-core/secp256k1#1811: bench: Update help functions in bench and bench_internal
10f546a2c0 Merge bitcoin-core/secp256k1#1832: testrand: Remove testrand_finish
8d0eda07e9 testrand: Remove testrand_finish
95e6815843 Merge bitcoin-core/secp256k1#1825: hash: remove redundant `secp256k1_sha256_initialize` in tagged hash midstate functions
f48b1bfa5d hash: add midstate initializer and use it for tagged hashes
3019186a6d Merge bitcoin-core/secp256k1#1829: ci: Fix leftover use of old ECMULTGENPRECISION
79e9f25237 ci: Fix leftover use of old ECMULTGENPRECISION
dfe042feb2 Merge bitcoin-core/secp256k1#1828: Revert "ci, docker: Fix LLVM repository signature failure"
76e92cfeea Revert "ci, docker: Fix LLVM repository signature failure"
ac561601b8 Merge bitcoin-core/secp256k1#1760: cmake: Add dynamic test discovery to improve parallelism
c7a7f732bd Merge bitcoin-core/secp256k1#1821: ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
921b9711ea util: introduce and use `ARRAY_SIZE` macro
b99a94c382 Add tests for bad scalar inputs in ellswift XDH
307b49f1b9 ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
322d0a4358 Merge bitcoin-core/secp256k1#1823: ci: Load Docker image by ID from builder step
ed02466d3f ci: Load Docker image by ID from builder step
c49c9be504 bench: Update help functions in bench and bench_internal
1d146ac3ed Merge bitcoin-core/secp256k1#1819: tests: Improve secp256k1_scalar_check_overflow tests (Issue bitcoin#1812)
f47bbc07f0 test: add unit tests for secp256k1_scalar_check_overflow
d071aa56d5 Merge bitcoin-core/secp256k1#1815: refactor: remove unnecessary `malloc` result casts
99ab4a105e Merge bitcoin-core/secp256k1#1817: ci: Disable Docker build summary generation
c5da3bde9c Merge bitcoin-core/secp256k1#1818: ci: Enforce base-10 evaluation
97de5120cf Merge bitcoin-core/secp256k1#1804: test: show both CMake and Autotools usage for ctime_tests
4fb7ccf5d4 ci: Enforce base-10 evaluation
3ae72e7867 ci: Disable Docker build summary generation
97b3c47849 refactor: remove unnecessary `malloc` result casts
1bc74a22f8 test: show both Autotools and CMake usage for ctime_tests
8354618e02 cmake: Set `LABELS` property for tests
29f26ec3cf cmake: Integrate DiscoverTests and normalize test names
f95b263f23 cmake: Add DiscoverTests module
4ac651144b cmake, refactor: Deduplicate test-related code

git-subtree-dir: src/secp256k1
git-subtree-split: b9cb1cbfd72c9391c337ed1e30d71355dde65248
fanquake added a commit to fanquake/bitcoin that referenced this pull request Mar 4, 2026
1aafe15139 Merge bitcoin-core/secp256k1#1777: Make SHA256 compression runtime pluggable
b9cb1cbfd7 Merge bitcoin-core/secp256k1#1824: util: introduce and use `ARRAY_SIZE` macro
4d92a083bc sha256: speed up writes using multi-block compression
0753f8b909 Add API to override SHA256 compression at runtime
fdb6a91a5e Introduce hash context to support pluggable SHA256 compression
c0a2aba088 Merge bitcoin-core/secp256k1#1811: bench: Update help functions in bench and bench_internal
10f546a2c0 Merge bitcoin-core/secp256k1#1832: testrand: Remove testrand_finish
8d0eda07e9 testrand: Remove testrand_finish
95e6815843 Merge bitcoin-core/secp256k1#1825: hash: remove redundant `secp256k1_sha256_initialize` in tagged hash midstate functions
f48b1bfa5d hash: add midstate initializer and use it for tagged hashes
3019186a6d Merge bitcoin-core/secp256k1#1829: ci: Fix leftover use of old ECMULTGENPRECISION
79e9f25237 ci: Fix leftover use of old ECMULTGENPRECISION
dfe042feb2 Merge bitcoin-core/secp256k1#1828: Revert "ci, docker: Fix LLVM repository signature failure"
76e92cfeea Revert "ci, docker: Fix LLVM repository signature failure"
ac561601b8 Merge bitcoin-core/secp256k1#1760: cmake: Add dynamic test discovery to improve parallelism
c7a7f732bd Merge bitcoin-core/secp256k1#1821: ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
921b9711ea util: introduce and use `ARRAY_SIZE` macro
b99a94c382 Add tests for bad scalar inputs in ellswift XDH
307b49f1b9 ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
322d0a4358 Merge bitcoin-core/secp256k1#1823: ci: Load Docker image by ID from builder step
ed02466d3f ci: Load Docker image by ID from builder step
c49c9be504 bench: Update help functions in bench and bench_internal
1d146ac3ed Merge bitcoin-core/secp256k1#1819: tests: Improve secp256k1_scalar_check_overflow tests (Issue bitcoin#1812)
f47bbc07f0 test: add unit tests for secp256k1_scalar_check_overflow
d071aa56d5 Merge bitcoin-core/secp256k1#1815: refactor: remove unnecessary `malloc` result casts
99ab4a105e Merge bitcoin-core/secp256k1#1817: ci: Disable Docker build summary generation
c5da3bde9c Merge bitcoin-core/secp256k1#1818: ci: Enforce base-10 evaluation
97de5120cf Merge bitcoin-core/secp256k1#1804: test: show both CMake and Autotools usage for ctime_tests
4fb7ccf5d4 ci: Enforce base-10 evaluation
3ae72e7867 ci: Disable Docker build summary generation
97b3c47849 refactor: remove unnecessary `malloc` result casts
1bc74a22f8 test: show both Autotools and CMake usage for ctime_tests
8354618e02 cmake: Set `LABELS` property for tests
29f26ec3cf cmake: Integrate DiscoverTests and normalize test names
f95b263f23 cmake: Add DiscoverTests module
4ac651144b cmake, refactor: Deduplicate test-related code

git-subtree-dir: src/secp256k1
git-subtree-split: 1aafe15139976b0142d791aaf4963de3fc1ff736
real-or-random added a commit to BlockstreamResearch/secp256k1-zkp that referenced this pull request Mar 5, 2026
656c7cc sync-upstream: Clarify that we merge a *single* upstream ref (Tim Ruffing)
349a94b sync-upstream: Remove "select" mode and simplify (Tim Ruffing)

Pull request description:

  Please see individual commit messages for details.

  ---

  Here's an example session that shows why `select` doesn't make sense:

  ```
  ### Let's find some PRs to sync (master is at 5a67b63)

  ❯ ./contrib/sync-upstream.sh -b master range
  Merging b9cb1cb 1aafe15 . Continue with y
  n

  ### Let's look at them in the order they have been merged

  ❯ git show b9cb1cb
  commit b9cb1cb
  Merge: c0a2aba 921b971
  Author: merge-script <me@real-or-random.org>
  Date:   Tue Mar 3 15:31:46 2026 +0100

      Merge bitcoin-core/secp256k1#1824: util: introduce and use `ARRAY_SIZE` macro

  [...]

  ❯ git --no-pager show 1aafe15

  commit 1aafe15 (upstream/master, upstream/HEAD)
  Merge: b9cb1cb 4d92a08
  Author: merge-script <me@real-or-random.org>
  Date:   Wed Mar 4 08:43:07 2026 +0100

      Merge bitcoin-core/secp256k1#1777: Make SHA256 compression runtime pluggable

  [...]

  ### Let's assume I want to cherry-pick 1aafe15 but not sync b9cb1bcf

  ❯ ./contrib/sync-upstream.sh -b master select 1aafe15
  -----------------------------------
  Upstream PRs 1777
  -----------------------------------

  [bitcoin-core/secp256k1#1777]: Make SHA256 compression runtime pluggable

  This PR can be recreated with `./contrib/sync-upstream.sh -b master select 1aafe15`.

  [...]

  ### Let's check if it's really only PR #1777

  ❯ git diff | grep ARRAY_SIZE
  + #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

  ### Ah damn, we also got the other PR #1824...
  ```

ACKs for top commit:
  mllwchrry:
    ACK 656c7cc
  jonasnick:
    ACK 656c7cc

Tree-SHA512: 27cdfe7c6decce840ef4f2d12fa0a5fd829b35ac24403272c4b34c0a3b0da2303cee36bcb419d0608c6b1297fd5bf8a6cd6d41672850ac092814c140638f9d83
fanquake added a commit to fanquake/bitcoin that referenced this pull request Mar 10, 2026
1aafe15139 Merge bitcoin-core/secp256k1#1777: Make SHA256 compression runtime pluggable
b9cb1cbfd7 Merge bitcoin-core/secp256k1#1824: util: introduce and use `ARRAY_SIZE` macro
4d92a083bc sha256: speed up writes using multi-block compression
0753f8b909 Add API to override SHA256 compression at runtime
fdb6a91a5e Introduce hash context to support pluggable SHA256 compression
c0a2aba088 Merge bitcoin-core/secp256k1#1811: bench: Update help functions in bench and bench_internal
10f546a2c0 Merge bitcoin-core/secp256k1#1832: testrand: Remove testrand_finish
8d0eda07e9 testrand: Remove testrand_finish
95e6815843 Merge bitcoin-core/secp256k1#1825: hash: remove redundant `secp256k1_sha256_initialize` in tagged hash midstate functions
f48b1bfa5d hash: add midstate initializer and use it for tagged hashes
3019186a6d Merge bitcoin-core/secp256k1#1829: ci: Fix leftover use of old ECMULTGENPRECISION
79e9f25237 ci: Fix leftover use of old ECMULTGENPRECISION
dfe042feb2 Merge bitcoin-core/secp256k1#1828: Revert "ci, docker: Fix LLVM repository signature failure"
76e92cfeea Revert "ci, docker: Fix LLVM repository signature failure"
ac561601b8 Merge bitcoin-core/secp256k1#1760: cmake: Add dynamic test discovery to improve parallelism
c7a7f732bd Merge bitcoin-core/secp256k1#1821: ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
921b9711ea util: introduce and use `ARRAY_SIZE` macro
b99a94c382 Add tests for bad scalar inputs in ellswift XDH
307b49f1b9 ellswift: fix overflow flag handling in secp256k1_ellswift_xdh
322d0a4358 Merge bitcoin-core/secp256k1#1823: ci: Load Docker image by ID from builder step
ed02466d3f ci: Load Docker image by ID from builder step
c49c9be504 bench: Update help functions in bench and bench_internal
1d146ac3ed Merge bitcoin-core/secp256k1#1819: tests: Improve secp256k1_scalar_check_overflow tests (Issue bitcoin#1812)
f47bbc07f0 test: add unit tests for secp256k1_scalar_check_overflow
d071aa56d5 Merge bitcoin-core/secp256k1#1815: refactor: remove unnecessary `malloc` result casts
99ab4a105e Merge bitcoin-core/secp256k1#1817: ci: Disable Docker build summary generation
c5da3bde9c Merge bitcoin-core/secp256k1#1818: ci: Enforce base-10 evaluation
97de5120cf Merge bitcoin-core/secp256k1#1804: test: show both CMake and Autotools usage for ctime_tests
4fb7ccf5d4 ci: Enforce base-10 evaluation
3ae72e7867 ci: Disable Docker build summary generation
97b3c47849 refactor: remove unnecessary `malloc` result casts
1bc74a22f8 test: show both Autotools and CMake usage for ctime_tests
8354618e02 cmake: Set `LABELS` property for tests
29f26ec3cf cmake: Integrate DiscoverTests and normalize test names
f95b263f23 cmake: Add DiscoverTests module
4ac651144b cmake, refactor: Deduplicate test-related code

git-subtree-dir: src/secp256k1
git-subtree-split: 1aafe15139976b0142d791aaf4963de3fc1ff736
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants