sha256: speed up writes using multi-block compression

Multiple 64-byte blocks can now be compressed directly
from the input buffer, without copying them into the
internal buffer.
This commit is contained in:
furszy
2026-01-14 12:16:41 -05:00
parent 0753f8b909
commit 4d92a083bc
2 changed files with 110 additions and 4 deletions

View File

@@ -143,22 +143,34 @@ static void secp256k1_hash_ctx_init(secp256k1_hash_ctx *hash_ctx) {
}
static void secp256k1_sha256_write(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *hash, const unsigned char *data, size_t len) {
size_t chunk_len;
size_t bufsize = hash->bytes & 0x3F;
hash->bytes += len;
VERIFY_CHECK(hash->bytes >= len);
VERIFY_CHECK(hash_ctx != NULL);
VERIFY_CHECK(hash_ctx->fn_sha256_compression != NULL);
while (len >= 64 - bufsize) {
/* Fill the buffer, and process it. */
size_t chunk_len = 64 - bufsize;
/* If we exceed the 64-byte block size with this input, process it and wipe the buffer */
chunk_len = 64 - bufsize;
if (bufsize && len >= chunk_len) {
memcpy(hash->buf + bufsize, data, chunk_len);
data += chunk_len;
len -= chunk_len;
hash_ctx->fn_sha256_compression(hash->s, hash->buf, 1);
bufsize = 0;
}
/* If we still have data to process, invoke compression directly on the input */
if (len >= 64) {
const size_t n_blocks = len / 64;
const size_t advance = n_blocks * 64;
hash_ctx->fn_sha256_compression(hash->s, data, n_blocks);
data += advance;
len -= advance;
}
/* Fill the buffer with what remains */
if (len) {
/* Fill the buffer with what remains. */
memcpy(hash->buf + bufsize, data, len);
}
}

View File

@@ -480,6 +480,99 @@ static void run_plug_sha256_compression_tests(void) {
secp256k1_context_destroy(ctx_cloned);
}
static void run_sha256_multi_block_compression_tests(void) {
secp256k1_hash_ctx hash_ctx;
secp256k1_sha256 sha256_one;
secp256k1_sha256 sha256_two;
unsigned char out_one[32], out_two[32];
hash_ctx.fn_sha256_compression = secp256k1_sha256_transform;
{ /* 1) Writing one 64-byte full block vs two 32-byte blocks */
const unsigned char data[64] = "totally serious test message to hash, definitely no random data";
unsigned char data32[32];
secp256k1_sha256_initialize(&sha256_one);
secp256k1_sha256_initialize(&sha256_two);
/* Write the 64-byte block */
secp256k1_sha256_write(&hash_ctx, &sha256_one, data, 64);
secp256k1_sha256_finalize(&hash_ctx, &sha256_one, out_one);
/* Write the two 32-byte blocks */
memcpy(data32, data, 32);
secp256k1_sha256_write(&hash_ctx, &sha256_two, data32, 32);
memcpy(data32, data + 32, 32);
secp256k1_sha256_write(&hash_ctx, &sha256_two, data32, 32);
secp256k1_sha256_finalize(&hash_ctx, &sha256_two, out_two);
CHECK(secp256k1_memcmp_var(out_one, out_two, 32) == 0);
}
{ /* 2) Writing one 80-byte block vs two 40-byte blocks */
const unsigned char data[80] = "Genesis: The Times 03/Jan/2009 Chancellor on brink of second bailout for banks ";
unsigned char data40[40];
secp256k1_sha256_initialize(&sha256_one);
secp256k1_sha256_initialize(&sha256_two);
/* Write the 80-byte block */
secp256k1_sha256_write(&hash_ctx, &sha256_one, data, 80);
secp256k1_sha256_finalize(&hash_ctx, &sha256_one, out_one);
/* Write the two 40-byte blocks */
memcpy(data40, data, 40);
secp256k1_sha256_write(&hash_ctx, &sha256_two, data40, 40);
memcpy(data40, data + 40, 40);
secp256k1_sha256_write(&hash_ctx, &sha256_two, data40, 40);
secp256k1_sha256_finalize(&hash_ctx, &sha256_two, out_two);
CHECK(secp256k1_memcmp_var(out_one, out_two, 32) == 0);
}
{ /* 3) Writing multiple consecutive full blocks in one write (128 bytes) */
unsigned char data[128];
unsigned char i;
for (i = 0; i < 128; i++) data[i] = i;
secp256k1_sha256_initialize(&sha256_one);
secp256k1_sha256_initialize(&sha256_two);
/* Single write of 128 bytes (two full 64-byte blocks) */
secp256k1_sha256_write(&hash_ctx, &sha256_one, data, 128);
secp256k1_sha256_finalize(&hash_ctx, &sha256_one, out_one);
/* Two separate writes of 64 bytes each */
secp256k1_sha256_write(&hash_ctx, &sha256_two, data, 64);
secp256k1_sha256_write(&hash_ctx, &sha256_two, data + 64, 64);
secp256k1_sha256_finalize(&hash_ctx, &sha256_two, out_two);
CHECK(secp256k1_memcmp_var(out_one, out_two, 32) == 0);
}
{ /* 4) Mixed small + large writes in sequence */
unsigned char data[150];
unsigned char i;
for (i = 0; i < 150; i++) data[i] = i;
secp256k1_sha256_initialize(&sha256_one);
secp256k1_sha256_initialize(&sha256_two);
/* Single write of 150 bytes */
secp256k1_sha256_write(&hash_ctx, &sha256_one, data, 150);
secp256k1_sha256_finalize(&hash_ctx, &sha256_one, out_one);
/* Split writes: 10, 64, 64, 12 bytes */
secp256k1_sha256_write(&hash_ctx, &sha256_two, data, 10);
secp256k1_sha256_write(&hash_ctx, &sha256_two, data + 10, 64);
secp256k1_sha256_write(&hash_ctx, &sha256_two, data + 74, 64);
secp256k1_sha256_write(&hash_ctx, &sha256_two, data + 138, 12);
secp256k1_sha256_finalize(&hash_ctx, &sha256_two, out_two);
CHECK(secp256k1_memcmp_var(out_one, out_two, 32) == 0);
}
}
static void run_ctz_tests(void) {
static const uint32_t b32[] = {1, 0xffffffff, 0x5e56968f, 0xe0d63129};
static const uint64_t b64[] = {1, 0xffffffffffffffff, 0xbcd02462139b3fc3, 0x98b5f80c769693ef};
@@ -7819,6 +7912,7 @@ static const struct tf_test_entry tests_general[] = {
CASE(deprecated_context_flags_test),
CASE(scratch_tests),
CASE(plug_sha256_compression_tests),
CASE(sha256_multi_block_compression_tests),
};
static const struct tf_test_entry tests_integer[] = {