{"thread":{"id":"62878","subject":"[PATCH 0/4] hash: introduce generic wrappers to update hash contexts","startedAt":"2025-01-31T12:55:42Z","lastAt":"2025-02-10T22:55:12Z","messageCount":8,"participants":["Patrick Steinhardt","Junio C Hamano","Taylor Blau"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"511551","messageId":"20250131-b4-pks-hash-context-direct-v1-0-67a6d3f49d6e@pks.im","threadId":"62878","inReplyTo":null,"subject":"[PATCH 0/4] hash: introduce generic wrappers to update hash contexts","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-01-31T12:55:27Z","receivedAt":"2025-01-31T12:55:42Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis patch series introduces a couple of generic wrappers to update hash\ncontexts. Instead of updating contexts via function pointers provided by\nthe hash algorithm, we now remember the hash algorithm in the context\nitself. As a result, subsequent calls that update the hash don't need to\nremember which algorithm they used:\n\n    ```\n    struct git_hash_ctx ctx;\n    struct object_id oid;\n\n    git_hash_sha1_init(&ctx);\n    git_hash_update(&ctx, data);\n    git_hash_final_oid(&oid, &ctx);\n    ```\n\nThis was discussed in [1] and [2].\n\nThe series is built on top of master at 3b0d05c4a7 (The fifth batch,\n2025-01-29) with tb/unsafe-hashtcleanup at 04292c3796 (hash.h: drop\nunsafe_ function variants, 2025-01-23) merged into it.\n\nThanks!\n\nPatrick\n\n[1]: <Z3fhK1ACzJfVehM2@pks.im>\n[2]: <Z4jyZCAwqOjZ-u2U@pks.im>\n\n---\nPatrick Steinhardt (4):\n      hash: convert hashing context to a structure\n      hash: stop typedeffing the hash context\n      hash: provide generic wrappers to update hash contexts\n      global: adapt callers to use generic hash context helpers\n\n builtin/fast-import.c      |  16 +++---\n builtin/index-pack.c       |  19 ++++---\n builtin/patch-id.c         |  14 ++---\n builtin/receive-pack.c     |  18 +++----\n builtin/unpack-objects.c   |  13 +++--\n bulk-checkin.c             |  10 ++--\n csum-file.c                |  16 +++---\n csum-file.h                |   4 +-\n diff.c                     |  34 ++++++------\n diff.h                     |   2 +-\n hash.h                     |  43 +++++++++++----\n http-push.c                |   6 +--\n http.c                     |   6 +--\n http.h                     |   2 +-\n object-file.c              | 130 +++++++++++++++++++++++----------------------\n pack-check.c               |   6 +--\n pack-write.c               |  16 +++---\n read-cache.c               |  26 ++++-----\n rerere.c                   |  18 +++----\n t/helper/test-hash-speed.c |   8 +--\n t/helper/test-hash.c       |   6 +--\n t/unit-tests/u-hash.c      |   6 +--\n trace2/tr2_sid.c           |   6 +--\n 23 files changed, 224 insertions(+), 201 deletions(-)\n\n\n---\nbase-commit: 16b8e620c866ddbff7e33cc47b9772541285da7f\nchange-id: 20250131-b4-pks-hash-context-direct-aa7acfda3abc\n\n"},{"id":"511552","messageId":"20250131-b4-pks-hash-context-direct-v1-1-67a6d3f49d6e@pks.im","threadId":"62878","inReplyTo":"20250131-b4-pks-hash-context-direct-v1-0-67a6d3f49d6e@pks.im","subject":"[PATCH 1/4] hash: convert hashing context to a structure","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-01-31T12:55:28Z","receivedAt":"2025-01-31T12:55:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The `git_hash_context` is a union containing the different hash-specific\nstates for SHA1, its unsafe variant as well as SHA256. We know that only\none of these states will ever be in use at the same time because hash\ncontexts cannot be used for multiple different hashes at the same point\nin time.\n\nWe're about to extend the structure though to keep track of the hash\nalgorithm used to initialize the context, which is impossible to do\nwhile the context is a union. Refactor it to instead be a structure that\ncontains the union of context states.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n hash.h        | 13 +++++++------\n object-file.c | 30 +++++++++++++++---------------\n 2 files changed, 22 insertions(+), 21 deletions(-)\n\ndiff --git a/hash.h b/hash.h\nindex ad2c919991..5b88d9b714 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -234,13 +234,14 @@ enum get_oid_result {\n #endif\n \n /* A suitably aligned type for stack allocations of hash contexts. */\n-union git_hash_ctx {\n-\tgit_SHA_CTX sha1;\n-\tgit_SHA_CTX_unsafe sha1_unsafe;\n-\n-\tgit_SHA256_CTX sha256;\n+struct git_hash_ctx {\n+\tunion {\n+\t\tgit_SHA_CTX sha1;\n+\t\tgit_SHA_CTX_unsafe sha1_unsafe;\n+\t\tgit_SHA256_CTX sha256;\n+\t} state;\n };\n-typedef union git_hash_ctx git_hash_ctx;\n+typedef struct git_hash_ctx git_hash_ctx;\n \n typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);\n typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);\ndiff --git a/object-file.c b/object-file.c\nindex 6ce1caacae..7505aa6b60 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -88,82 +88,82 @@ static const struct object_id null_oid_sha256 = {\n \n static void git_hash_sha1_init(git_hash_ctx *ctx)\n {\n-\tgit_SHA1_Init(&ctx->sha1);\n+\tgit_SHA1_Init(&ctx->state.sha1);\n }\n \n static void git_hash_sha1_clone(git_hash_ctx *dst, const git_hash_ctx *src)\n {\n-\tgit_SHA1_Clone(&dst->sha1, &src->sha1);\n+\tgit_SHA1_Clone(&dst->state.sha1, &src->state.sha1);\n }\n \n static void git_hash_sha1_update(git_hash_ctx *ctx, const void *data, size_t len)\n {\n-\tgit_SHA1_Update(&ctx->sha1, data, len);\n+\tgit_SHA1_Update(&ctx->state.sha1, data, len);\n }\n \n static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx)\n {\n-\tgit_SHA1_Final(hash, &ctx->sha1);\n+\tgit_SHA1_Final(hash, &ctx->state.sha1);\n }\n \n static void git_hash_sha1_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n {\n-\tgit_SHA1_Final(oid->hash, &ctx->sha1);\n+\tgit_SHA1_Final(oid->hash, &ctx->state.sha1);\n \tmemset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);\n \toid->algo = GIT_HASH_SHA1;\n }\n \n static void git_hash_sha1_init_unsafe(git_hash_ctx *ctx)\n {\n-\tgit_SHA1_Init_unsafe(&ctx->sha1_unsafe);\n+\tgit_SHA1_Init_unsafe(&ctx->state.sha1_unsafe);\n }\n \n static void git_hash_sha1_clone_unsafe(git_hash_ctx *dst, const git_hash_ctx *src)\n {\n-\tgit_SHA1_Clone_unsafe(&dst->sha1_unsafe, &src->sha1_unsafe);\n+\tgit_SHA1_Clone_unsafe(&dst->state.sha1_unsafe, &src->state.sha1_unsafe);\n }\n \n static void git_hash_sha1_update_unsafe(git_hash_ctx *ctx, const void *data,\n \t\t\t\t      size_t len)\n {\n-\tgit_SHA1_Update_unsafe(&ctx->sha1_unsafe, data, len);\n+\tgit_SHA1_Update_unsafe(&ctx->state.sha1_unsafe, data, len);\n }\n \n static void git_hash_sha1_final_unsafe(unsigned char *hash, git_hash_ctx *ctx)\n {\n-\tgit_SHA1_Final_unsafe(hash, &ctx->sha1_unsafe);\n+\tgit_SHA1_Final_unsafe(hash, &ctx->state.sha1_unsafe);\n }\n \n static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, git_hash_ctx *ctx)\n {\n-\tgit_SHA1_Final_unsafe(oid->hash, &ctx->sha1_unsafe);\n+\tgit_SHA1_Final_unsafe(oid->hash, &ctx->state.sha1_unsafe);\n \tmemset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);\n \toid->algo = GIT_HASH_SHA1;\n }\n \n static void git_hash_sha256_init(git_hash_ctx *ctx)\n {\n-\tgit_SHA256_Init(&ctx->sha256);\n+\tgit_SHA256_Init(&ctx->state.sha256);\n }\n \n static void git_hash_sha256_clone(git_hash_ctx *dst, const git_hash_ctx *src)\n {\n-\tgit_SHA256_Clone(&dst->sha256, &src->sha256);\n+\tgit_SHA256_Clone(&dst->state.sha256, &src->state.sha256);\n }\n \n static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len)\n {\n-\tgit_SHA256_Update(&ctx->sha256, data, len);\n+\tgit_SHA256_Update(&ctx->state.sha256, data, len);\n }\n \n static void git_hash_sha256_final(unsigned char *hash, git_hash_ctx *ctx)\n {\n-\tgit_SHA256_Final(hash, &ctx->sha256);\n+\tgit_SHA256_Final(hash, &ctx->state.sha256);\n }\n \n static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n {\n-\tgit_SHA256_Final(oid->hash, &ctx->sha256);\n+\tgit_SHA256_Final(oid->hash, &ctx->state.sha256);\n \t/*\n \t * This currently does nothing, so the compiler should optimize it out,\n \t * but keep it in case we extend the hash size again.\n\n-- \n2.48.1.502.g6dc24dfdaf.dirty\n\n"},{"id":"511553","messageId":"20250131-b4-pks-hash-context-direct-v1-2-67a6d3f49d6e@pks.im","threadId":"62878","inReplyTo":"20250131-b4-pks-hash-context-direct-v1-0-67a6d3f49d6e@pks.im","subject":"[PATCH 2/4] hash: stop typedeffing the hash context","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-01-31T12:55:29Z","receivedAt":"2025-01-31T12:55:44Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"We generally avoid using `typedef` in the Git codebase. One exception\nthough is the `git_hash_ctx`, likely because it used to be a union\nrather than a struct until the preceding commit refactored it. But now\nthat it is a normal `struct` there isn't really a need for a typedef\nanymore.\n\nDrop the typedef and adapt all callers accordingly.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fast-import.c      |  4 +--\n builtin/index-pack.c       |  6 ++---\n builtin/patch-id.c         |  2 +-\n builtin/receive-pack.c     |  2 +-\n builtin/unpack-objects.c   |  4 +--\n bulk-checkin.c             |  4 +--\n csum-file.c                |  2 +-\n csum-file.h                |  4 +--\n diff.c                     | 10 ++++----\n diff.h                     |  2 +-\n hash.h                     | 11 ++++----\n http-push.c                |  2 +-\n http.h                     |  2 +-\n object-file.c              | 62 +++++++++++++++++++++++-----------------------\n pack-check.c               |  2 +-\n pack-write.c               |  2 +-\n read-cache.c               | 12 ++++-----\n rerere.c                   |  4 +--\n t/helper/test-hash-speed.c |  4 +--\n t/helper/test-hash.c       |  2 +-\n t/unit-tests/u-hash.c      |  2 +-\n trace2/tr2_sid.c           |  2 +-\n 22 files changed, 73 insertions(+), 74 deletions(-)\n\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex ccdada1810..9862704c62 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -953,7 +953,7 @@ static int store_object(\n \tunsigned char hdr[96];\n \tstruct object_id oid;\n \tunsigned long hdrlen, deltalen;\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \tgit_zstream s;\n \n \thdrlen = format_object_header((char *)hdr, sizeof(hdr), type,\n@@ -1095,7 +1095,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)\n \tstruct object_id oid;\n \tunsigned long hdrlen;\n \toff_t offset;\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \tgit_zstream s;\n \tstruct hashfile_checkpoint checkpoint;\n \tint status = Z_OK;\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 6ffbb7ce35..40e49868b1 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -151,7 +151,7 @@ static unsigned int input_offset, input_len;\n static off_t consumed_bytes;\n static off_t max_input_size;\n static unsigned deepest_delta;\n-static git_hash_ctx input_ctx;\n+static struct git_hash_ctx input_ctx;\n static uint32_t input_crc32;\n static int input_fd, output_fd;\n static const char *curr_pack;\n@@ -475,7 +475,7 @@ static void *unpack_entry_data(off_t offset, unsigned long size,\n \tint status;\n \tgit_zstream stream;\n \tvoid *buf;\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \tchar hdr[32];\n \tint hdrlen;\n \n@@ -1248,7 +1248,7 @@ static void parse_pack_objects(unsigned char *hash)\n \tstruct ofs_delta_entry *ofs_delta = ofs_deltas;\n \tstruct object_id ref_delta_oid;\n \tstruct stat st;\n-\tgit_hash_ctx tmp_ctx;\n+\tstruct git_hash_ctx tmp_ctx;\n \n \tif (verbose)\n \t\tprogress = start_progress(\ndiff --git a/builtin/patch-id.c b/builtin/patch-id.c\nindex f540d8daa7..923ff2bb77 100644\n--- a/builtin/patch-id.c\n+++ b/builtin/patch-id.c\n@@ -70,7 +70,7 @@ static size_t get_one_patchid(struct object_id *next_oid, struct object_id *resu\n \tint before = -1, after = -1;\n \tint diff_is_binary = 0;\n \tchar pre_oid_str[GIT_MAX_HEXSZ + 1], post_oid_str[GIT_MAX_HEXSZ + 1];\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \n \tthe_hash_algo->init_fn(&ctx);\n \toidclr(result, the_repository->hash_algo);\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 0fb0266cfd..d2360c453c 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -566,7 +566,7 @@ static void hmac_hash(unsigned char *out,\n \tunsigned char k_ipad[GIT_MAX_BLKSZ];\n \tunsigned char k_opad[GIT_MAX_BLKSZ];\n \tint i;\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \n \t/* RFC 2104 2. (1) */\n \tmemset(key, '\\0', GIT_MAX_BLKSZ);\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex f6b9825fb0..d72885510c 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -28,7 +28,7 @@ static unsigned char buffer[4096];\n static unsigned int offset, len;\n static off_t consumed_bytes;\n static off_t max_input_size;\n-static git_hash_ctx ctx;\n+static struct git_hash_ctx ctx;\n static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n static struct progress *progress;\n \n@@ -614,7 +614,7 @@ int cmd_unpack_objects(int argc,\n {\n \tint i;\n \tstruct object_id oid;\n-\tgit_hash_ctx tmp_ctx;\n+\tstruct git_hash_ctx tmp_ctx;\n \n \tdisable_replace_refs();\n \ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex f889fce734..db1958b525 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -161,7 +161,7 @@ static int already_written(struct bulk_checkin_packfile *state, struct object_id\n  * with a new pack.\n  */\n static int stream_blob_to_pack(struct bulk_checkin_packfile *state,\n-\t\t\t       git_hash_ctx *ctx, off_t *already_hashed_to,\n+\t\t\t       struct git_hash_ctx *ctx, off_t *already_hashed_to,\n \t\t\t       int fd, size_t size, const char *path,\n \t\t\t       unsigned flags)\n {\n@@ -258,7 +258,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,\n \t\t\t\tconst char *path, unsigned flags)\n {\n \toff_t seekback, already_hashed_to;\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \tunsigned char obuf[16384];\n \tunsigned header_len;\n \tstruct hashfile_checkpoint checkpoint;\ndiff --git a/csum-file.c b/csum-file.c\nindex 232121f415..3383515669 100644\n--- a/csum-file.c\n+++ b/csum-file.c\n@@ -248,7 +248,7 @@ uint32_t crc32_end(struct hashfile *f)\n int hashfile_checksum_valid(const unsigned char *data, size_t total_len)\n {\n \tunsigned char got[GIT_MAX_RAWSZ];\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \tconst struct git_hash_algo *algop = unsafe_hash_algo(the_hash_algo);\n \tsize_t data_len = total_len - algop->rawsz;\n \ndiff --git a/csum-file.h b/csum-file.h\nindex b7475f16c2..ffccbf0996 100644\n--- a/csum-file.h\n+++ b/csum-file.h\n@@ -11,7 +11,7 @@ struct hashfile {\n \tint fd;\n \tint check_fd;\n \tunsigned int offset;\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \toff_t total;\n \tstruct progress *tp;\n \tconst char *name;\n@@ -33,7 +33,7 @@ struct hashfile {\n /* Checkpoint */\n struct hashfile_checkpoint {\n \toff_t offset;\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n };\n \n void hashfile_checkpoint_init(struct hashfile *, struct hashfile_checkpoint *);\ndiff --git a/diff.c b/diff.c\nindex 0822ae4433..7f570ebdf9 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -6392,7 +6392,7 @@ static void diff_summary(struct diff_options *opt, struct diff_filepair *p)\n }\n \n struct patch_id_t {\n-\tgit_hash_ctx *ctx;\n+\tstruct git_hash_ctx *ctx;\n \tint patchlen;\n };\n \n@@ -6409,7 +6409,7 @@ static int remove_space(char *line, int len)\n \treturn dst - line;\n }\n \n-void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx)\n+void flush_one_hunk(struct object_id *result, struct git_hash_ctx *ctx)\n {\n \tunsigned char hash[GIT_MAX_RAWSZ];\n \tunsigned short carry = 0;\n@@ -6439,12 +6439,12 @@ static int patch_id_consume(void *priv, char *line, unsigned long len)\n \treturn 0;\n }\n \n-static void patch_id_add_string(git_hash_ctx *ctx, const char *str)\n+static void patch_id_add_string(struct git_hash_ctx *ctx, const char *str)\n {\n \tthe_hash_algo->update_fn(ctx, str, strlen(str));\n }\n \n-static void patch_id_add_mode(git_hash_ctx *ctx, unsigned mode)\n+static void patch_id_add_mode(struct git_hash_ctx *ctx, unsigned mode)\n {\n \t/* large enough for 2^32 in octal */\n \tchar buf[12];\n@@ -6457,7 +6457,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid\n {\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i;\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \tstruct patch_id_t data;\n \n \tthe_hash_algo->init_fn(&ctx);\ndiff --git a/diff.h b/diff.h\nindex 6e6007c17b..d56296dd21 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -644,7 +644,7 @@ void run_diff_index(struct rev_info *revs, unsigned int option);\n \n int do_diff_cache(const struct object_id *, struct diff_options *);\n int diff_flush_patch_id(struct diff_options *, struct object_id *, int);\n-void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx);\n+void flush_one_hunk(struct object_id *result, struct git_hash_ctx *ctx);\n \n int diff_result_code(struct rev_info *);\n \ndiff --git a/hash.h b/hash.h\nindex 5b88d9b714..42b52c6dae 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -241,13 +241,12 @@ struct git_hash_ctx {\n \t\tgit_SHA256_CTX sha256;\n \t} state;\n };\n-typedef struct git_hash_ctx git_hash_ctx;\n \n-typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);\n-typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);\n-typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);\n-typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);\n-typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);\n+typedef void (*git_hash_init_fn)(struct git_hash_ctx *ctx);\n+typedef void (*git_hash_clone_fn)(struct git_hash_ctx *dst, const struct git_hash_ctx *src);\n+typedef void (*git_hash_update_fn)(struct git_hash_ctx *ctx, const void *in, size_t len);\n+typedef void (*git_hash_final_fn)(unsigned char *hash, struct git_hash_ctx *ctx);\n+typedef void (*git_hash_final_oid_fn)(struct object_id *oid, struct git_hash_ctx *ctx);\n \n struct git_hash_algo {\n \t/*\ndiff --git a/http-push.c b/http-push.c\nindex 43da1c7cd3..36867af2f8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -760,7 +760,7 @@ static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)\n static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)\n {\n \tstruct remote_lock *lock = (struct remote_lock *)ctx->userData;\n-\tgit_hash_ctx hash_ctx;\n+\tstruct git_hash_ctx hash_ctx;\n \tunsigned char lock_token_hash[GIT_MAX_RAWSZ];\n \n \tif (tag_closed && ctx->cdata) {\ndiff --git a/http.h b/http.h\nindex 46e334c2c2..36202139f4 100644\n--- a/http.h\n+++ b/http.h\n@@ -228,7 +228,7 @@ struct http_object_request {\n \tlong http_code;\n \tstruct object_id oid;\n \tstruct object_id real_oid;\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \tgit_zstream stream;\n \tint zret;\n \tint rename;\ndiff --git a/object-file.c b/object-file.c\nindex 7505aa6b60..154bcfce78 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -86,82 +86,82 @@ static const struct object_id null_oid_sha256 = {\n \t.algo = GIT_HASH_SHA256,\n };\n \n-static void git_hash_sha1_init(git_hash_ctx *ctx)\n+static void git_hash_sha1_init(struct git_hash_ctx *ctx)\n {\n \tgit_SHA1_Init(&ctx->state.sha1);\n }\n \n-static void git_hash_sha1_clone(git_hash_ctx *dst, const git_hash_ctx *src)\n+static void git_hash_sha1_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)\n {\n \tgit_SHA1_Clone(&dst->state.sha1, &src->state.sha1);\n }\n \n-static void git_hash_sha1_update(git_hash_ctx *ctx, const void *data, size_t len)\n+static void git_hash_sha1_update(struct git_hash_ctx *ctx, const void *data, size_t len)\n {\n \tgit_SHA1_Update(&ctx->state.sha1, data, len);\n }\n \n-static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx)\n+static void git_hash_sha1_final(unsigned char *hash, struct git_hash_ctx *ctx)\n {\n \tgit_SHA1_Final(hash, &ctx->state.sha1);\n }\n \n-static void git_hash_sha1_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n+static void git_hash_sha1_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)\n {\n \tgit_SHA1_Final(oid->hash, &ctx->state.sha1);\n \tmemset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);\n \toid->algo = GIT_HASH_SHA1;\n }\n \n-static void git_hash_sha1_init_unsafe(git_hash_ctx *ctx)\n+static void git_hash_sha1_init_unsafe(struct git_hash_ctx *ctx)\n {\n \tgit_SHA1_Init_unsafe(&ctx->state.sha1_unsafe);\n }\n \n-static void git_hash_sha1_clone_unsafe(git_hash_ctx *dst, const git_hash_ctx *src)\n+static void git_hash_sha1_clone_unsafe(struct git_hash_ctx *dst, const struct git_hash_ctx *src)\n {\n \tgit_SHA1_Clone_unsafe(&dst->state.sha1_unsafe, &src->state.sha1_unsafe);\n }\n \n-static void git_hash_sha1_update_unsafe(git_hash_ctx *ctx, const void *data,\n+static void git_hash_sha1_update_unsafe(struct git_hash_ctx *ctx, const void *data,\n \t\t\t\t      size_t len)\n {\n \tgit_SHA1_Update_unsafe(&ctx->state.sha1_unsafe, data, len);\n }\n \n-static void git_hash_sha1_final_unsafe(unsigned char *hash, git_hash_ctx *ctx)\n+static void git_hash_sha1_final_unsafe(unsigned char *hash, struct git_hash_ctx *ctx)\n {\n \tgit_SHA1_Final_unsafe(hash, &ctx->state.sha1_unsafe);\n }\n \n-static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, git_hash_ctx *ctx)\n+static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, struct git_hash_ctx *ctx)\n {\n \tgit_SHA1_Final_unsafe(oid->hash, &ctx->state.sha1_unsafe);\n \tmemset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);\n \toid->algo = GIT_HASH_SHA1;\n }\n \n-static void git_hash_sha256_init(git_hash_ctx *ctx)\n+static void git_hash_sha256_init(struct git_hash_ctx *ctx)\n {\n \tgit_SHA256_Init(&ctx->state.sha256);\n }\n \n-static void git_hash_sha256_clone(git_hash_ctx *dst, const git_hash_ctx *src)\n+static void git_hash_sha256_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)\n {\n \tgit_SHA256_Clone(&dst->state.sha256, &src->state.sha256);\n }\n \n-static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len)\n+static void git_hash_sha256_update(struct git_hash_ctx *ctx, const void *data, size_t len)\n {\n \tgit_SHA256_Update(&ctx->state.sha256, data, len);\n }\n \n-static void git_hash_sha256_final(unsigned char *hash, git_hash_ctx *ctx)\n+static void git_hash_sha256_final(unsigned char *hash, struct git_hash_ctx *ctx)\n {\n \tgit_SHA256_Final(hash, &ctx->state.sha256);\n }\n \n-static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n+static void git_hash_sha256_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)\n {\n \tgit_SHA256_Final(oid->hash, &ctx->state.sha256);\n \t/*\n@@ -172,18 +172,18 @@ static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n \toid->algo = GIT_HASH_SHA256;\n }\n \n-static void git_hash_unknown_init(git_hash_ctx *ctx UNUSED)\n+static void git_hash_unknown_init(struct git_hash_ctx *ctx UNUSED)\n {\n \tBUG(\"trying to init unknown hash\");\n }\n \n-static void git_hash_unknown_clone(git_hash_ctx *dst UNUSED,\n-\t\t\t\t   const git_hash_ctx *src UNUSED)\n+static void git_hash_unknown_clone(struct git_hash_ctx *dst UNUSED,\n+\t\t\t\t   const struct git_hash_ctx *src UNUSED)\n {\n \tBUG(\"trying to clone unknown hash\");\n }\n \n-static void git_hash_unknown_update(git_hash_ctx *ctx UNUSED,\n+static void git_hash_unknown_update(struct git_hash_ctx *ctx UNUSED,\n \t\t\t\t    const void *data UNUSED,\n \t\t\t\t    size_t len UNUSED)\n {\n@@ -191,13 +191,13 @@ static void git_hash_unknown_update(git_hash_ctx *ctx UNUSED,\n }\n \n static void git_hash_unknown_final(unsigned char *hash UNUSED,\n-\t\t\t\t   git_hash_ctx *ctx UNUSED)\n+\t\t\t\t   struct git_hash_ctx *ctx UNUSED)\n {\n \tBUG(\"trying to finalize unknown hash\");\n }\n \n static void git_hash_unknown_final_oid(struct object_id *oid UNUSED,\n-\t\t\t\t       git_hash_ctx *ctx UNUSED)\n+\t\t\t\t       struct git_hash_ctx *ctx UNUSED)\n {\n \tBUG(\"trying to finalize unknown hash\");\n }\n@@ -1180,7 +1180,7 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)\n \tunsigned long size;\n \tenum object_type obj_type;\n \tstruct git_istream *st;\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \tchar hdr[MAX_HEADER_LEN];\n \tint hdrlen;\n \n@@ -1945,7 +1945,7 @@ void *read_object_with_reference(struct repository *r,\n \t}\n }\n \n-static void hash_object_body(const struct git_hash_algo *algo, git_hash_ctx *c,\n+static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_ctx *c,\n \t\t\t     const void *buf, unsigned long len,\n \t\t\t     struct object_id *oid,\n \t\t\t     char *hdr, int *hdrlen)\n@@ -1961,7 +1961,7 @@ static void write_object_file_prepare(const struct git_hash_algo *algo,\n \t\t\t\t      enum object_type type, struct object_id *oid,\n \t\t\t\t      char *hdr, int *hdrlen)\n {\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \n \t/* Generate the header */\n \t*hdrlen = format_object_header(hdr, *hdrlen, type, len);\n@@ -1975,7 +1975,7 @@ static void write_object_file_prepare_literally(const struct git_hash_algo *algo\n \t\t\t\t      const char *type, struct object_id *oid,\n \t\t\t\t      char *hdr, int *hdrlen)\n {\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \n \t*hdrlen = format_object_header_literally(hdr, *hdrlen, type, len);\n \thash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);\n@@ -2206,7 +2206,7 @@ static int start_loose_object_common(struct strbuf *tmp_file,\n \t\t\t\t     const char *filename, unsigned flags,\n \t\t\t\t     git_zstream *stream,\n \t\t\t\t     unsigned char *buf, size_t buflen,\n-\t\t\t\t     git_hash_ctx *c, git_hash_ctx *compat_c,\n+\t\t\t\t     struct git_hash_ctx *c, struct git_hash_ctx *compat_c,\n \t\t\t\t     char *hdr, int hdrlen)\n {\n \tstruct repository *repo = the_repository;\n@@ -2251,7 +2251,7 @@ static int start_loose_object_common(struct strbuf *tmp_file,\n  * Common steps for the inner git_deflate() loop for writing loose\n  * objects. Returns what git_deflate() returns.\n  */\n-static int write_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,\n+static int write_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *compat_c,\n \t\t\t\t     git_zstream *stream, const int flush,\n \t\t\t\t     unsigned char *in0, const int fd,\n \t\t\t\t     unsigned char *compressed,\n@@ -2280,7 +2280,7 @@ static int write_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,\n  * - End the compression of zlib stream.\n  * - Get the calculated oid to \"oid\".\n  */\n-static int end_loose_object_common(git_hash_ctx *c, git_hash_ctx *compat_c,\n+static int end_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *compat_c,\n \t\t\t\t   git_zstream *stream, struct object_id *oid,\n \t\t\t\t   struct object_id *compat_oid)\n {\n@@ -2306,7 +2306,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,\n \tint fd, ret;\n \tunsigned char compressed[4096];\n \tgit_zstream stream;\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \tstruct object_id parano_oid;\n \tstatic struct strbuf tmp_file = STRBUF_INIT;\n \tstatic struct strbuf filename = STRBUF_INIT;\n@@ -2386,7 +2386,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,\n \tint fd, ret, err = 0, flush = 0;\n \tunsigned char compressed[4096];\n \tgit_zstream stream;\n-\tgit_hash_ctx c, compat_c;\n+\tstruct git_hash_ctx c, compat_c;\n \tstruct strbuf tmp_file = STRBUF_INIT;\n \tstruct strbuf filename = STRBUF_INIT;\n \tint dirlen;\n@@ -3046,7 +3046,7 @@ static int check_stream_oid(git_zstream *stream,\n \t\t\t    const char *path,\n \t\t\t    const struct object_id *expected_oid)\n {\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \tstruct object_id real_oid;\n \tunsigned char buf[4096];\n \tunsigned long total_read;\ndiff --git a/pack-check.c b/pack-check.c\nindex 8d9f6da7ce..f20209fccb 100644\n--- a/pack-check.c\n+++ b/pack-check.c\n@@ -58,7 +58,7 @@ static int verify_packfile(struct repository *r,\n {\n \toff_t index_size = p->index_size;\n \tconst unsigned char *index_base = p->index_data;\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \tunsigned char hash[GIT_MAX_RAWSZ], *pack_sig;\n \toff_t offset = 0, pack_sig_ofs = 0;\n \tuint32_t nr_objects, i;\ndiff --git a/pack-write.c b/pack-write.c\nindex 98a8c0e785..9004d1d095 100644\n--- a/pack-write.c\n+++ b/pack-write.c\n@@ -388,7 +388,7 @@ void fixup_pack_header_footer(int pack_fd,\n \t\t\t off_t partial_pack_offset)\n {\n \tint aligned_sz, buf_sz = 8 * 1024;\n-\tgit_hash_ctx old_hash_ctx, new_hash_ctx;\n+\tstruct git_hash_ctx old_hash_ctx, new_hash_ctx;\n \tstruct pack_header hdr;\n \tchar *buf;\n \tssize_t read_result;\ndiff --git a/read-cache.c b/read-cache.c\nindex d54be2c172..5e765d9af5 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1717,7 +1717,7 @@ int verify_ce_order;\n \n static int verify_hdr(const struct cache_header *hdr, unsigned long size)\n {\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \tunsigned char hash[GIT_MAX_RAWSZ];\n \tint hdr_version;\n \tunsigned char *start, *end;\n@@ -2002,7 +2002,7 @@ static struct index_entry_offset_table *read_ieot_extension(const char *mmap, si\n static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot);\n \n static size_t read_eoie_extension(const char *mmap, size_t mmap_size);\n-static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset);\n+static void write_eoie_extension(struct strbuf *sb, struct git_hash_ctx *eoie_context, size_t offset);\n \n struct load_index_extensions\n {\n@@ -2566,7 +2566,7 @@ int repo_index_has_changes(struct repository *repo,\n }\n \n static int write_index_ext_header(struct hashfile *f,\n-\t\t\t\t  git_hash_ctx *eoie_f,\n+\t\t\t\t  struct git_hash_ctx *eoie_f,\n \t\t\t\t  unsigned int ext,\n \t\t\t\t  unsigned int sz)\n {\n@@ -2831,7 +2831,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,\n {\n \tuint64_t start = getnanotime();\n \tstruct hashfile *f;\n-\tgit_hash_ctx *eoie_c = NULL;\n+\tstruct git_hash_ctx *eoie_c = NULL;\n \tstruct cache_header hdr;\n \tint i, err = 0, removed, extended, hdr_version;\n \tstruct cache_entry **cache = istate->cache;\n@@ -3579,7 +3579,7 @@ static size_t read_eoie_extension(const char *mmap, size_t mmap_size)\n \tuint32_t extsize;\n \tsize_t offset, src_offset;\n \tunsigned char hash[GIT_MAX_RAWSZ];\n-\tgit_hash_ctx c;\n+\tstruct git_hash_ctx c;\n \n \t/* ensure we have an index big enough to contain an EOIE extension */\n \tif (mmap_size < sizeof(struct cache_header) + EOIE_SIZE_WITH_HEADER + the_hash_algo->rawsz)\n@@ -3650,7 +3650,7 @@ static size_t read_eoie_extension(const char *mmap, size_t mmap_size)\n \treturn offset;\n }\n \n-static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset)\n+static void write_eoie_extension(struct strbuf *sb, struct git_hash_ctx *eoie_context, size_t offset)\n {\n \tuint32_t buffer;\n \tunsigned char hash[GIT_MAX_RAWSZ];\ndiff --git a/rerere.c b/rerere.c\nindex e7fa6426b3..5ff9624b57 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -358,7 +358,7 @@ static void rerere_strbuf_putconflict(struct strbuf *buf, int ch, size_t size)\n }\n \n static int handle_conflict(struct strbuf *out, struct rerere_io *io,\n-\t\t\t   int marker_size, git_hash_ctx *ctx)\n+\t\t\t   int marker_size, struct git_hash_ctx *ctx)\n {\n \tenum {\n \t\tRR_SIDE_1 = 0, RR_SIDE_2, RR_ORIGINAL\n@@ -432,7 +432,7 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,\n  */\n static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_size)\n {\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \tstruct strbuf buf = STRBUF_INIT, out = STRBUF_INIT;\n \tint has_conflicts = 0;\n \tif (hash)\ndiff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c\nindex 80df1aae66..803f41c89d 100644\n--- a/t/helper/test-hash-speed.c\n+++ b/t/helper/test-hash-speed.c\n@@ -3,7 +3,7 @@\n \n #define NUM_SECONDS 3\n \n-static inline void compute_hash(const struct git_hash_algo *algo, git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len)\n+static inline void compute_hash(const struct git_hash_algo *algo, struct git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len)\n {\n \talgo->init_fn(ctx);\n \talgo->update_fn(ctx, p, len);\n@@ -12,7 +12,7 @@ static inline void compute_hash(const struct git_hash_algo *algo, git_hash_ctx *\n \n int cmd__hash_speed(int ac, const char **av)\n {\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \tunsigned char hash[GIT_MAX_RAWSZ];\n \tclock_t initial, start, end;\n \tunsigned bufsizes[] = { 64, 256, 1024, 8192, 16384 };\ndiff --git a/t/helper/test-hash.c b/t/helper/test-hash.c\nindex aa82638c62..f9a3db487a 100644\n--- a/t/helper/test-hash.c\n+++ b/t/helper/test-hash.c\n@@ -3,7 +3,7 @@\n \n int cmd_hash_impl(int ac, const char **av, int algo, int unsafe)\n {\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \tunsigned char hash[GIT_MAX_HEXSZ];\n \tunsigned bufsz = 8192;\n \tint binary = 0;\ndiff --git a/t/unit-tests/u-hash.c b/t/unit-tests/u-hash.c\nindex a0320efe4b..05204e7b6c 100644\n--- a/t/unit-tests/u-hash.c\n+++ b/t/unit-tests/u-hash.c\n@@ -8,7 +8,7 @@ static void check_hash_data(const void *data, size_t data_length,\n \tcl_assert(data != NULL);\n \n \tfor (size_t i = 1; i < ARRAY_SIZE(hash_algos); i++) {\n-\t\tgit_hash_ctx ctx;\n+\t\tstruct git_hash_ctx ctx;\n \t\tunsigned char hash[GIT_MAX_HEXSZ];\n \t\tconst struct git_hash_algo *algop = &hash_algos[i];\n \ndiff --git a/trace2/tr2_sid.c b/trace2/tr2_sid.c\nindex 09c4ef0d17..c42696ef52 100644\n--- a/trace2/tr2_sid.c\n+++ b/trace2/tr2_sid.c\n@@ -32,7 +32,7 @@ static void tr2_sid_append_my_sid_component(void)\n {\n \tconst struct git_hash_algo *algo = &hash_algos[GIT_HASH_SHA1];\n \tstruct tr2_tbuf tb_now;\n-\tgit_hash_ctx ctx;\n+\tstruct git_hash_ctx ctx;\n \tpid_t pid = getpid();\n \tunsigned char hash[GIT_MAX_RAWSZ + 1];\n \tchar hex[GIT_MAX_HEXSZ + 1];\n\n-- \n2.48.1.502.g6dc24dfdaf.dirty\n\n"},{"id":"511554","messageId":"20250131-b4-pks-hash-context-direct-v1-3-67a6d3f49d6e@pks.im","threadId":"62878","inReplyTo":"20250131-b4-pks-hash-context-direct-v1-0-67a6d3f49d6e@pks.im","subject":"[PATCH 3/4] hash: provide generic wrappers to update hash contexts","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-01-31T12:55:30Z","receivedAt":"2025-01-31T12:55:45Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The hash context is supposed to be updated via the `git_hash_algo`\nstructure, which contains a list of function pointers to update, clone\nor finalize a hashing context. This requires the callers to track which\nalgorithm was used to initialize the context and continue to use the\nexact same algorithm. If they fail to do that correctly, it can happen\nthat we start to access context state of one hash algorithm with\nfunctions of a different hash algorithm. The result would typically be a\nsegfault, as could be seen e.g. in the patches part of 98422943f0 (Merge\nbranch 'ps/weak-sha1-for-tail-sum-fix', 2025-01-01).\n\nThe situation was significantly improved starting with 04292c3796\n(hash.h: drop unsafe_ function variants, 2025-01-23) and its parent\ncommits. These refactorings ensure that it is not possible to mix up\nsafe and unsafe variants of the same hash algorithm anymore. But in\ntheory, it is still possible to mix up different hash algorithms with\neach other, even though this is a lot less likely to happen.\n\nBut still, we can do better: instead of asking the caller to remember\nthe hash algorithm used to initialize a context, we can instead make the\ncontext itself remember which algorithm it has been initialized with. If\nwe do so, callers can use a set of generic helpers to update the context\nand don't need to be aware of the hash algorithm at all anymore.\n\nAdapt the context initialization functions to store the hash algorithm\nin the hashing context and introduce these generic helpers. Callers will\nbe adapted in the subsequent commit.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n hash.h        | 21 +++++++++++++++++++++\n object-file.c |  6 ++++++\n 2 files changed, 27 insertions(+)\n\ndiff --git a/hash.h b/hash.h\nindex 42b52c6dae..4367acfec5 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -235,6 +235,7 @@ enum get_oid_result {\n \n /* A suitably aligned type for stack allocations of hash contexts. */\n struct git_hash_ctx {\n+\tconst struct git_hash_algo *algop;\n \tunion {\n \t\tgit_SHA_CTX sha1;\n \t\tgit_SHA_CTX_unsafe sha1_unsafe;\n@@ -296,6 +297,26 @@ struct git_hash_algo {\n };\n extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];\n \n+static inline void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)\n+{\n+\tsrc->algop->clone_fn(dst, src);\n+}\n+\n+static inline void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len)\n+{\n+\tctx->algop->update_fn(ctx, in, len);\n+}\n+\n+static inline void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx)\n+{\n+\tctx->algop->final_fn(hash, ctx);\n+}\n+\n+static inline void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)\n+{\n+\tctx->algop->final_oid_fn(oid, ctx);\n+}\n+\n /*\n  * Return a GIT_HASH_* constant based on the name.  Returns GIT_HASH_UNKNOWN if\n  * the name doesn't match a known algorithm.\ndiff --git a/object-file.c b/object-file.c\nindex 154bcfce78..b7f2af515f 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -88,11 +88,13 @@ static const struct object_id null_oid_sha256 = {\n \n static void git_hash_sha1_init(struct git_hash_ctx *ctx)\n {\n+\tctx->algop = &hash_algos[GIT_HASH_SHA1];\n \tgit_SHA1_Init(&ctx->state.sha1);\n }\n \n static void git_hash_sha1_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)\n {\n+\tdst->algop = src->algop;\n \tgit_SHA1_Clone(&dst->state.sha1, &src->state.sha1);\n }\n \n@@ -115,11 +117,13 @@ static void git_hash_sha1_final_oid(struct object_id *oid, struct git_hash_ctx *\n \n static void git_hash_sha1_init_unsafe(struct git_hash_ctx *ctx)\n {\n+\tctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA1]);\n \tgit_SHA1_Init_unsafe(&ctx->state.sha1_unsafe);\n }\n \n static void git_hash_sha1_clone_unsafe(struct git_hash_ctx *dst, const struct git_hash_ctx *src)\n {\n+\tdst->algop = src->algop;\n \tgit_SHA1_Clone_unsafe(&dst->state.sha1_unsafe, &src->state.sha1_unsafe);\n }\n \n@@ -143,11 +147,13 @@ static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, struct git_has\n \n static void git_hash_sha256_init(struct git_hash_ctx *ctx)\n {\n+\tctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA256]);\n \tgit_SHA256_Init(&ctx->state.sha256);\n }\n \n static void git_hash_sha256_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)\n {\n+\tdst->algop = src->algop;\n \tgit_SHA256_Clone(&dst->state.sha256, &src->state.sha256);\n }\n \n\n-- \n2.48.1.502.g6dc24dfdaf.dirty\n\n"},{"id":"511555","messageId":"20250131-b4-pks-hash-context-direct-v1-4-67a6d3f49d6e@pks.im","threadId":"62878","inReplyTo":"20250131-b4-pks-hash-context-direct-v1-0-67a6d3f49d6e@pks.im","subject":"[PATCH 4/4] global: adapt callers to use generic hash context helpers","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-01-31T12:55:31Z","receivedAt":"2025-01-31T12:55:45Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Adapt callers to use generic hash context helpers instead of using the\nhash algorithm to update them. This makes the callsites easier to reason\nabout and removes the possibility that the wrong hash algorithm is used\nto update the hash context's state. And as a nice side effect this also\ngets rid of a bunch of users of `the_hash_algo`.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n builtin/fast-import.c      | 12 ++++++------\n builtin/index-pack.c       | 13 ++++++-------\n builtin/patch-id.c         | 12 ++++++------\n builtin/receive-pack.c     | 16 ++++++++--------\n builtin/unpack-objects.c   |  9 ++++-----\n bulk-checkin.c             |  6 +++---\n csum-file.c                | 14 +++++++-------\n diff.c                     | 24 ++++++++++++------------\n http-push.c                |  4 ++--\n http.c                     |  6 +++---\n object-file.c              | 32 +++++++++++++++-----------------\n pack-check.c               |  4 ++--\n pack-write.c               | 14 +++++++-------\n read-cache.c               | 14 +++++++-------\n rerere.c                   | 14 +++++++-------\n t/helper/test-hash-speed.c |  4 ++--\n t/helper/test-hash.c       |  4 ++--\n t/unit-tests/u-hash.c      |  4 ++--\n trace2/tr2_sid.c           |  4 ++--\n 19 files changed, 103 insertions(+), 107 deletions(-)\n\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex 9862704c62..91f88c467f 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -959,9 +959,9 @@ static int store_object(\n \thdrlen = format_object_header((char *)hdr, sizeof(hdr), type,\n \t\t\t\t      dat->len);\n \tthe_hash_algo->init_fn(&c);\n-\tthe_hash_algo->update_fn(&c, hdr, hdrlen);\n-\tthe_hash_algo->update_fn(&c, dat->buf, dat->len);\n-\tthe_hash_algo->final_oid_fn(&oid, &c);\n+\tgit_hash_update(&c, hdr, hdrlen);\n+\tgit_hash_update(&c, dat->buf, dat->len);\n+\tgit_hash_final_oid(&oid, &c);\n \tif (oidout)\n \t\toidcpy(oidout, &oid);\n \n@@ -1113,7 +1113,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)\n \thdrlen = format_object_header((char *)out_buf, out_sz, OBJ_BLOB, len);\n \n \tthe_hash_algo->init_fn(&c);\n-\tthe_hash_algo->update_fn(&c, out_buf, hdrlen);\n+\tgit_hash_update(&c, out_buf, hdrlen);\n \n \tcrc32_begin(pack_file);\n \n@@ -1131,7 +1131,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)\n \t\t\tif (!n && feof(stdin))\n \t\t\t\tdie(\"EOF in data (%\" PRIuMAX \" bytes remaining)\", len);\n \n-\t\t\tthe_hash_algo->update_fn(&c, in_buf, n);\n+\t\t\tgit_hash_update(&c, in_buf, n);\n \t\t\ts.next_in = in_buf;\n \t\t\ts.avail_in = n;\n \t\t\tlen -= n;\n@@ -1157,7 +1157,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)\n \t\t}\n \t}\n \tgit_deflate_end(&s);\n-\tthe_hash_algo->final_oid_fn(&oid, &c);\n+\tgit_hash_final_oid(&oid, &c);\n \n \tif (oidout)\n \t\toidcpy(oidout, &oid);\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 40e49868b1..5ee13661a1 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -301,7 +301,7 @@ static void flush(void)\n \tif (input_offset) {\n \t\tif (output_fd >= 0)\n \t\t\twrite_or_die(output_fd, input_buffer, input_offset);\n-\t\tthe_hash_algo->update_fn(&input_ctx, input_buffer, input_offset);\n+\t\tgit_hash_update(&input_ctx, input_buffer, input_offset);\n \t\tmemmove(input_buffer, input_buffer + input_offset, input_len);\n \t\tinput_offset = 0;\n \t}\n@@ -482,7 +482,7 @@ static void *unpack_entry_data(off_t offset, unsigned long size,\n \tif (!is_delta_type(type)) {\n \t\thdrlen = format_object_header(hdr, sizeof(hdr), type, size);\n \t\tthe_hash_algo->init_fn(&c);\n-\t\tthe_hash_algo->update_fn(&c, hdr, hdrlen);\n+\t\tgit_hash_update(&c, hdr, hdrlen);\n \t} else\n \t\toid = NULL;\n \tif (type == OBJ_BLOB && size > big_file_threshold)\n@@ -502,7 +502,7 @@ static void *unpack_entry_data(off_t offset, unsigned long size,\n \t\tstatus = git_inflate(&stream, 0);\n \t\tuse(input_len - stream.avail_in);\n \t\tif (oid)\n-\t\t\tthe_hash_algo->update_fn(&c, last_out, stream.next_out - last_out);\n+\t\t\tgit_hash_update(&c, last_out, stream.next_out - last_out);\n \t\tif (buf == fixed_buf) {\n \t\t\tstream.next_out = buf;\n \t\t\tstream.avail_out = sizeof(fixed_buf);\n@@ -512,7 +512,7 @@ static void *unpack_entry_data(off_t offset, unsigned long size,\n \t\tbad_object(offset, _(\"inflate returned %d\"), status);\n \tgit_inflate_end(&stream);\n \tif (oid)\n-\t\tthe_hash_algo->final_oid_fn(oid, &c);\n+\t\tgit_hash_final_oid(oid, &c);\n \treturn buf == fixed_buf ? NULL : buf;\n }\n \n@@ -1286,9 +1286,8 @@ static void parse_pack_objects(unsigned char *hash)\n \n \t/* Check pack integrity */\n \tflush();\n-\tthe_hash_algo->init_fn(&tmp_ctx);\n-\tthe_hash_algo->clone_fn(&tmp_ctx, &input_ctx);\n-\tthe_hash_algo->final_fn(hash, &tmp_ctx);\n+\tgit_hash_clone(&tmp_ctx, &input_ctx);\n+\tgit_hash_final(hash, &tmp_ctx);\n \tif (!hasheq(fill(the_hash_algo->rawsz), hash, the_repository->hash_algo))\n \t\tdie(_(\"pack is corrupted (SHA1 mismatch)\"));\n \tuse(the_hash_algo->rawsz);\ndiff --git a/builtin/patch-id.c b/builtin/patch-id.c\nindex 923ff2bb77..cdef2ec10a 100644\n--- a/builtin/patch-id.c\n+++ b/builtin/patch-id.c\n@@ -85,7 +85,7 @@ static size_t get_one_patchid(struct object_id *next_oid, struct object_id *resu\n \t\t    !skip_prefix(line, \"From \", &p) &&\n \t\t    starts_with(line, \"\\\\ \") && 12 < strlen(line)) {\n \t\t\tif (verbatim)\n-\t\t\t\tthe_hash_algo->update_fn(&ctx, line, strlen(line));\n+\t\t\t\tgit_hash_update(&ctx, line, strlen(line));\n \t\t\tcontinue;\n \t\t}\n \n@@ -104,10 +104,10 @@ static size_t get_one_patchid(struct object_id *next_oid, struct object_id *resu\n \t\t\t    starts_with(line, \"Binary files\")) {\n \t\t\t\tdiff_is_binary = 1;\n \t\t\t\tbefore = 0;\n-\t\t\t\tthe_hash_algo->update_fn(&ctx, pre_oid_str,\n-\t\t\t\t\t\t\t strlen(pre_oid_str));\n-\t\t\t\tthe_hash_algo->update_fn(&ctx, post_oid_str,\n-\t\t\t\t\t\t\t strlen(post_oid_str));\n+\t\t\t\tgit_hash_update(&ctx, pre_oid_str,\n+\t\t\t\t\t\tstrlen(pre_oid_str));\n+\t\t\t\tgit_hash_update(&ctx, post_oid_str,\n+\t\t\t\t\t\tstrlen(post_oid_str));\n \t\t\t\tif (stable)\n \t\t\t\t\tflush_one_hunk(result, &ctx);\n \t\t\t\tcontinue;\n@@ -165,7 +165,7 @@ static size_t get_one_patchid(struct object_id *next_oid, struct object_id *resu\n \t\t/* Add line to hash algo (possibly removing whitespace) */\n \t\tlen = verbatim ? strlen(line) : remove_space(line);\n \t\tpatchlen += len;\n-\t\tthe_hash_algo->update_fn(&ctx, line, len);\n+\t\tgit_hash_update(&ctx, line, len);\n \t}\n \n \tif (!found_next)\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex d2360c453c..cc358600ca 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -572,8 +572,8 @@ static void hmac_hash(unsigned char *out,\n \tmemset(key, '\\0', GIT_MAX_BLKSZ);\n \tif (the_hash_algo->blksz < key_len) {\n \t\tthe_hash_algo->init_fn(&ctx);\n-\t\tthe_hash_algo->update_fn(&ctx, key_in, key_len);\n-\t\tthe_hash_algo->final_fn(key, &ctx);\n+\t\tgit_hash_update(&ctx, key_in, key_len);\n+\t\tgit_hash_final(key, &ctx);\n \t} else {\n \t\tmemcpy(key, key_in, key_len);\n \t}\n@@ -586,15 +586,15 @@ static void hmac_hash(unsigned char *out,\n \n \t/* RFC 2104 2. (3) & (4) */\n \tthe_hash_algo->init_fn(&ctx);\n-\tthe_hash_algo->update_fn(&ctx, k_ipad, sizeof(k_ipad));\n-\tthe_hash_algo->update_fn(&ctx, text, text_len);\n-\tthe_hash_algo->final_fn(out, &ctx);\n+\tgit_hash_update(&ctx, k_ipad, sizeof(k_ipad));\n+\tgit_hash_update(&ctx, text, text_len);\n+\tgit_hash_final(out, &ctx);\n \n \t/* RFC 2104 2. (6) & (7) */\n \tthe_hash_algo->init_fn(&ctx);\n-\tthe_hash_algo->update_fn(&ctx, k_opad, sizeof(k_opad));\n-\tthe_hash_algo->update_fn(&ctx, out, the_hash_algo->rawsz);\n-\tthe_hash_algo->final_fn(out, &ctx);\n+\tgit_hash_update(&ctx, k_opad, sizeof(k_opad));\n+\tgit_hash_update(&ctx, out, the_hash_algo->rawsz);\n+\tgit_hash_final(out, &ctx);\n }\n \n static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex d72885510c..8383bcf404 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -70,7 +70,7 @@ static void *fill(int min)\n \tif (min > sizeof(buffer))\n \t\tdie(\"cannot fill %d bytes\", min);\n \tif (offset) {\n-\t\tthe_hash_algo->update_fn(&ctx, buffer, offset);\n+\t\tgit_hash_update(&ctx, buffer, offset);\n \t\tmemmove(buffer, buffer + offset, len);\n \t\toffset = 0;\n \t}\n@@ -667,10 +667,9 @@ int cmd_unpack_objects(int argc,\n \t}\n \tthe_hash_algo->init_fn(&ctx);\n \tunpack_all();\n-\tthe_hash_algo->update_fn(&ctx, buffer, offset);\n-\tthe_hash_algo->init_fn(&tmp_ctx);\n-\tthe_hash_algo->clone_fn(&tmp_ctx, &ctx);\n-\tthe_hash_algo->final_oid_fn(&oid, &tmp_ctx);\n+\tgit_hash_update(&ctx, buffer, offset);\n+\tgit_hash_clone(&tmp_ctx, &ctx);\n+\tgit_hash_final_oid(&oid, &tmp_ctx);\n \tif (strict) {\n \t\twrite_rest();\n \t\tif (fsck_finish(&fsck_options))\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex db1958b525..0f40c5dac9 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -194,7 +194,7 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,\n \t\t\t\tif (rsize < hsize)\n \t\t\t\t\thsize = rsize;\n \t\t\t\tif (hsize)\n-\t\t\t\t\tthe_hash_algo->update_fn(ctx, ibuf, hsize);\n+\t\t\t\t\tgit_hash_update(ctx, ibuf, hsize);\n \t\t\t\t*already_hashed_to = offset;\n \t\t\t}\n \t\t\ts.next_in = ibuf;\n@@ -271,7 +271,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,\n \theader_len = format_object_header((char *)obuf, sizeof(obuf),\n \t\t\t\t\t  OBJ_BLOB, size);\n \tthe_hash_algo->init_fn(&ctx);\n-\tthe_hash_algo->update_fn(&ctx, obuf, header_len);\n+\tgit_hash_update(&ctx, obuf, header_len);\n \n \t/* Note: idx is non-NULL when we are writing */\n \tif ((flags & HASH_WRITE_OBJECT) != 0) {\n@@ -306,7 +306,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,\n \t\tif (lseek(fd, seekback, SEEK_SET) == (off_t) -1)\n \t\t\treturn error(\"cannot seek back\");\n \t}\n-\tthe_hash_algo->final_oid_fn(result_oid, &ctx);\n+\tgit_hash_final_oid(result_oid, &ctx);\n \tif (!idx)\n \t\treturn 0;\n \ndiff --git a/csum-file.c b/csum-file.c\nindex 3383515669..5c54b3a0b9 100644\n--- a/csum-file.c\n+++ b/csum-file.c\n@@ -50,7 +50,7 @@ void hashflush(struct hashfile *f)\n \n \tif (offset) {\n \t\tif (!f->skip_hash)\n-\t\t\tf->algop->update_fn(&f->ctx, f->buffer, offset);\n+\t\t\tgit_hash_update(&f->ctx, f->buffer, offset);\n \t\tflush(f, f->buffer, offset);\n \t\tf->offset = 0;\n \t}\n@@ -73,7 +73,7 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,\n \tif (f->skip_hash)\n \t\thashclr(f->buffer, f->algop);\n \telse\n-\t\tf->algop->final_fn(f->buffer, &f->ctx);\n+\t\tgit_hash_final(f->buffer, &f->ctx);\n \n \tif (result)\n \t\thashcpy(result, f->buffer, f->algop);\n@@ -128,7 +128,7 @@ void hashwrite(struct hashfile *f, const void *buf, unsigned int count)\n \t\t\t * f->offset is necessarily zero.\n \t\t\t */\n \t\t\tif (!f->skip_hash)\n-\t\t\t\tf->algop->update_fn(&f->ctx, buf, nr);\n+\t\t\t\tgit_hash_update(&f->ctx, buf, nr);\n \t\t\tflush(f, buf, nr);\n \t\t} else {\n \t\t\t/*\n@@ -217,7 +217,7 @@ void hashfile_checkpoint(struct hashfile *f, struct hashfile_checkpoint *checkpo\n {\n \thashflush(f);\n \tcheckpoint->offset = f->total;\n-\tf->algop->clone_fn(&checkpoint->ctx, &f->ctx);\n+\tgit_hash_clone(&checkpoint->ctx, &f->ctx);\n }\n \n int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint)\n@@ -228,7 +228,7 @@ int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint\n \t    lseek(f->fd, offset, SEEK_SET) != offset)\n \t\treturn -1;\n \tf->total = offset;\n-\tf->algop->clone_fn(&f->ctx, &checkpoint->ctx);\n+\tgit_hash_clone(&f->ctx, &checkpoint->ctx);\n \tf->offset = 0; /* hashflush() was called in checkpoint */\n \treturn 0;\n }\n@@ -256,8 +256,8 @@ int hashfile_checksum_valid(const unsigned char *data, size_t total_len)\n \t\treturn 0; /* say \"too short\"? */\n \n \talgop->init_fn(&ctx);\n-\talgop->update_fn(&ctx, data, data_len);\n-\talgop->final_fn(got, &ctx);\n+\tgit_hash_update(&ctx, data, data_len);\n+\tgit_hash_final(got, &ctx);\n \n \treturn hasheq(got, data + data_len, algop);\n }\ndiff --git a/diff.c b/diff.c\nindex 7f570ebdf9..019fb893a7 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -6415,7 +6415,7 @@ void flush_one_hunk(struct object_id *result, struct git_hash_ctx *ctx)\n \tunsigned short carry = 0;\n \tint i;\n \n-\tthe_hash_algo->final_fn(hash, ctx);\n+\tgit_hash_final(hash, ctx);\n \tthe_hash_algo->init_fn(ctx);\n \t/* 20-byte sum, with carry */\n \tfor (i = 0; i < the_hash_algo->rawsz; ++i) {\n@@ -6434,14 +6434,14 @@ static int patch_id_consume(void *priv, char *line, unsigned long len)\n \t\treturn 0;\n \tnew_len = remove_space(line, len);\n \n-\tthe_hash_algo->update_fn(data->ctx, line, new_len);\n+\tgit_hash_update(data->ctx, line, new_len);\n \tdata->patchlen += new_len;\n \treturn 0;\n }\n \n static void patch_id_add_string(struct git_hash_ctx *ctx, const char *str)\n {\n-\tthe_hash_algo->update_fn(ctx, str, strlen(str));\n+\tgit_hash_update(ctx, str, strlen(str));\n }\n \n static void patch_id_add_mode(struct git_hash_ctx *ctx, unsigned mode)\n@@ -6449,7 +6449,7 @@ static void patch_id_add_mode(struct git_hash_ctx *ctx, unsigned mode)\n \t/* large enough for 2^32 in octal */\n \tchar buf[12];\n \tint len = xsnprintf(buf, sizeof(buf), \"%06o\", mode);\n-\tthe_hash_algo->update_fn(ctx, buf, len);\n+\tgit_hash_update(ctx, buf, len);\n }\n \n /* returns 0 upon success, and writes result into oid */\n@@ -6493,9 +6493,9 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid\n \t\tlen2 = remove_space(p->two->path, strlen(p->two->path));\n \t\tpatch_id_add_string(&ctx, \"diff--git\");\n \t\tpatch_id_add_string(&ctx, \"a/\");\n-\t\tthe_hash_algo->update_fn(&ctx, p->one->path, len1);\n+\t\tgit_hash_update(&ctx, p->one->path, len1);\n \t\tpatch_id_add_string(&ctx, \"b/\");\n-\t\tthe_hash_algo->update_fn(&ctx, p->two->path, len2);\n+\t\tgit_hash_update(&ctx, p->two->path, len2);\n \n \t\tif (p->one->mode == 0) {\n \t\t\tpatch_id_add_string(&ctx, \"newfilemode\");\n@@ -6514,24 +6514,24 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid\n \t\t\t/* don't do anything since we're only populating header info */\n \t\t} else if (diff_filespec_is_binary(options->repo, p->one) ||\n \t\t    diff_filespec_is_binary(options->repo, p->two)) {\n-\t\t\tthe_hash_algo->update_fn(&ctx, oid_to_hex(&p->one->oid),\n+\t\t\tgit_hash_update(&ctx, oid_to_hex(&p->one->oid),\n \t\t\t\t\tthe_hash_algo->hexsz);\n-\t\t\tthe_hash_algo->update_fn(&ctx, oid_to_hex(&p->two->oid),\n+\t\t\tgit_hash_update(&ctx, oid_to_hex(&p->two->oid),\n \t\t\t\t\tthe_hash_algo->hexsz);\n \t\t} else {\n \t\t\tif (p->one->mode == 0) {\n \t\t\t\tpatch_id_add_string(&ctx, \"---/dev/null\");\n \t\t\t\tpatch_id_add_string(&ctx, \"+++b/\");\n-\t\t\t\tthe_hash_algo->update_fn(&ctx, p->two->path, len2);\n+\t\t\t\tgit_hash_update(&ctx, p->two->path, len2);\n \t\t\t} else if (p->two->mode == 0) {\n \t\t\t\tpatch_id_add_string(&ctx, \"---a/\");\n-\t\t\t\tthe_hash_algo->update_fn(&ctx, p->one->path, len1);\n+\t\t\t\tgit_hash_update(&ctx, p->one->path, len1);\n \t\t\t\tpatch_id_add_string(&ctx, \"+++/dev/null\");\n \t\t\t} else {\n \t\t\t\tpatch_id_add_string(&ctx, \"---a/\");\n-\t\t\t\tthe_hash_algo->update_fn(&ctx, p->one->path, len1);\n+\t\t\t\tgit_hash_update(&ctx, p->one->path, len1);\n \t\t\t\tpatch_id_add_string(&ctx, \"+++b/\");\n-\t\t\t\tthe_hash_algo->update_fn(&ctx, p->two->path, len2);\n+\t\t\t\tgit_hash_update(&ctx, p->two->path, len2);\n \t\t\t}\n \n \t\t\tif (fill_mmfile(options->repo, &mf1, p->one) < 0 ||\ndiff --git a/http-push.c b/http-push.c\nindex 36867af2f8..1b030d96f4 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -774,8 +774,8 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)\n \t\t\tlock->token = xstrdup(ctx->cdata);\n \n \t\t\tthe_hash_algo->init_fn(&hash_ctx);\n-\t\t\tthe_hash_algo->update_fn(&hash_ctx, lock->token, strlen(lock->token));\n-\t\t\tthe_hash_algo->final_fn(lock_token_hash, &hash_ctx);\n+\t\t\tgit_hash_update(&hash_ctx, lock->token, strlen(lock->token));\n+\t\t\tgit_hash_final(lock_token_hash, &hash_ctx);\n \n \t\t\tlock->tmpfile_suffix[0] = '_';\n \t\t\tmemcpy(lock->tmpfile_suffix + 1, hash_to_hex(lock_token_hash), the_hash_algo->hexsz);\ndiff --git a/http.c b/http.c\nindex f08b2ae474..f4504133e8 100644\n--- a/http.c\n+++ b/http.c\n@@ -2597,8 +2597,8 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,\n \t\tfreq->stream.next_out = expn;\n \t\tfreq->stream.avail_out = sizeof(expn);\n \t\tfreq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);\n-\t\tthe_hash_algo->update_fn(&freq->c, expn,\n-\t\t\t\t\t sizeof(expn) - freq->stream.avail_out);\n+\t\tgit_hash_update(&freq->c, expn,\n+\t\t\t\tsizeof(expn) - freq->stream.avail_out);\n \t} while (freq->stream.avail_in && freq->zret == Z_OK);\n \treturn nmemb;\n }\n@@ -2763,7 +2763,7 @@ int finish_http_object_request(struct http_object_request *freq)\n \t\treturn -1;\n \t}\n \n-\tthe_hash_algo->final_oid_fn(&freq->real_oid, &freq->c);\n+\tgit_hash_final_oid(&freq->real_oid, &freq->c);\n \tif (freq->zret != Z_STREAM_END) {\n \t\tunlink_or_warn(freq->tmpfile.buf);\n \t\treturn -1;\ndiff --git a/object-file.c b/object-file.c\nindex b7f2af515f..00c3a4b910 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -1199,7 +1199,7 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)\n \n \t/* Sha1.. */\n \tr->hash_algo->init_fn(&c);\n-\tr->hash_algo->update_fn(&c, hdr, hdrlen);\n+\tgit_hash_update(&c, hdr, hdrlen);\n \tfor (;;) {\n \t\tchar buf[1024 * 16];\n \t\tssize_t readlen = read_istream(st, buf, sizeof(buf));\n@@ -1210,9 +1210,9 @@ int stream_object_signature(struct repository *r, const struct object_id *oid)\n \t\t}\n \t\tif (!readlen)\n \t\t\tbreak;\n-\t\tr->hash_algo->update_fn(&c, buf, readlen);\n+\t\tgit_hash_update(&c, buf, readlen);\n \t}\n-\tr->hash_algo->final_oid_fn(&real_oid, &c);\n+\tgit_hash_final_oid(&real_oid, &c);\n \tclose_istream(st);\n \treturn !oideq(oid, &real_oid) ? -1 : 0;\n }\n@@ -1957,9 +1957,9 @@ static void hash_object_body(const struct git_hash_algo *algo, struct git_hash_c\n \t\t\t     char *hdr, int *hdrlen)\n {\n \talgo->init_fn(c);\n-\talgo->update_fn(c, hdr, *hdrlen);\n-\talgo->update_fn(c, buf, len);\n-\talgo->final_oid_fn(oid, c);\n+\tgit_hash_update(c, hdr, *hdrlen);\n+\tgit_hash_update(c, buf, len);\n+\tgit_hash_final_oid(oid, c);\n }\n \n static void write_object_file_prepare(const struct git_hash_algo *algo,\n@@ -2246,9 +2246,9 @@ static int start_loose_object_common(struct strbuf *tmp_file,\n \tstream->avail_in = hdrlen;\n \twhile (git_deflate(stream, 0) == Z_OK)\n \t\t; /* nothing */\n-\talgo->update_fn(c, hdr, hdrlen);\n+\tgit_hash_update(c, hdr, hdrlen);\n \tif (compat && compat_c)\n-\t\tcompat->update_fn(compat_c, hdr, hdrlen);\n+\t\tgit_hash_update(compat_c, hdr, hdrlen);\n \n \treturn fd;\n }\n@@ -2264,14 +2264,13 @@ static int write_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx\n \t\t\t\t     const size_t compressed_len)\n {\n \tstruct repository *repo = the_repository;\n-\tconst struct git_hash_algo *algo = repo->hash_algo;\n \tconst struct git_hash_algo *compat = repo->compat_hash_algo;\n \tint ret;\n \n \tret = git_deflate(stream, flush ? Z_FINISH : 0);\n-\talgo->update_fn(c, in0, stream->next_in - in0);\n+\tgit_hash_update(c, in0, stream->next_in - in0);\n \tif (compat && compat_c)\n-\t\tcompat->update_fn(compat_c, in0, stream->next_in - in0);\n+\t\tgit_hash_update(compat_c, in0, stream->next_in - in0);\n \tif (write_in_full(fd, compressed, stream->next_out - compressed) < 0)\n \t\tdie_errno(_(\"unable to write loose object file\"));\n \tstream->next_out = compressed;\n@@ -2291,16 +2290,15 @@ static int end_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *\n \t\t\t\t   struct object_id *compat_oid)\n {\n \tstruct repository *repo = the_repository;\n-\tconst struct git_hash_algo *algo = repo->hash_algo;\n \tconst struct git_hash_algo *compat = repo->compat_hash_algo;\n \tint ret;\n \n \tret = git_deflate_end_gently(stream);\n \tif (ret != Z_OK)\n \t\treturn ret;\n-\talgo->final_oid_fn(oid, c);\n+\tgit_hash_final_oid(oid, c);\n \tif (compat && compat_c)\n-\t\tcompat->final_oid_fn(compat_oid, compat_c);\n+\t\tgit_hash_final_oid(compat_oid, compat_c);\n \n \treturn Z_OK;\n }\n@@ -3059,7 +3057,7 @@ static int check_stream_oid(git_zstream *stream,\n \tint status = Z_OK;\n \n \tthe_hash_algo->init_fn(&c);\n-\tthe_hash_algo->update_fn(&c, hdr, stream->total_out);\n+\tgit_hash_update(&c, hdr, stream->total_out);\n \n \t/*\n \t * We already read some bytes into hdr, but the ones up to the NUL\n@@ -3079,7 +3077,7 @@ static int check_stream_oid(git_zstream *stream,\n \t\tif (size - total_read < stream->avail_out)\n \t\t\tstream->avail_out = size - total_read;\n \t\tstatus = git_inflate(stream, Z_FINISH);\n-\t\tthe_hash_algo->update_fn(&c, buf, stream->next_out - buf);\n+\t\tgit_hash_update(&c, buf, stream->next_out - buf);\n \t\ttotal_read += stream->next_out - buf;\n \t}\n \tgit_inflate_end(stream);\n@@ -3094,7 +3092,7 @@ static int check_stream_oid(git_zstream *stream,\n \t\treturn -1;\n \t}\n \n-\tthe_hash_algo->final_oid_fn(&real_oid, &c);\n+\tgit_hash_final_oid(&real_oid, &c);\n \tif (!oideq(expected_oid, &real_oid)) {\n \t\terror(_(\"hash mismatch for %s (expected %s)\"), path,\n \t\t      oid_to_hex(expected_oid));\ndiff --git a/pack-check.c b/pack-check.c\nindex f20209fccb..d0aeb5ec41 100644\n--- a/pack-check.c\n+++ b/pack-check.c\n@@ -77,9 +77,9 @@ static int verify_packfile(struct repository *r,\n \t\t\tpack_sig_ofs = p->pack_size - r->hash_algo->rawsz;\n \t\tif (offset > pack_sig_ofs)\n \t\t\tremaining -= (unsigned int)(offset - pack_sig_ofs);\n-\t\tr->hash_algo->update_fn(&ctx, in, remaining);\n+\t\tgit_hash_update(&ctx, in, remaining);\n \t} while (offset < pack_sig_ofs);\n-\tr->hash_algo->final_fn(hash, &ctx);\n+\tgit_hash_final(hash, &ctx);\n \tpack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL);\n \tif (!hasheq(hash, pack_sig, the_repository->hash_algo))\n \t\terr = error(\"%s pack checksum mismatch\",\ndiff --git a/pack-write.c b/pack-write.c\nindex 9004d1d095..cfcb7297b8 100644\n--- a/pack-write.c\n+++ b/pack-write.c\n@@ -406,9 +406,9 @@ void fixup_pack_header_footer(int pack_fd,\n \t\t\t  pack_name);\n \tif (lseek(pack_fd, 0, SEEK_SET) != 0)\n \t\tdie_errno(\"Failed seeking to start of '%s'\", pack_name);\n-\tthe_hash_algo->update_fn(&old_hash_ctx, &hdr, sizeof(hdr));\n+\tgit_hash_update(&old_hash_ctx, &hdr, sizeof(hdr));\n \thdr.hdr_entries = htonl(object_count);\n-\tthe_hash_algo->update_fn(&new_hash_ctx, &hdr, sizeof(hdr));\n+\tgit_hash_update(&new_hash_ctx, &hdr, sizeof(hdr));\n \twrite_or_die(pack_fd, &hdr, sizeof(hdr));\n \tpartial_pack_offset -= sizeof(hdr);\n \n@@ -423,7 +423,7 @@ void fixup_pack_header_footer(int pack_fd,\n \t\t\tbreak;\n \t\tif (n < 0)\n \t\t\tdie_errno(\"Failed to checksum '%s'\", pack_name);\n-\t\tthe_hash_algo->update_fn(&new_hash_ctx, buf, n);\n+\t\tgit_hash_update(&new_hash_ctx, buf, n);\n \n \t\taligned_sz -= n;\n \t\tif (!aligned_sz)\n@@ -432,11 +432,11 @@ void fixup_pack_header_footer(int pack_fd,\n \t\tif (!partial_pack_hash)\n \t\t\tcontinue;\n \n-\t\tthe_hash_algo->update_fn(&old_hash_ctx, buf, n);\n+\t\tgit_hash_update(&old_hash_ctx, buf, n);\n \t\tpartial_pack_offset -= n;\n \t\tif (partial_pack_offset == 0) {\n \t\t\tunsigned char hash[GIT_MAX_RAWSZ];\n-\t\t\tthe_hash_algo->final_fn(hash, &old_hash_ctx);\n+\t\t\tgit_hash_final(hash, &old_hash_ctx);\n \t\t\tif (!hasheq(hash, partial_pack_hash,\n \t\t\t\t    the_repository->hash_algo))\n \t\t\t\tdie(\"Unexpected checksum for %s \"\n@@ -454,8 +454,8 @@ void fixup_pack_header_footer(int pack_fd,\n \tfree(buf);\n \n \tif (partial_pack_hash)\n-\t\tthe_hash_algo->final_fn(partial_pack_hash, &old_hash_ctx);\n-\tthe_hash_algo->final_fn(new_pack_hash, &new_hash_ctx);\n+\t\tgit_hash_final(partial_pack_hash, &old_hash_ctx);\n+\tgit_hash_final(new_pack_hash, &new_hash_ctx);\n \twrite_or_die(pack_fd, new_pack_hash, the_hash_algo->rawsz);\n \tfsync_component_or_die(FSYNC_COMPONENT_PACK, pack_fd, pack_name);\n }\ndiff --git a/read-cache.c b/read-cache.c\nindex 5e765d9af5..7ef01c3806 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1739,8 +1739,8 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)\n \t\treturn 0;\n \n \tthe_hash_algo->init_fn(&c);\n-\tthe_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);\n-\tthe_hash_algo->final_fn(hash, &c);\n+\tgit_hash_update(&c, hdr, size - the_hash_algo->rawsz);\n+\tgit_hash_final(hash, &c);\n \tif (!hasheq(hash, start, the_repository->hash_algo))\n \t\treturn error(_(\"bad index file sha1 signature\"));\n \treturn 0;\n@@ -2576,8 +2576,8 @@ static int write_index_ext_header(struct hashfile *f,\n \tif (eoie_f) {\n \t\text = htonl(ext);\n \t\tsz = htonl(sz);\n-\t\tthe_hash_algo->update_fn(eoie_f, &ext, sizeof(ext));\n-\t\tthe_hash_algo->update_fn(eoie_f, &sz, sizeof(sz));\n+\t\tgit_hash_update(eoie_f, &ext, sizeof(ext));\n+\t\tgit_hash_update(eoie_f, &sz, sizeof(sz));\n \t}\n \treturn 0;\n }\n@@ -3634,12 +3634,12 @@ static size_t read_eoie_extension(const char *mmap, size_t mmap_size)\n \t\tif (src_offset + 8 + extsize < src_offset)\n \t\t\treturn 0;\n \n-\t\tthe_hash_algo->update_fn(&c, mmap + src_offset, 8);\n+\t\tgit_hash_update(&c, mmap + src_offset, 8);\n \n \t\tsrc_offset += 8;\n \t\tsrc_offset += extsize;\n \t}\n-\tthe_hash_algo->final_fn(hash, &c);\n+\tgit_hash_final(hash, &c);\n \tif (!hasheq(hash, (const unsigned char *)index, the_repository->hash_algo))\n \t\treturn 0;\n \n@@ -3660,7 +3660,7 @@ static void write_eoie_extension(struct strbuf *sb, struct git_hash_ctx *eoie_co\n \tstrbuf_add(sb, &buffer, sizeof(uint32_t));\n \n \t/* hash */\n-\tthe_hash_algo->final_fn(hash, eoie_context);\n+\tgit_hash_final(hash, eoie_context);\n \tstrbuf_add(sb, hash, the_hash_algo->rawsz);\n }\n \ndiff --git a/rerere.c b/rerere.c\nindex 5ff9624b57..c42cee618b 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -396,12 +396,12 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io,\n \t\t\tstrbuf_addbuf(out, &two);\n \t\t\trerere_strbuf_putconflict(out, '>', marker_size);\n \t\t\tif (ctx) {\n-\t\t\t\tthe_hash_algo->update_fn(ctx, one.buf ?\n-\t\t\t\t\t\t\t one.buf : \"\",\n-\t\t\t\t\t\t\t one.len + 1);\n-\t\t\t\tthe_hash_algo->update_fn(ctx, two.buf ?\n-\t\t\t\t\t\t\t two.buf : \"\",\n-\t\t\t\t\t\t\t two.len + 1);\n+\t\t\t\tgit_hash_update(ctx, one.buf ?\n+\t\t\t\t\t\tone.buf : \"\",\n+\t\t\t\t\t\tone.len + 1);\n+\t\t\t\tgit_hash_update(ctx, two.buf ?\n+\t\t\t\t\t\ttwo.buf : \"\",\n+\t\t\t\t\t\ttwo.len + 1);\n \t\t\t}\n \t\t\tbreak;\n \t\t} else if (hunk == RR_SIDE_1)\n@@ -453,7 +453,7 @@ static int handle_path(unsigned char *hash, struct rerere_io *io, int marker_siz\n \tstrbuf_release(&out);\n \n \tif (hash)\n-\t\tthe_hash_algo->final_fn(hash, &ctx);\n+\t\tgit_hash_final(hash, &ctx);\n \n \treturn has_conflicts;\n }\ndiff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c\nindex 803f41c89d..fbf67fe6bd 100644\n--- a/t/helper/test-hash-speed.c\n+++ b/t/helper/test-hash-speed.c\n@@ -6,8 +6,8 @@\n static inline void compute_hash(const struct git_hash_algo *algo, struct git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len)\n {\n \talgo->init_fn(ctx);\n-\talgo->update_fn(ctx, p, len);\n-\talgo->final_fn(final, ctx);\n+\tgit_hash_update(ctx, p, len);\n+\tgit_hash_final(final, ctx);\n }\n \n int cmd__hash_speed(int ac, const char **av)\ndiff --git a/t/helper/test-hash.c b/t/helper/test-hash.c\nindex f9a3db487a..f0ee61c8b4 100644\n--- a/t/helper/test-hash.c\n+++ b/t/helper/test-hash.c\n@@ -48,9 +48,9 @@ int cmd_hash_impl(int ac, const char **av, int algo, int unsafe)\n \t\t}\n \t\tif (this_sz == 0)\n \t\t\tbreak;\n-\t\talgop->update_fn(&ctx, buffer, this_sz);\n+\t\tgit_hash_update(&ctx, buffer, this_sz);\n \t}\n-\talgop->final_fn(hash, &ctx);\n+\tgit_hash_final(hash, &ctx);\n \n \tif (binary)\n \t\tfwrite(hash, 1, algop->rawsz, stdout);\ndiff --git a/t/unit-tests/u-hash.c b/t/unit-tests/u-hash.c\nindex 05204e7b6c..bd4ac6a6e1 100644\n--- a/t/unit-tests/u-hash.c\n+++ b/t/unit-tests/u-hash.c\n@@ -13,8 +13,8 @@ static void check_hash_data(const void *data, size_t data_length,\n \t\tconst struct git_hash_algo *algop = &hash_algos[i];\n \n \t\talgop->init_fn(&ctx);\n-\t\talgop->update_fn(&ctx, data, data_length);\n-\t\talgop->final_fn(hash, &ctx);\n+\t\tgit_hash_update(&ctx, data, data_length);\n+\t\tgit_hash_final(hash, &ctx);\n \n \t\tcl_assert_equal_s(hash_to_hex_algop(hash,algop), expected_hashes[i - 1]);\n \t}\ndiff --git a/trace2/tr2_sid.c b/trace2/tr2_sid.c\nindex c42696ef52..1c1d27b0ee 100644\n--- a/trace2/tr2_sid.c\n+++ b/trace2/tr2_sid.c\n@@ -46,8 +46,8 @@ static void tr2_sid_append_my_sid_component(void)\n \t\tstrbuf_add(&tr2sid_buf, \"Localhost\", 9);\n \telse {\n \t\talgo->init_fn(&ctx);\n-\t\talgo->update_fn(&ctx, hostname, strlen(hostname));\n-\t\talgo->final_fn(hash, &ctx);\n+\t\tgit_hash_update(&ctx, hostname, strlen(hostname));\n+\t\tgit_hash_final(hash, &ctx);\n \t\thash_to_hex_algop_r(hex, hash, algo);\n \t\tstrbuf_addch(&tr2sid_buf, 'H');\n \t\tstrbuf_add(&tr2sid_buf, hex, 8);\n\n-- \n2.48.1.502.g6dc24dfdaf.dirty\n\n"},{"id":"511584","messageId":"xmqqh65e97d8.fsf@gitster.g","threadId":"62878","inReplyTo":"20250131-b4-pks-hash-context-direct-v1-0-67a6d3f49d6e@pks.im","subject":"Re: [PATCH 0/4] hash: introduce generic wrappers to update hash contexts","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-31T18:16:19Z","receivedAt":"2025-01-31T18:16:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> this patch series introduces a couple of generic wrappers to update hash\n> contexts. Instead of updating contexts via function pointers provided by\n> the hash algorithm, we now remember the hash algorithm in the context\n> itself. As a result, subsequent calls that update the hash don't need to\n> remember which algorithm they used:\n>\n>     ```\n>     struct git_hash_ctx ctx;\n>     struct object_id oid;\n>\n>     git_hash_sha1_init(&ctx);\n>     git_hash_update(&ctx, data);\n>     git_hash_final_oid(&oid, &ctx);\n>     ```\n>\n> This was discussed in [1] and [2].\n>\n> The series is built on top of master at 3b0d05c4a7 (The fifth batch,\n> 2025-01-29) with tb/unsafe-hashtcleanup at 04292c3796 (hash.h: drop\n> unsafe_ function variants, 2025-01-23) merged into it.\n>\n> Thanks!\n>\n> Patrick\n>\n> [1]: <Z3fhK1ACzJfVehM2@pks.im>\n> [2]: <Z4jyZCAwqOjZ-u2U@pks.im>\n\nSounds sensible.  \n\nIt seems to textually interact with Karthik's attempt to pass down a\nhash_algo instance through the callchain in pack-write.c but I\nshould be able to resolve the conflicts.\n\nThanks.\n"},{"id":"511676","messageId":"Z6BXVJraWurdMehX@pks.im","threadId":"62878","inReplyTo":"xmqqh65e97d8.fsf@gitster.g","subject":"Re: [PATCH 0/4] hash: introduce generic wrappers to update hash contexts","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-02-03T05:42:44Z","receivedAt":"2025-02-03T05:42:47Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Jan 31, 2025 at 10:16:19AM -0800, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > this patch series introduces a couple of generic wrappers to update hash\n> > contexts. Instead of updating contexts via function pointers provided by\n> > the hash algorithm, we now remember the hash algorithm in the context\n> > itself. As a result, subsequent calls that update the hash don't need to\n> > remember which algorithm they used:\n> >\n> >     ```\n> >     struct git_hash_ctx ctx;\n> >     struct object_id oid;\n> >\n> >     git_hash_sha1_init(&ctx);\n> >     git_hash_update(&ctx, data);\n> >     git_hash_final_oid(&oid, &ctx);\n> >     ```\n> >\n> > This was discussed in [1] and [2].\n> >\n> > The series is built on top of master at 3b0d05c4a7 (The fifth batch,\n> > 2025-01-29) with tb/unsafe-hashtcleanup at 04292c3796 (hash.h: drop\n> > unsafe_ function variants, 2025-01-23) merged into it.\n> >\n> > Thanks!\n> >\n> > Patrick\n> >\n> > [1]: <Z3fhK1ACzJfVehM2@pks.im>\n> > [2]: <Z4jyZCAwqOjZ-u2U@pks.im>\n> \n> Sounds sensible.  \n> \n> It seems to textually interact with Karthik's attempt to pass down a\n> hash_algo instance through the callchain in pack-write.c but I\n> should be able to resolve the conflicts.\n\nAh, sorry, I didn't do a test merge with 'seen'. In any case, the\nconflict resolution looks good to me, thanks!\n\nPatrick\n"},{"id":"512202","messageId":"Z6qDzscMzRyoIiz1@nand.local","threadId":"62878","inReplyTo":"20250131-b4-pks-hash-context-direct-v1-0-67a6d3f49d6e@pks.im","subject":"Re: [PATCH 0/4] hash: introduce generic wrappers to update hash contexts","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2025-02-10T22:55:10Z","receivedAt":"2025-02-10T22:55:12Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Fri, Jan 31, 2025 at 01:55:27PM +0100, Patrick Steinhardt wrote:\n> ---\n> Patrick Steinhardt (4):\n>       hash: convert hashing context to a structure\n>       hash: stop typedeffing the hash context\n>       hash: provide generic wrappers to update hash contexts\n>       global: adapt callers to use generic hash context helpers\n\nCatching up through some emails that I had missed, and this series looks\ngreat to me. I'm really glad that you ended up pulling on this thread a\nlittle more, and the result is quite pleasing.\n\nJunio has already marked this as \"Will merge to 'master'\" in the latest\nWC, and I have no objections to that ;-).\n\nThanks,\nTaylor\n"}]}