{"thread":{"id":"64108","subject":"[PATCH 00/17] Use rust types in xdiff.","startedAt":"2025-09-07T19:45:23Z","lastAt":"2025-10-03T13:47:54Z","messageCount":158,"participants":["Ezekiel Newren via GitGitGadget","Junio C Hamano","Ezekiel Newren","Elijah Newren","Phillip Wood","Ben Knoble","Jeff King","Kristoffer Haugsbakk"],"isPatch":true,"patchVersion":1,"patchTotal":17},"messages":[{"id":"525725","messageId":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":null,"subject":"[PATCH 00/17] Use rust types in xdiff.","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:03Z","receivedAt":"2025-09-07T19:45:23Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"This patch series involves ZERO Rust code and toolchains, which avoids the\ndebate about Rust's portability and timeline. Instead, it shows how Git can\nimmediately benefit from Rust's design choices without using it at all. The\nrationale for using Rust types on the C and Rust side is addressed in the\ncommit that creates compat/rust_types.h.\n\nThis patch series has 2 parts:\n\n * Patches 1-9: Clean up xdiff, this can be merged without part 2.\n * Patches 10-17: Define Rust types in compat/rust_types.h and then start\n   refactoring xdiff with Rust types. This depends on part 1.\n\nThe cleanup in this patch series makes the structs xrecord_t and xdfile_t\nRust FFI friendly. My opinion is that part 1 should be merged soon, while\npart 2 can be discussed further.\n\nBefore:\n\ntypedef struct s_xrecord {\n\tstruct s_xrecord *next;\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\tchastore_t rcha;\n\tlong nrec;\n\tunsigned int hbits;\n\txrecord_t **rhash;\n\tlong dstart, dend;\n\txrecord_t **recs;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n\tunsigned long *ha;\n} xdfile_t;\n\n\nAfter cleanup:\n\ntypedef struct s_xrecord {\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\txrecord_t *recs;\n\tlong nrec;\n\tlong dstart, dend;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n} xdfile_t;\n\n\nAfter using Rust types:\n\ntypedef struct s_xrecord {\n\tu8 const *ptr;\n\tusize size;\n\tu64 line_hash;\n\tusize minimal_perfect_hash;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\txrecord_t *recs;\n\tusize nrec;\n\ti32 dstart, dend;\n\tu8 *rchg;\n\tusize *rindex;\n\tusize nreff;\n} xdfile_t;\n\n\nEzekiel Newren (17):\n  xdiff: delete static forward declarations in xprepare\n  xdiff: delete local variables and initialize/free xdfile_t directly\n  xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  xdiff: delete xdl_get_rec() in xemit\n  xdiff: delete struct diffdata_t\n  xdiff: delete redundant array xdfile_t.ha\n  xdiff: delete fields ha, line, size in xdlclass_t in favor of an\n    xrecord_t\n  xdiff: delete chastore from xdfile_t, view with --color-words\n  xdiff: treat xdfile_t.rchg like an enum\n  compat/rust_types.h: define rust primitive types\n  xdiff: include compat/rust_types.h\n  xdiff: make xrecord_t.ptr a u8 instead of char\n  xdiff: make xrecord_t.size a usize instead of long\n  xdiff: split xrecord_t.ha into line_hash and minimal_perfect_hash\n  xdiff: make xdfile_t.nrec a usize instead of long\n  xdiff: make xdfile_t.nreff a usize instead of long\n  xdiff: change the types of dstart, dend, rchg, and rindex in xdfile_t\n\n compat/rust_types.h |  28 +++++\n xdiff/xdiff.h       |   4 +\n xdiff/xdiffi.c      | 118 ++++++++----------\n xdiff/xdiffi.h      |  11 +-\n xdiff/xemit.c       |  52 +++-----\n xdiff/xhistogram.c  |  14 +--\n xdiff/xinclude.h    |   1 +\n xdiff/xmacros.h     |   2 +-\n xdiff/xmerge.c      |  66 +++++-----\n xdiff/xpatience.c   |  28 ++---\n xdiff/xprepare.c    | 289 +++++++++++++++++---------------------------\n xdiff/xtypes.h      |  26 ++--\n xdiff/xutils.c      |  12 +-\n 13 files changed, 293 insertions(+), 358 deletions(-)\n create mode 100644 compat/rust_types.h\n\n\nbase-commit: 16bd9f20a403117f2e0d9bcda6c6e621d3763e77\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2048%2Fezekielnewren%2Fuse_rust_types_in_xdiff-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2048/ezekielnewren/use_rust_types_in_xdiff-v1\nPull-Request: https://github.com/git/git/pull/2048\n-- \ngitgitgadget\n"},{"id":"525726","messageId":"9cf9d09c079060bb03556db270a3100ca62c6ba0.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 01/17] xdiff: delete static forward declarations in xprepare","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:04Z","receivedAt":"2025-09-07T19:45:24Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nMove xdl_prepare_env() later in the file to avoid the need\nfor static forward declarations.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 116 ++++++++++++++++++++---------------------------\n 1 file changed, 50 insertions(+), 66 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e1d4017b2d..a45c5ee208 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -53,21 +53,6 @@ typedef struct s_xdlclassifier {\n \n \n \n-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);\n-static void xdl_free_classifier(xdlclassifier_t *cf);\n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec);\n-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n-\t\t\t   xdlclassifier_t *cf, xdfile_t *xdf);\n-static void xdl_free_ctx(xdfile_t *xdf);\n-static int xdl_clean_mmatch(char const *dis, long i, long s, long e);\n-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-\n-\n-\n-\n static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {\n \tcf->flags = flags;\n \n@@ -242,57 +227,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {\n }\n \n \n-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n-\t\t    xdfenv_t *xe) {\n-\tlong enl1, enl2, sample;\n-\txdlclassifier_t cf;\n-\n-\tmemset(&cf, 0, sizeof(cf));\n-\n-\t/*\n-\t * For histogram diff, we can afford a smaller sample size and\n-\t * thus a poorer estimate of the number of lines, as the hash\n-\t * table (rhash) won't be filled up/grown. The number of lines\n-\t * (nrecs) will be updated correctly anyway by\n-\t * xdl_prepare_ctx().\n-\t */\n-\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n-\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n-\n-\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n-\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n-\n-\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n-\t\treturn -1;\n-\n-\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n-\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n-\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n-\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf2);\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\txdl_free_classifier(&cf);\n-\n-\treturn 0;\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n@@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2\n \n \treturn 0;\n }\n+\n+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n+\t\t    xdfenv_t *xe) {\n+\tlong enl1, enl2, sample;\n+\txdlclassifier_t cf;\n+\n+\tmemset(&cf, 0, sizeof(cf));\n+\n+\t/*\n+\t * For histogram diff, we can afford a smaller sample size and\n+\t * thus a poorer estimate of the number of lines, as the hash\n+\t * table (rhash) won't be filled up/grown. The number of lines\n+\t * (nrecs) will be updated correctly anyway by\n+\t * xdl_prepare_ctx().\n+\t */\n+\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n+\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n+\n+\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n+\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n+\n+\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n+\t\treturn -1;\n+\n+\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n+\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n+\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n+\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf2);\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t    }\n+\n+\txdl_free_classifier(&cf);\n+\n+\treturn 0;\n+}\n-- \ngitgitgadget\n\n"},{"id":"525727","messageId":"15832ad2715d377ed9b0c9f3de66cc4532cac2ba.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 02/17] xdiff: delete local variables and initialize/free xdfile_t directly","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:05Z","receivedAt":"2025-09-07T19:45:25Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxdl_prepare_ctx() uses local variables and assigns them to the\ncorresponding xdfile_t fields if there are no errors. Delete them and\nuse the fields of xdfile_t directly.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 79 +++++++++++++++++++-----------------------------\n 1 file changed, 31 insertions(+), 48 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex a45c5ee208..2ed1785b09 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -134,99 +134,82 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n }\n \n \n+static void xdl_free_ctx(xdfile_t *xdf)\n+{\n+\n+\txdl_free(xdf->rhash);\n+\txdl_free(xdf->rindex);\n+\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->ha);\n+\txdl_free(xdf->recs);\n+\txdl_cha_free(&xdf->rcha);\n+}\n+\n+\n static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n \t\t\t   xdlclassifier_t *cf, xdfile_t *xdf) {\n-\tunsigned int hbits;\n-\tlong nrec, hsize, bsize;\n+\tlong bsize;\n \tunsigned long hav;\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n-\txrecord_t **recs;\n-\txrecord_t **rhash;\n-\tunsigned long *ha;\n-\tchar *rchg;\n-\tlong *rindex;\n \n-\tha = NULL;\n-\trindex = NULL;\n-\trchg = NULL;\n-\trhash = NULL;\n-\trecs = NULL;\n+\txdf->ha = NULL;\n+\txdf->rindex = NULL;\n+\txdf->rchg = NULL;\n+\txdf->rhash = NULL;\n+\txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n \t\tgoto abort;\n-\tif (!XDL_ALLOC_ARRAY(recs, narec))\n+\tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\thbits = xdl_hashbits((unsigned int) narec);\n-\thsize = 1 << hbits;\n-\tif (!XDL_CALLOC_ARRAY(rhash, hsize))\n+\txdf->hbits = xdl_hashbits((unsigned int) narec);\n+\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n \t\tgoto abort;\n \n-\tnrec = 0;\n+\txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n \t\t\tprev = cur;\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n-\t\t\tif (XDL_ALLOC_GROW(recs, nrec + 1, narec))\n+\t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n \t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n \t\t\t\tgoto abort;\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\trecs[nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)\n+\t\t\txdf->recs[xdf->nrec++] = crec;\n+\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(rchg, nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n-\t\tif (!XDL_ALLOC_ARRAY(rindex, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(ha, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n \t\t\tgoto abort;\n \t}\n \n-\txdf->nrec = nrec;\n-\txdf->recs = recs;\n-\txdf->hbits = hbits;\n-\txdf->rhash = rhash;\n-\txdf->rchg = rchg + 1;\n-\txdf->rindex = rindex;\n+\txdf->rchg += 1;\n \txdf->nreff = 0;\n-\txdf->ha = ha;\n \txdf->dstart = 0;\n-\txdf->dend = nrec - 1;\n+\txdf->dend = xdf->nrec - 1;\n \n \treturn 0;\n \n abort:\n-\txdl_free(ha);\n-\txdl_free(rindex);\n-\txdl_free(rchg);\n-\txdl_free(rhash);\n-\txdl_free(recs);\n-\txdl_cha_free(&xdf->rcha);\n+\txdl_free_ctx(xdf);\n \treturn -1;\n }\n \n \n-static void xdl_free_ctx(xdfile_t *xdf) {\n-\n-\txdl_free(xdf->rhash);\n-\txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n-\txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n-- \ngitgitgadget\n\n"},{"id":"525728","messageId":"7d5e387916eaf4afd40afc85b1678306b8a5f076.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 03/17] xdiff: delete unnecessary fields from xrecord_t and xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:06Z","receivedAt":"2025-09-07T19:45:26Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,\nbut never used for anything by the code. Remove them.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 15 ++-------------\n xdiff/xtypes.h   |  3 ---\n 2 files changed, 2 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 2ed1785b09..91b0ed54e0 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -91,8 +91,7 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n }\n \n \n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec) {\n+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n \tchar const *line;\n \txdlclass_t *rcrec;\n@@ -126,10 +125,6 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \n \trec->ha = (unsigned long) rcrec->idx;\n \n-\thi = (long) XDL_HASHLONG(rec->ha, hbits);\n-\trec->next = rhash[hi];\n-\trhash[hi] = rec;\n-\n \treturn 0;\n }\n \n@@ -137,7 +132,6 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n static void xdl_free_ctx(xdfile_t *xdf)\n {\n \n-\txdl_free(xdf->rhash);\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->ha);\n@@ -156,7 +150,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n-\txdf->rhash = NULL;\n \txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n@@ -164,10 +157,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\txdf->hbits = xdl_hashbits((unsigned int) narec);\n-\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n-\t\tgoto abort;\n-\n \txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n@@ -181,7 +170,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n \t\t\txdf->recs[xdf->nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n+\t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8442bd436e..8b8467360e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -39,7 +39,6 @@ typedef struct s_chastore {\n } chastore_t;\n \n typedef struct s_xrecord {\n-\tstruct s_xrecord *next;\n \tchar const *ptr;\n \tlong size;\n \tunsigned long ha;\n@@ -48,8 +47,6 @@ typedef struct s_xrecord {\n typedef struct s_xdfile {\n \tchastore_t rcha;\n \tlong nrec;\n-\tunsigned int hbits;\n-\txrecord_t **rhash;\n \tlong dstart, dend;\n \txrecord_t **recs;\n \tchar *rchg;\n-- \ngitgitgadget\n\n"},{"id":"525729","messageId":"ab17d8c23f0ca48374416ec1eb34128585795736.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 04/17] xdiff: delete xdl_get_rec() in xemit","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:07Z","receivedAt":"2025-09-07T19:45:27Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThis function aliases the fields of xrecord_t, which makes it harder\nto track the usages of those fields. Delete it.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 38 +++++++++++++-------------------------\n 1 file changed, 13 insertions(+), 25 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 1d40c9cb40..2161ac3cd0 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -22,23 +22,13 @@\n \n #include \"xinclude.h\"\n \n-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n \n-\t*rec = xdf->recs[ri]->ptr;\n-\n-\treturn xdf->recs[ri]->size;\n-}\n-\n-\n-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n-\tlong size, psize = strlen(pre);\n-\tchar const *rec;\n-\n-\tsize = xdl_get_rec(xdf, ri, &rec);\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n+{\n+\txrecord_t *rec = xdf->recs[ri];\n \n+\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n-\t}\n \n \treturn 0;\n }\n@@ -120,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\txrecord_t *rec = xdf->recs[ri];\n+\n \tif (!xecfg->find_func)\n-\t\treturn def_ff(rec, len, buf, sz);\n-\treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n+\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n+\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n }\n \n static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n@@ -160,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\txrecord_t *rec = xdf->recs[ri];\n+\tlong i = 0;\n \n-\twhile (len > 0 && XDL_ISSPACE(*rec)) {\n-\t\trec++;\n-\t\tlen--;\n-\t}\n-\treturn !len;\n+\tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n+\n+\treturn i == rec->size;\n }\n \n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n-- \ngitgitgadget\n\n"},{"id":"525730","messageId":"6cf371ec13d3e57ea7d679a0170951d0b0346349.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 05/17] xdiff: delete struct diffdata_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:08Z","receivedAt":"2025-09-07T19:45:29Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nEvery field in this struct is an alias for a certain field in xdfile_t.\n\ndiffdata_t.nrec   -> xdfile_t.nreff\ndiffdata_t.ha     -> xdfile_t.ha\ndiffdata_t.rindex -> xdfile_t.rindex\ndiffdata_t.rchg   -> xdfile_t.rchg\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 32 ++++++++------------------------\n xdiff/xdiffi.h | 11 ++---------\n 2 files changed, 10 insertions(+), 33 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 5a96e36dfb..bbf0161f84 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -257,10 +257,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n  * sub-boxes by calling the box splitting function. Note that the real job\n  * (marking changed lines) is done in the two boundary reaching checks.\n  */\n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;\n+\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n@@ -273,17 +273,11 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t * be obviously changed.\n \t */\n \tif (off1 == lim1) {\n-\t\tchar *rchg2 = dd2->rchg;\n-\t\tlong *rindex2 = dd2->rindex;\n-\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\trchg2[rindex2[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n \t} else if (off2 == lim2) {\n-\t\tchar *rchg1 = dd1->rchg;\n-\t\tlong *rindex1 = dd1->rindex;\n-\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\trchg1[rindex1[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -300,9 +294,9 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t\t/*\n \t\t * ... et Impera.\n \t\t */\n-\t\tif (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,\n+\t\tif (xdl_recs_cmp(xdf1, off1, spl.i1, xdf2, off2, spl.i2,\n \t\t\t\t kvdf, kvdb, spl.min_lo, xenv) < 0 ||\n-\t\t    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,\n+\t\t    xdl_recs_cmp(xdf1, spl.i1, lim1, xdf2, spl.i2, lim2,\n \t\t\t\t kvdf, kvdb, spl.min_hi, xenv) < 0) {\n \n \t\t\treturn -1;\n@@ -318,7 +312,6 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \tlong ndiags;\n \tlong *kvd, *kvdf, *kvdb;\n \txdalgoenv_t xenv;\n-\tdiffdata_t dd1, dd2;\n \tint res;\n \n \tif (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)\n@@ -357,16 +350,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \txenv.snake_cnt = XDL_SNAKE_CNT;\n \txenv.heur_min = XDL_HEUR_MIN_COST;\n \n-\tdd1.nrec = xe->xdf1.nreff;\n-\tdd1.ha = xe->xdf1.ha;\n-\tdd1.rchg = xe->xdf1.rchg;\n-\tdd1.rindex = xe->xdf1.rindex;\n-\tdd2.nrec = xe->xdf2.nreff;\n-\tdd2.ha = xe->xdf2.ha;\n-\tdd2.rchg = xe->xdf2.rchg;\n-\tdd2.rindex = xe->xdf2.rindex;\n-\n-\tres = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,\n+\tres = xdl_recs_cmp(&xe->xdf1, 0, xe->xdf1.nreff, &xe->xdf2, 0, xe->xdf2.nreff,\n \t\t\t   kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,\n \t\t\t   &xenv);\n \txdl_free(kvd);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 126c9d8ff4..49e52c67f9 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -24,13 +24,6 @@\n #define XDIFFI_H\n \n \n-typedef struct s_diffdata {\n-\tlong nrec;\n-\tunsigned long const *ha;\n-\tlong *rindex;\n-\tchar *rchg;\n-} diffdata_t;\n-\n typedef struct s_xdalgoenv {\n \tlong mxcost;\n \tlong snake_cnt;\n@@ -46,8 +39,8 @@ typedef struct s_xdchange {\n \n \n \n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);\n int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\txdfenv_t *xe);\n-- \ngitgitgadget\n\n"},{"id":"525731","messageId":"bff456860268a833078b273ff34e3bc26a0c3975.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 06/17] xdiff: delete redundant array xdfile_t.ha","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:09Z","receivedAt":"2025-09-07T19:45:30Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen 0 <= i < xdfile_t.nreff the following is true:\nxdfile_t.ha[i] == xdfile_t.recs[xdfile_t.rindex[i]]\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c   | 24 ++++++++++++++----------\n xdiff/xprepare.c | 12 ++----------\n xdiff/xtypes.h   |  1 -\n 3 files changed, 16 insertions(+), 21 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex bbf0161f84..11cd090b53 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -22,6 +22,11 @@\n \n #include \"xinclude.h\"\n \n+static unsigned long get_hash(xdfile_t *xdf, long index)\n+{\n+\treturn xdf->recs[xdf->rindex[index]]->ha;\n+}\n+\n #define XDL_MAX_COST_MIN 256\n #define XDL_HEUR_MIN_COST 256\n #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)\n@@ -42,8 +47,8 @@ typedef struct s_xdpsplit {\n  * using this algorithm, so a little bit of heuristic is needed to cut the\n  * search and to return a suboptimal point.\n  */\n-static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n-\t\t      unsigned long const *ha2, long off2, long lim2,\n+static long xdl_split(xdfile_t *xdf1, long off1, long lim1,\n+\t\t      xdfile_t *xdf2, long off2, long lim2,\n \t\t      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,\n \t\t      xdalgoenv_t *xenv) {\n \tlong dmin = off1 - lim2, dmax = lim1 - off2;\n@@ -87,7 +92,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdf[d + 1];\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);\n+\t\t\tfor (; i1 < lim1 && i2 < lim2 && get_hash(xdf1, i1) == get_hash(xdf2, i2); i1++, i2++);\n \t\t\tif (i1 - prev1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdf[d] = i1;\n@@ -124,7 +129,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdb[d + 1] - 1;\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);\n+\t\t\tfor (; i1 > off1 && i2 > off2 && get_hash(xdf1, i1 - 1) == get_hash(xdf2, i2 - 1); i1--, i2--);\n \t\t\tif (prev1 - i1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdb[d] = i1;\n@@ -159,7 +164,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&\n \t\t\t\t    off2 + xenv->snake_cnt <= i2 && i2 < lim2) {\n-\t\t\t\t\tfor (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)\n+\t\t\t\t\tfor (k = 1; get_hash(xdf1, i1 - k) == get_hash(xdf2, i2 - k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -183,7 +188,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&\n \t\t\t\t    off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {\n-\t\t\t\t\tfor (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)\n+\t\t\t\t\tfor (k = 0; get_hash(xdf1, i1 + k) == get_hash(xdf2, i2 + k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt - 1) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -260,13 +265,12 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n \t */\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, off1) == get_hash(xdf2, off2); off1++, off2++);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, lim1 - 1) == get_hash(xdf2, lim2 - 1); lim1--, lim2--);\n \n \t/*\n \t * If one dimension is empty, then all records on the other one must\n@@ -285,7 +289,7 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t/*\n \t\t * Divide ...\n \t\t */\n-\t\tif (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,\n+\t\tif (xdl_split(xdf1, off1, lim1, xdf2, off2, lim2, kvdf, kvdb,\n \t\t\t      need_min, &spl, xenv) < 0) {\n \n \t\t\treturn -1;\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 91b0ed54e0..59730989a3 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -134,7 +134,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n \n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n \txdl_free(xdf->recs);\n \txdl_cha_free(&xdf->rcha);\n }\n@@ -147,7 +146,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n \n-\txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n@@ -182,8 +180,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n \t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n-\t\t\tgoto abort;\n \t}\n \n \txdf->rchg += 1;\n@@ -301,9 +297,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (dis1[i] == 1 ||\n \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n-\t\t\txdf1->rindex[nreff] = i;\n-\t\t\txdf1->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf1->rchg[i] = 1;\n \t}\n@@ -313,9 +307,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf2->dend; i++, recs++) {\n \t\tif (dis2[i] == 1 ||\n \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n-\t\t\txdf2->rindex[nreff] = i;\n-\t\t\txdf2->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf2->rchg[i] = 1;\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8b8467360e..85848f1685 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -52,7 +52,6 @@ typedef struct s_xdfile {\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\n-\tunsigned long *ha;\n } xdfile_t;\n \n typedef struct s_xdfenv {\n-- \ngitgitgadget\n\n"},{"id":"525732","messageId":"db3d4e9a89d4b72dfb7e59dd38c6dad31f5c1cf0.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 07/17] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:10Z","receivedAt":"2025-09-07T19:45:31Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 16 ++++++----------\n 1 file changed, 6 insertions(+), 10 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 59730989a3..6f1d4b4725 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -32,9 +32,7 @@\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n-\tunsigned long ha;\n-\tchar const *line;\n-\tlong size;\n+\txrecord_t rec;\n \tlong idx;\n \tlong len1, len2;\n } xdlclass_t;\n@@ -93,14 +91,12 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n \n static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n-\tchar const *line;\n \txdlclass_t *rcrec;\n \n-\tline = rec->ptr;\n \thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n-\t\tif (rcrec->ha == rec->ha &&\n-\t\t\t\txdl_recmatch(rcrec->line, rcrec->size,\n+\t\tif (rcrec->rec.ha == rec->ha &&\n+\t\t\t\txdl_recmatch(rcrec->rec.ptr, rcrec->rec.size,\n \t\t\t\t\trec->ptr, rec->size, cf->flags))\n \t\t\tbreak;\n \n@@ -113,9 +109,9 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \t\tif (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))\n \t\t\t\treturn -1;\n \t\tcf->rcrecs[rcrec->idx] = rcrec;\n-\t\trcrec->line = line;\n-\t\trcrec->size = rec->size;\n-\t\trcrec->ha = rec->ha;\n+\t\trcrec->rec.ptr = rec->ptr;\n+\t\trcrec->rec.size = rec->size;\n+\t\trcrec->rec.ha = rec->ha;\n \t\trcrec->len1 = rcrec->len2 = 0;\n \t\trcrec->next = cf->rchash[hi];\n \t\tcf->rchash[hi] = rcrec;\n-- \ngitgitgadget\n\n"},{"id":"525733","messageId":"e7d1933d1c470528de94118fe9c58b47bcc67aca.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 08/17] xdiff: delete chastore from xdfile_t, view with --color-words","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:11Z","receivedAt":"2025-09-07T19:45:32Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe chastore_t type is very unfriendly to Rust FFI. It's also redundant\nsince 'recs' is a vector type that grows every time an xrecord_t is\nadded.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 24 ++++++++++----------\n xdiff/xemit.c      |  6 ++---\n xdiff/xhistogram.c |  2 +-\n xdiff/xmerge.c     | 56 +++++++++++++++++++++++-----------------------\n xdiff/xpatience.c  | 10 ++++-----\n xdiff/xprepare.c   | 19 ++++++----------\n xdiff/xtypes.h     |  3 +--\n xdiff/xutils.c     | 12 +++++-----\n 8 files changed, 63 insertions(+), 69 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 11cd090b53..a66125d44a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -24,7 +24,7 @@\n \n static unsigned long get_hash(xdfile_t *xdf, long index)\n {\n-\treturn xdf->recs[xdf->rindex[index]]->ha;\n+\treturn xdf->recs[xdf->rindex[index]].ha;\n }\n \n #define XDL_MAX_COST_MIN 256\n@@ -489,13 +489,13 @@ static void measure_split(const xdfile_t *xdf, long split,\n \t\tm->indent = -1;\n \t} else {\n \t\tm->end_of_file = 0;\n-\t\tm->indent = get_indent(xdf->recs[split]);\n+\t\tm->indent = get_indent(&xdf->recs[split]);\n \t}\n \n \tm->pre_blank = 0;\n \tm->pre_indent = -1;\n \tfor (i = split - 1; i >= 0; i--) {\n-\t\tm->pre_indent = get_indent(xdf->recs[i]);\n+\t\tm->pre_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->pre_indent != -1)\n \t\t\tbreak;\n \t\tm->pre_blank += 1;\n@@ -508,7 +508,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n \tm->post_blank = 0;\n \tm->post_indent = -1;\n \tfor (i = split + 1; i < xdf->nrec; i++) {\n-\t\tm->post_indent = get_indent(xdf->recs[i]);\n+\t\tm->post_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->post_indent != -1)\n \t\t\tbreak;\n \t\tm->post_blank += 1;\n@@ -752,7 +752,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n-\t    recs_match(xdf->recs[g->start], xdf->recs[g->end])) {\n+\t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n \t\txdf->rchg[g->start++] = 0;\n \t\txdf->rchg[g->end++] = 1;\n \n@@ -773,7 +773,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n-\t    recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {\n+\t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n \t\txdf->rchg[--g->start] = 1;\n \t\txdf->rchg[--g->end] = 0;\n \n@@ -988,16 +988,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n \t\tint ignore = 1;\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tlong i;\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\txch->ignore = ignore;\n \t}\n@@ -1021,7 +1021,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \txdchange_t *xch;\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tint ignore = 1;\n \t\tlong i;\n \n@@ -1033,11 +1033,11 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\txch->ignore = ignore;\n \t}\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 2161ac3cd0..b2f1f30cd3 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -25,7 +25,7 @@\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n@@ -110,7 +110,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n@@ -150,7 +150,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \tlong i = 0;\n \n \tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 040d81e0bc..4d857e8ae2 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -86,7 +86,7 @@ struct region {\n \t((LINE_MAP(index, ptr))->cnt)\n \n #define REC(env, s, l) \\\n-\t(env->xdf##s.recs[l - 1])\n+\t(&env->xdf##s.recs[l - 1])\n \n static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n {\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex af40c88a5b..fd600cbb5d 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \t\tint line_count, long flags)\n {\n \tint i;\n-\txrecord_t **rec1 = xe1->xdf2.recs + i1;\n-\txrecord_t **rec2 = xe2->xdf2.recs + i2;\n+\txrecord_t *rec1 = xe1->xdf2.recs + i1;\n+\txrecord_t *rec2 = xe2->xdf2.recs + i2;\n \n \tfor (i = 0; i < line_count; i++) {\n-\t\tint result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,\n-\t\t\trec2[i]->ptr, rec2[i]->size, flags);\n+\t\tint result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n+\t\t\trec2[i].ptr, rec2[i].size, flags);\n \t\tif (!result)\n \t\t\treturn -1;\n \t}\n@@ -111,7 +111,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \n static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)\n {\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \tint size = 0;\n \n \trecs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n@@ -119,12 +119,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n \tif (count < 1)\n \t\treturn 0;\n \n-\tfor (i = 0; i < count; size += recs[i++]->size)\n+\tfor (i = 0; i < count; size += recs[i++].size)\n \t\tif (dest)\n-\t\t\tmemcpy(dest + size, recs[i]->ptr, recs[i]->size);\n+\t\t\tmemcpy(dest + size, recs[i].ptr, recs[i].size);\n \tif (add_nl) {\n-\t\ti = recs[count - 1]->size;\n-\t\tif (i == 0 || recs[count - 1]->ptr[i - 1] != '\\n') {\n+\t\ti = recs[count - 1].size;\n+\t\tif (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n \t\t\tif (needs_cr) {\n \t\t\t\tif (dest)\n \t\t\t\t\tdest[size] = '\\r';\n@@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)\n \n \tif (i < file->nrec - 1)\n \t\t/* All lines before the last *must* end in LF */\n-\t\treturn (size = file->recs[i]->size) > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\treturn (size = file->recs[i].size) > 1 &&\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!file->nrec)\n \t\t/* Cannot determine eol style from empty file */\n \t\treturn -1;\n-\tif ((size = file->recs[i]->size) &&\n-\t\t\tfile->recs[i]->ptr[size - 1] == '\\n')\n+\tif ((size = file->recs[i].size) &&\n+\t\t\tfile->recs[i].ptr[size - 1] == '\\n')\n \t\t/* Last line; ends in LF; Is it CR/LF? */\n \t\treturn size > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!i)\n \t\t/* The only line has no eol */\n \t\treturn -1;\n \t/* Determine eol from second-to-last line */\n-\treturn (size = file->recs[i - 1]->size) > 1 &&\n-\t\tfile->recs[i - 1]->ptr[size - 2] == '\\r';\n+\treturn (size = file->recs[i - 1].size) > 1 &&\n+\t\tfile->recs[i - 1].ptr[size - 2] == '\\r';\n }\n \n static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)\n@@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\txpparam_t const *xpp)\n {\n-\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n+\txrecord_t *rec1 = xe1->xdf2.recs, *rec2 = xe2->xdf2.recs;\n \tfor (; m; m = m->next) {\n \t\t/* let's handle just the conflicts */\n \t\tif (m->mode)\n \t\t\tcontinue;\n \n \t\twhile(m->chg1 && m->chg2 &&\n-\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n+\t\t      recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t\tm->i1++;\n \t\t\tm->i2++;\n \t\t}\n \t\twhile (m->chg1 && m->chg2 &&\n-\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n-\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n+\t\t       recmatch(&rec1[m->i1 + m->chg1 - 1],\n+\t\t\t\t&rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t}\n@@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t * This probably does not work outside git, since\n \t\t * we have a very simple mmfile structure.\n \t\t */\n-\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;\n-\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr\n-\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;\n-\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;\n-\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr\n-\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;\n+\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n+\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n+\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n+\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n+\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n+\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n \t\tif (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n \t\t\treturn -1;\n \t\tif (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||\n@@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)\n static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n {\n \tfor (; chg; chg--, i++)\n-\t\tif (line_contains_alnum(xe->xdf2.recs[i]->ptr,\n-\t\t\t\txe->xdf2.recs[i]->size))\n+\t\tif (line_contains_alnum(xe->xdf2.recs[i].ptr,\n+\t\t\t\txe->xdf2.recs[i].size))\n \t\t\treturn 1;\n \treturn 0;\n }\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 77dc411d19..bf69a58527 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)\n static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\t\t  int pass)\n {\n-\txrecord_t **records = pass == 1 ?\n+\txrecord_t *records = pass == 1 ?\n \t\tmap->env->xdf1.recs : map->env->xdf2.recs;\n-\txrecord_t *record = records[line - 1];\n+\txrecord_t *record = &records[line - 1];\n \t/*\n \t * After xdl_prepare_env() (or more precisely, due to\n \t * xdl_classify_record()), the \"ha\" member of the records (AKA lines)\n@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\treturn;\n \tmap->entries[index].line1 = line;\n \tmap->entries[index].hash = record->ha;\n-\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);\n+\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n \tif (!map->first)\n \t\tmap->first = map->entries + index;\n \tif (map->last) {\n@@ -246,8 +246,8 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)\n \n static int match(struct hashmap *map, int line1, int line2)\n {\n-\txrecord_t *record1 = map->env->xdf1.recs[line1 - 1];\n-\txrecord_t *record2 = map->env->xdf2.recs[line2 - 1];\n+\txrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n+\txrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n \treturn record1->ha == record2->ha;\n }\n \ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 6f1d4b4725..92f9845003 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -131,7 +131,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n }\n \n \n@@ -146,8 +145,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n \n-\tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n-\t\tgoto abort;\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n@@ -158,12 +155,10 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n \t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n-\t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n-\t\t\t\tgoto abort;\n+\t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\txdf->recs[xdf->nrec++] = crec;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n@@ -263,7 +258,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, nm, nreff, mlim;\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \txdlclass_t *rcrec;\n \tchar *dis, *dis1, *dis2;\n \tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n@@ -276,7 +271,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n \t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -284,7 +279,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n \t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -320,13 +315,13 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n  */\n static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, lim;\n-\txrecord_t **recs1, **recs2;\n+\txrecord_t *recs1, *recs2;\n \n \trecs1 = xdf1->recs;\n \trecs2 = xdf2->recs;\n \tfor (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n \t     i++, recs1++, recs2++)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dstart = xdf2->dstart = i;\n@@ -334,7 +329,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \trecs1 = xdf1->recs + xdf1->nrec - 1;\n \trecs2 = xdf2->recs + xdf2->nrec - 1;\n \tfor (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dend = xdf1->nrec - i - 1;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 85848f1685..3d26cbf1ec 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -45,10 +45,9 @@ typedef struct s_xrecord {\n } xrecord_t;\n \n typedef struct s_xdfile {\n-\tchastore_t rcha;\n+\txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\txrecord_t **recs;\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 444a108f87..332982b509 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tmmfile_t subfile1, subfile2;\n \txdfenv_t env;\n \n-\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;\n-\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +\n-\t\tdiff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;\n-\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;\n-\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +\n-\t\tdiff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;\n+\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n+\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n+\t\tdiff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n+\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n+\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n+\t\tdiff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-- \ngitgitgadget\n\n"},{"id":"525734","messageId":"d1657f51012c8ee97f81bf5e3550aa47a0c5b70c.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 09/17] xdiff: treat xdfile_t.rchg like an enum","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:12Z","receivedAt":"2025-09-07T19:45:34Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nDefine macros NO(0), YES(1), MAYBE(2) as the enum values for rchg to\nmake the code easier to follow. Perhaps 'rchg' should be renamed to\n'changed'?\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiff.h      |  4 ++++\n xdiff/xdiffi.c     | 29 ++++++++++++++---------------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 24 ++++++++++++------------\n 5 files changed, 38 insertions(+), 35 deletions(-)\n\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 2cecde5afe..7092879829 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -27,6 +27,10 @@\n extern \"C\" {\n #endif /* #ifdef __cplusplus */\n \n+#define NO 0\n+#define YES 1\n+#define MAYBE 2\n+\n /* xpparm_t.flags */\n #define XDF_NEED_MINIMAL (1 << 0)\n \ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex a66125d44a..44fd27823a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = YES;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = YES;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -708,7 +708,7 @@ struct xdlgroup {\n static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tg->start = g->end = 0;\n-\twhile (xdf->rchg[g->end])\n+\twhile (xdf->rchg[g->end] == YES)\n \t\tg->end++;\n }\n \n@@ -722,7 +722,7 @@ static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->start = g->end + 1;\n-\tfor (g->end = g->start; xdf->rchg[g->end]; g->end++)\n+\tfor (g->end = g->start; xdf->rchg[g->end] == YES; g->end++)\n \t\t;\n \n \treturn 0;\n@@ -738,7 +738,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->end = g->start - 1;\n-\tfor (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n+\tfor (g->start = g->end; xdf->rchg[g->start - 1] == YES; g->start--)\n \t\t;\n \n \treturn 0;\n@@ -753,10 +753,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->rchg[g->start++] = 0;\n-\t\txdf->rchg[g->end++] = 1;\n+\t\txdf->rchg[g->start++] = NO;\n+\t\txdf->rchg[g->end++] = YES;\n \n-\t\twhile (xdf->rchg[g->end])\n+\t\twhile (xdf->rchg[g->end] == YES)\n \t\t\tg->end++;\n \n \t\treturn 0;\n@@ -774,10 +774,10 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->rchg[--g->start] = 1;\n-\t\txdf->rchg[--g->end] = 0;\n+\t\txdf->rchg[--g->start] = YES;\n+\t\txdf->rchg[--g->end] = NO;\n \n-\t\twhile (xdf->rchg[g->start - 1])\n+\t\twhile (xdf->rchg[g->start - 1] == YES)\n \t\t\tg->start--;\n \n \t\treturn 0;\n@@ -932,16 +932,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n \n int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n \txdchange_t *cscr = NULL, *xch;\n-\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n \tlong i1, i2, l1, l2;\n \n \t/*\n \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n \t */\n \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n-\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n-\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n-\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n+\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n+\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n+\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n \n \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n \t\t\t\txdl_free_script(cscr);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 4d857e8ae2..c2e85b8ab9 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex bf69a58527..20cda5e258 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 92f9845003..36437f91bb 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -215,9 +215,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \t * current line (i) is already a multimatch line.\n \t */\n \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n-\t\tif (!dis[i - r])\n+\t\tif (dis[i - r] == NO)\n \t\t\trdis0++;\n-\t\telse if (dis[i - r] == 2)\n+\t\telse if (dis[i - r] == MAYBE)\n \t\t\trpdis0++;\n \t\telse\n \t\t\tbreak;\n@@ -231,9 +231,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \tif (rdis0 == 0)\n \t\treturn 0;\n \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n-\t\tif (!dis[i + r])\n+\t\tif (dis[i + r] == NO)\n \t\t\trdis1++;\n-\t\telse if (dis[i + r] == 2)\n+\t\telse if (dis[i + r] == MAYBE)\n \t\t\trpdis1++;\n \t\telse\n \t\t\tbreak;\n@@ -273,7 +273,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n-\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tdis1[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n@@ -281,26 +281,26 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n-\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tdis2[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n-\t\tif (dis1[i] == 1 ||\n-\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n+\t\tif (dis1[i] == YES ||\n+\t\t    (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf1->rchg[i] = 1;\n+\t\t\txdf1->rchg[i] = YES;\n \t}\n \txdf1->nreff = nreff;\n \n \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n \t     i <= xdf2->dend; i++, recs++) {\n-\t\tif (dis2[i] == 1 ||\n-\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n+\t\tif (dis2[i] == YES ||\n+\t\t    (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf2->rchg[i] = 1;\n+\t\t\txdf2->rchg[i] = YES;\n \t}\n \txdf2->nreff = nreff;\n \n-- \ngitgitgadget\n\n"},{"id":"525735","messageId":"2a7d5b05c18d4a96f1905b7043d47c62d367cd2a.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 10/17] compat/rust_types.h: define rust primitive types","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:13Z","receivedAt":"2025-09-07T19:45:35Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhy Rust primitive types should be used in C:\n  * Consistency across languages: Sharing the same type names makes it\n    easier to translate and refactor code across boundaries, and search\n    history.\n  * Clarity and ergonomics: The types f32 and f64 are clearer than\n    float and double. The types u64 or isize are easier to write than\n    uint64_t, or ptrdiff_t.\n  * Explicit intent: Inclusion of compat/rust_types.h signals other\n    readers that the code is designed, or being cleaned up, for Rust\n    interop.\n  * Character types: Rust's char is defined as an unsigned 32-bit type.\n    In contrast, C's char is an 8-bit type that is neither signed nor\n    unsigned. The u8 type should be used instead of C's char when\n    referring to bytes in memory.\n  * Keep the FFI boundary precise: When Rust calls into C, the C\n    interface should use Rust types exclusively in both functions and\n    structs. If a broad refactor would cause too much churn, C stub\n    functions may be used as an interim step.\n\nReasons to avoid c_* types (e.g. c_char, c_long) in Rust:\n  * Rust remains precise: Bringing c_* into Rust reintroduces the very\n    ambiguity Rust was designed to eliminate. Using only Rust\n    primitives keeps our code portable and predictable.\n  * One clear contract: Rust should define the interface with precise\n    types. C adapts through compat/rust_types.h, ensuring the boundary\n    is consistent and easy to audit.\n  * Future-proof interop: Other runtimes (Python, Go, Java, Wasm, etc.)\n    map cleanly onto Rust's primitives, but not onto c_*. Sticking with\n    Rust types makes bindings straightforward and avoids locking Git's\n    ABI to C's historical quirks.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n compat/rust_types.h | 28 ++++++++++++++++++++++++++++\n 1 file changed, 28 insertions(+)\n create mode 100644 compat/rust_types.h\n\ndiff --git a/compat/rust_types.h b/compat/rust_types.h\nnew file mode 100644\nindex 0000000000..af93d0a116\n--- /dev/null\n+++ b/compat/rust_types.h\n@@ -0,0 +1,28 @@\n+#ifndef COMPAT_RUST_TYPES_H\n+#define COMPAT_RUST_TYPES_H\n+\n+#include <compat/posix.h>\n+\n+/*\n+ * A typedef for bool is not needed because C bool and Rust bool are\n+ * the same if #include <stdbool.h> is used.\n+ */\n+\n+typedef uint8_t   u8;\n+typedef uint16_t  u16;\n+typedef uint32_t  u32;\n+typedef uint64_t  u64;\n+\n+typedef int8_t    i8;\n+typedef int16_t   i16;\n+typedef int32_t   i32;\n+typedef int64_t   i64;\n+\n+typedef float     f32;\n+typedef double    f64;\n+\n+typedef size_t    usize;\n+typedef ptrdiff_t isize;\n+typedef uint32_t  rust_char;\n+\n+#endif /* COMPAT_RUST_TYPES_H */\n-- \ngitgitgadget\n\n"},{"id":"525736","messageId":"ec54380ed385438a1322ef25afa6e0f4dfa7c711.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 11/17] xdiff: include compat/rust_types.h","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:14Z","receivedAt":"2025-09-07T19:45:36Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xinclude.h | 1 +\n xdiff/xmacros.h  | 2 +-\n xdiff/xtypes.h   | 2 +-\n 3 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/xdiff/xinclude.h b/xdiff/xinclude.h\nindex a4285ac0eb..6733d752a4 100644\n--- a/xdiff/xinclude.h\n+++ b/xdiff/xinclude.h\n@@ -24,6 +24,7 @@\n #define XINCLUDE_H\n \n #include \"git-compat-util.h\"\n+#include <compat/rust_types.h>\n #include \"xmacros.h\"\n #include \"xdiff.h\"\n #include \"xtypes.h\"\ndiff --git a/xdiff/xmacros.h b/xdiff/xmacros.h\nindex 8487bb396f..ef663af3b8 100644\n--- a/xdiff/xmacros.h\n+++ b/xdiff/xmacros.h\n@@ -23,7 +23,7 @@\n #if !defined(XMACROS_H)\n #define XMACROS_H\n \n-\n+#include <compat/rust_types.h>\n \n \n #define XDL_MIN(a, b) ((a) < (b) ? (a): (b))\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 3d26cbf1ec..80afb98bf4 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -23,7 +23,7 @@\n #if !defined(XTYPES_H)\n #define XTYPES_H\n \n-\n+#include <compat/rust_types.h>\n \n typedef struct s_chanode {\n \tstruct s_chanode *next;\n-- \ngitgitgadget\n\n"},{"id":"525737","messageId":"182f93b60bc9d9631d3e38e30c45cd57c8a3c1a9.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 12/17] xdiff: make xrecord_t.ptr a u8 instead of char","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:15Z","receivedAt":"2025-09-07T19:45:37Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c    |  8 ++++----\n xdiff/xemit.c     |  6 +++---\n xdiff/xmerge.c    | 14 +++++++-------\n xdiff/xpatience.c |  2 +-\n xdiff/xprepare.c  |  6 +++---\n xdiff/xtypes.h    |  2 +-\n xdiff/xutils.c    |  4 ++--\n 7 files changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 44fd27823a..370813d2cf 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -407,7 +407,7 @@ static int get_indent(xrecord_t *rec)\n \tint ret = 0;\n \n \tfor (i = 0; i < rec->size; i++) {\n-\t\tchar c = rec->ptr[i];\n+\t\tu8 c = rec->ptr[i];\n \n \t\tif (!XDL_ISSPACE(c))\n \t\t\treturn ret;\n@@ -992,11 +992,11 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n+\t\t\tignore = xdl_blankline((const char *)rec[i].ptr, rec[i].size, flags);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n+\t\t\tignore = xdl_blankline((const char *)rec[i].ptr, rec[i].size, flags);\n \n \t\txch->ignore = ignore;\n \t}\n@@ -1007,7 +1007,7 @@ static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) {\n \tsize_t i;\n \n \tfor (i = 0; i < xpp->ignore_regex_nr; i++)\n-\t\tif (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1,\n+\t\tif (!regexec_buf(xpp->ignore_regex[i], (const char *)rec->ptr, rec->size, 1,\n \t\t\t\t &regmatch, 0))\n \t\t\treturn 1;\n \ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex b2f1f30cd3..ead930088a 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -27,7 +27,7 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *\n {\n \txrecord_t *rec = &xdf->recs[ri];\n \n-\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n+\tif (xdl_emit_diffrec((char const *)rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n \n \treturn 0;\n@@ -113,8 +113,8 @@ static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \txrecord_t *rec = &xdf->recs[ri];\n \n \tif (!xecfg->find_func)\n-\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n-\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n+\t\treturn def_ff((const char *)rec->ptr, rec->size, buf, sz);\n+\treturn xecfg->find_func((const char *)rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n }\n \n static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex fd600cbb5d..75cb3e76a2 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -101,8 +101,8 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \txrecord_t *rec2 = xe2->xdf2.recs + i2;\n \n \tfor (i = 0; i < line_count; i++) {\n-\t\tint result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n-\t\t\trec2[i].ptr, rec2[i].size, flags);\n+\t\tint result = xdl_recmatch((const char *)rec1[i].ptr, rec1[i].size,\n+\t\t\t(const char *)rec2[i].ptr, rec2[i].size, flags);\n \t\tif (!result)\n \t\t\treturn -1;\n \t}\n@@ -324,8 +324,8 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n \n static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n {\n-\treturn xdl_recmatch(rec1->ptr, rec1->size,\n-\t\t\t    rec2->ptr, rec2->size, flags);\n+\treturn xdl_recmatch((const char *)rec1->ptr, rec1->size,\n+\t\t\t    (const char *)rec2->ptr, rec2->size, flags);\n }\n \n /*\n@@ -382,10 +382,10 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t * we have a very simple mmfile structure.\n \t\t */\n \t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n-\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n+\t\tt1.size = (char *)xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n \t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n \t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n-\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n+\t\tt2.size = (char *)xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n \t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n \t\tif (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n \t\t\treturn -1;\n@@ -440,7 +440,7 @@ static int line_contains_alnum(const char *ptr, long size)\n static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n {\n \tfor (; chg; chg--, i++)\n-\t\tif (line_contains_alnum(xe->xdf2.recs[i].ptr,\n+\t\tif (line_contains_alnum((const char *)xe->xdf2.recs[i].ptr,\n \t\t\t\txe->xdf2.recs[i].size))\n \t\t\treturn 1;\n \treturn 0;\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 20cda5e258..9181815fd4 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\treturn;\n \tmap->entries[index].line1 = line;\n \tmap->entries[index].hash = record->ha;\n-\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n+\tmap->entries[index].anchor = is_anchor(xpp, (const char *)map->env->xdf1.recs[line - 1].ptr);\n \tif (!map->first)\n \t\tmap->first = map->entries + index;\n \tif (map->last) {\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 36437f91bb..f5c04afe50 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -96,8 +96,8 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n \t\tif (rcrec->rec.ha == rec->ha &&\n-\t\t\t\txdl_recmatch(rcrec->rec.ptr, rcrec->rec.size,\n-\t\t\t\t\trec->ptr, rec->size, cf->flags))\n+\t\t\t\txdl_recmatch((const char *)rcrec->rec.ptr, rcrec->rec.size,\n+\t\t\t\t\t(const char *)rec->ptr, rec->size, cf->flags))\n \t\t\tbreak;\n \n \tif (!rcrec) {\n@@ -156,7 +156,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n \t\t\tcrec = &xdf->recs[xdf->nrec++];\n-\t\t\tcrec->ptr = prev;\n+\t\t\tcrec->ptr = (u8 const *)prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 80afb98bf4..a1a9a61840 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -39,7 +39,7 @@ typedef struct s_chastore {\n } chastore_t;\n \n typedef struct s_xrecord {\n-\tchar const *ptr;\n+\tu8 const *ptr;\n \tlong size;\n \tunsigned long ha;\n } xrecord_t;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 332982b509..530addf1c6 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -417,10 +417,10 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \txdfenv_t env;\n \n \tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n-\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n+\tsubfile1.size = (char *)diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n \t\tdiff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n \tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n-\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n+\tsubfile2.size = (char *)diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n \t\tdiff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n-- \ngitgitgadget\n\n"},{"id":"525738","messageId":"f7aaef8f36ee8fba80b49c0c1267a8fb9bc513af.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 13/17] xdiff: make xrecord_t.size a usize instead of long","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:16Z","receivedAt":"2025-09-07T19:45:38Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c   |  7 +++----\n xdiff/xemit.c    |  8 ++++----\n xdiff/xmerge.c   | 16 ++++++++--------\n xdiff/xprepare.c |  6 +++---\n xdiff/xtypes.h   |  2 +-\n 5 files changed, 19 insertions(+), 20 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 370813d2cf..45cc9ce116 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -403,10 +403,9 @@ static int recs_match(xrecord_t *rec1, xrecord_t *rec2)\n  */\n static int get_indent(xrecord_t *rec)\n {\n-\tlong i;\n \tint ret = 0;\n \n-\tfor (i = 0; i < rec->size; i++) {\n+\tfor (usize i = 0; i < rec->size; i++) {\n \t\tu8 c = rec->ptr[i];\n \n \t\tif (!XDL_ISSPACE(c))\n@@ -992,11 +991,11 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = xdl_blankline((const char *)rec[i].ptr, rec[i].size, flags);\n+\t\t\tignore = xdl_blankline((const char *)rec[i].ptr, (long)rec[i].size, flags);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = xdl_blankline((const char *)rec[i].ptr, rec[i].size, flags);\n+\t\t\tignore = xdl_blankline((const char *)rec[i].ptr, (long)rec[i].size, flags);\n \n \t\txch->ignore = ignore;\n \t}\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex ead930088a..ad3e859c57 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -27,7 +27,7 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *\n {\n \txrecord_t *rec = &xdf->recs[ri];\n \n-\tif (xdl_emit_diffrec((char const *)rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n+\tif (xdl_emit_diffrec((char const *)rec->ptr, (long)rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n \n \treturn 0;\n@@ -113,8 +113,8 @@ static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \txrecord_t *rec = &xdf->recs[ri];\n \n \tif (!xecfg->find_func)\n-\t\treturn def_ff((const char *)rec->ptr, rec->size, buf, sz);\n-\treturn xecfg->find_func((const char *)rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n+\t\treturn def_ff((const char *)rec->ptr, (long)rec->size, buf, sz);\n+\treturn xecfg->find_func((const char *)rec->ptr, (long)rec->size, buf, sz, xecfg->find_func_priv);\n }\n \n static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n@@ -151,7 +151,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n \txrecord_t *rec = &xdf->recs[ri];\n-\tlong i = 0;\n+\tusize i = 0;\n \n \tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n \ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 75cb3e76a2..c1a003326a 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -101,8 +101,8 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \txrecord_t *rec2 = xe2->xdf2.recs + i2;\n \n \tfor (i = 0; i < line_count; i++) {\n-\t\tint result = xdl_recmatch((const char *)rec1[i].ptr, rec1[i].size,\n-\t\t\t(const char *)rec2[i].ptr, rec2[i].size, flags);\n+\t\tint result = xdl_recmatch((const char *)rec1[i].ptr, (long)rec1[i].size,\n+\t\t\t(const char *)rec2[i].ptr, (long)rec2[i].size, flags);\n \t\tif (!result)\n \t\t\treturn -1;\n \t}\n@@ -119,11 +119,11 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n \tif (count < 1)\n \t\treturn 0;\n \n-\tfor (i = 0; i < count; size += recs[i++].size)\n+\tfor (i = 0; i < count; size += (int)recs[i++].size)\n \t\tif (dest)\n \t\t\tmemcpy(dest + size, recs[i].ptr, recs[i].size);\n \tif (add_nl) {\n-\t\ti = recs[count - 1].size;\n+\t\ti = (int)recs[count - 1].size;\n \t\tif (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n \t\t\tif (needs_cr) {\n \t\t\t\tif (dest)\n@@ -156,7 +156,7 @@ static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int needs_cr, int add_n\n  */\n static int is_eol_crlf(xdfile_t *file, int i)\n {\n-\tlong size;\n+\tusize size;\n \n \tif (i < file->nrec - 1)\n \t\t/* All lines before the last *must* end in LF */\n@@ -324,8 +324,8 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n \n static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n {\n-\treturn xdl_recmatch((const char *)rec1->ptr, rec1->size,\n-\t\t\t    (const char *)rec2->ptr, rec2->size, flags);\n+\treturn xdl_recmatch((const char *)rec1->ptr, (long)rec1->size,\n+\t\t\t    (const char *)rec2->ptr, (long)rec2->size, flags);\n }\n \n /*\n@@ -441,7 +441,7 @@ static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n {\n \tfor (; chg; chg--, i++)\n \t\tif (line_contains_alnum((const char *)xe->xdf2.recs[i].ptr,\n-\t\t\t\txe->xdf2.recs[i].size))\n+\t\t\t\t(long)xe->xdf2.recs[i].size))\n \t\t\treturn 1;\n \treturn 0;\n }\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex f5c04afe50..d62a329d0c 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -96,8 +96,8 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n \t\tif (rcrec->rec.ha == rec->ha &&\n-\t\t\t\txdl_recmatch((const char *)rcrec->rec.ptr, rcrec->rec.size,\n-\t\t\t\t\t(const char *)rec->ptr, rec->size, cf->flags))\n+\t\t\t\txdl_recmatch((const char *)rcrec->rec.ptr, (long)rcrec->rec.size,\n+\t\t\t\t\t(const char *)rec->ptr, (long)rec->size, cf->flags))\n \t\t\tbreak;\n \n \tif (!rcrec) {\n@@ -157,7 +157,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\t\tgoto abort;\n \t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = (u8 const *)prev;\n-\t\t\tcrec->size = (long) (cur - prev);\n+\t\t\tcrec->size = cur - prev;\n \t\t\tcrec->ha = hav;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex a1a9a61840..6f83a9f4ff 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -40,7 +40,7 @@ typedef struct s_chastore {\n \n typedef struct s_xrecord {\n \tu8 const *ptr;\n-\tlong size;\n+\tusize size;\n \tunsigned long ha;\n } xrecord_t;\n \n-- \ngitgitgadget\n\n"},{"id":"525739","messageId":"af96763036e13480ed4e6dfedcade5b2c90e414c.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 14/17] xdiff: split xrecord_t.ha into line_hash and minimal_perfect_hash","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:17Z","receivedAt":"2025-09-07T19:45:40Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     |  4 ++--\n xdiff/xhistogram.c |  4 ++--\n xdiff/xpatience.c  | 10 +++++-----\n xdiff/xprepare.c   | 18 +++++++++---------\n xdiff/xtypes.h     |  3 ++-\n 5 files changed, 20 insertions(+), 19 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 45cc9ce116..c8d351705c 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -24,7 +24,7 @@\n \n static unsigned long get_hash(xdfile_t *xdf, long index)\n {\n-\treturn xdf->recs[xdf->rindex[index]].ha;\n+\treturn xdf->recs[xdf->rindex[index]].minimal_perfect_hash;\n }\n \n #define XDL_MAX_COST_MIN 256\n@@ -385,7 +385,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,\n \n static int recs_match(xrecord_t *rec1, xrecord_t *rec2)\n {\n-\treturn (rec1->ha == rec2->ha);\n+\treturn (rec1->minimal_perfect_hash == rec2->minimal_perfect_hash);\n }\n \n /*\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex c2e85b8ab9..4c827b0cba 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -90,7 +90,7 @@ struct region {\n \n static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n {\n-\treturn r1->ha == r2->ha;\n+\treturn r1->minimal_perfect_hash == r2->minimal_perfect_hash;\n \n }\n \n@@ -98,7 +98,7 @@ static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n \t(cmp_recs(REC(i->env, s1, l1), REC(i->env, s2, l2)))\n \n #define TABLE_HASH(index, side, line) \\\n-\tXDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)\n+\tXDL_HASHLONG((REC(index->env, side, line))->minimal_perfect_hash, index->table_bits)\n \n static int scanA(struct histindex *index, int line1, int count1)\n {\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 9181815fd4..e400d85072 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -48,7 +48,7 @@\n struct hashmap {\n \tint nr, alloc;\n \tstruct entry {\n-\t\tunsigned long hash;\n+\t\tusize minimal_perfect_hash;\n \t\t/*\n \t\t * 0 = unused entry, 1 = first line, 2 = second, etc.\n \t\t * line2 is NON_UNIQUE if the line is not unique\n@@ -101,10 +101,10 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t * So we multiply ha by 2 in the hope that the hashing was\n \t * \"unique enough\".\n \t */\n-\tint index = (int)((record->ha << 1) % map->alloc);\n+\tint index = (int)((record->minimal_perfect_hash << 1) % map->alloc);\n \n \twhile (map->entries[index].line1) {\n-\t\tif (map->entries[index].hash != record->ha) {\n+\t\tif (map->entries[index].minimal_perfect_hash != record->minimal_perfect_hash) {\n \t\t\tif (++index >= map->alloc)\n \t\t\t\tindex = 0;\n \t\t\tcontinue;\n@@ -120,7 +120,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \tif (pass == 2)\n \t\treturn;\n \tmap->entries[index].line1 = line;\n-\tmap->entries[index].hash = record->ha;\n+\tmap->entries[index].minimal_perfect_hash = record->minimal_perfect_hash;\n \tmap->entries[index].anchor = is_anchor(xpp, (const char *)map->env->xdf1.recs[line - 1].ptr);\n \tif (!map->first)\n \t\tmap->first = map->entries + index;\n@@ -248,7 +248,7 @@ static int match(struct hashmap *map, int line1, int line2)\n {\n \txrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n \txrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n-\treturn record1->ha == record2->ha;\n+\treturn record1->minimal_perfect_hash == record2->minimal_perfect_hash;\n }\n \n static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex d62a329d0c..9ec2a5d078 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -93,9 +93,9 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \tlong hi;\n \txdlclass_t *rcrec;\n \n-\thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n+\thi = (long) XDL_HASHLONG(rec->line_hash, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n-\t\tif (rcrec->rec.ha == rec->ha &&\n+\t\tif (rcrec->rec.line_hash == rec->line_hash &&\n \t\t\t\txdl_recmatch((const char *)rcrec->rec.ptr, (long)rcrec->rec.size,\n \t\t\t\t\t(const char *)rec->ptr, (long)rec->size, cf->flags))\n \t\t\tbreak;\n@@ -111,7 +111,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \t\tcf->rcrecs[rcrec->idx] = rcrec;\n \t\trcrec->rec.ptr = rec->ptr;\n \t\trcrec->rec.size = rec->size;\n-\t\trcrec->rec.ha = rec->ha;\n+\t\trcrec->rec.line_hash = rec->line_hash;\n \t\trcrec->len1 = rcrec->len2 = 0;\n \t\trcrec->next = cf->rchash[hi];\n \t\tcf->rchash[hi] = rcrec;\n@@ -119,7 +119,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \n \t(pass == 1) ? rcrec->len1++ : rcrec->len2++;\n \n-\trec->ha = (unsigned long) rcrec->idx;\n+\trec->minimal_perfect_hash = (unsigned long) rcrec->idx;\n \n \treturn 0;\n }\n@@ -158,7 +158,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = (u8 const *)prev;\n \t\t\tcrec->size = cur - prev;\n-\t\t\tcrec->ha = hav;\n+\t\t\tcrec->line_hash = hav;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n@@ -271,7 +271,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[recs->ha];\n+\t\trcrec = cf->rcrecs[recs->minimal_perfect_hash];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n \t\tdis1[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n@@ -279,7 +279,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[recs->ha];\n+\t\trcrec = cf->rcrecs[recs->minimal_perfect_hash];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n \t\tdis2[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n@@ -321,7 +321,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \trecs2 = xdf2->recs;\n \tfor (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n \t     i++, recs1++, recs2++)\n-\t\tif (recs1->ha != recs2->ha)\n+\t\tif (recs1->minimal_perfect_hash != recs2->minimal_perfect_hash)\n \t\t\tbreak;\n \n \txdf1->dstart = xdf2->dstart = i;\n@@ -329,7 +329,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \trecs1 = xdf1->recs + xdf1->nrec - 1;\n \trecs2 = xdf2->recs + xdf2->nrec - 1;\n \tfor (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n-\t\tif (recs1->ha != recs2->ha)\n+\t\tif (recs1->minimal_perfect_hash != recs2->minimal_perfect_hash)\n \t\t\tbreak;\n \n \txdf1->dend = xdf1->nrec - i - 1;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 6f83a9f4ff..f2b53a6553 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -41,7 +41,8 @@ typedef struct s_chastore {\n typedef struct s_xrecord {\n \tu8 const *ptr;\n \tusize size;\n-\tunsigned long ha;\n+\tu64 line_hash;\n+\tusize minimal_perfect_hash;\n } xrecord_t;\n \n typedef struct s_xdfile {\n-- \ngitgitgadget\n\n"},{"id":"525740","messageId":"0a180f69ff3cd4ac9efdee9546613df883cba9fe.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 15/17] xdiff: make xdfile_t.nrec a usize instead of long","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:18Z","receivedAt":"2025-09-07T19:45:41Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c   |  8 ++++----\n xdiff/xemit.c    | 14 +++++++-------\n xdiff/xmerge.c   |  4 ++--\n xdiff/xprepare.c |  6 +++---\n xdiff/xtypes.h   |  2 +-\n 5 files changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex c8d351705c..ee72f5ea3b 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -483,7 +483,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n {\n \tlong i;\n \n-\tif (split >= xdf->nrec) {\n+\tif (split >= (long)xdf->nrec) {\n \t\tm->end_of_file = 1;\n \t\tm->indent = -1;\n \t} else {\n@@ -506,7 +506,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n \n \tm->post_blank = 0;\n \tm->post_indent = -1;\n-\tfor (i = split + 1; i < xdf->nrec; i++) {\n+\tfor (i = split + 1; i < (long)xdf->nrec; i++) {\n \t\tm->post_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->post_indent != -1)\n \t\t\tbreak;\n@@ -717,7 +717,7 @@ static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n  */\n static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n {\n-\tif (g->end == xdf->nrec)\n+\tif (g->end == (long)xdf->nrec)\n \t\treturn -1;\n \n \tg->start = g->end + 1;\n@@ -750,7 +750,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n  */\n static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n-\tif (g->end < xdf->nrec &&\n+\tif (g->end < (long)xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n \t\txdf->rchg[g->start++] = NO;\n \t\txdf->rchg[g->end++] = YES;\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex ad3e859c57..aa63aab749 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -137,7 +137,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \tbuf = func_line ? func_line->buf : dummy;\n \tsize = func_line ? sizeof(func_line->buf) : sizeof(dummy);\n \n-\tfor (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {\n+\tfor (l = start; l != limit && 0 <= l && l < (long)xe->xdf1.nrec; l += step) {\n \t\tlong len = match_func_rec(&xe->xdf1, xecfg, l, buf, size);\n \t\tif (len >= 0) {\n \t\t\tif (func_line)\n@@ -179,14 +179,14 @@ pre_context_calculation:\n \t\t\tlong fs1, i1 = xch->i1;\n \n \t\t\t/* Appended chunk? */\n-\t\t\tif (i1 >= xe->xdf1.nrec) {\n+\t\t\tif (i1 >= (long)xe->xdf1.nrec) {\n \t\t\t\tlong i2 = xch->i2;\n \n \t\t\t\t/*\n \t\t\t\t * We don't need additional context if\n \t\t\t\t * a whole function was added.\n \t\t\t\t */\n-\t\t\t\twhile (i2 < xe->xdf2.nrec) {\n+\t\t\t\twhile (i2 < (long)xe->xdf2.nrec) {\n \t\t\t\t\tif (is_func_rec(&xe->xdf2, xecfg, i2))\n \t\t\t\t\t\tgoto post_context_calculation;\n \t\t\t\t\ti2++;\n@@ -228,8 +228,8 @@ pre_context_calculation:\n \n  post_context_calculation:\n \t\tlctx = xecfg->ctxlen;\n-\t\tlctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));\n-\t\tlctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));\n+\t\tlctx = XDL_MIN(lctx, (long)xe->xdf1.nrec - (xche->i1 + xche->chg1));\n+\t\tlctx = XDL_MIN(lctx, (long)xe->xdf2.nrec - (xche->i2 + xche->chg2));\n \n \t\te1 = xche->i1 + xche->chg1 + lctx;\n \t\te2 = xche->i2 + xche->chg2 + lctx;\n@@ -243,7 +243,7 @@ pre_context_calculation:\n \t\t\tif (fe1 < 0)\n \t\t\t\tfe1 = xe->xdf1.nrec;\n \t\t\tif (fe1 > e1) {\n-\t\t\t\te2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec);\n+\t\t\t\te2 = XDL_MIN(e2 + (fe1 - e1), (long)xe->xdf2.nrec);\n \t\t\t\te1 = fe1;\n \t\t\t}\n \n@@ -254,7 +254,7 @@ pre_context_calculation:\n \t\t\t */\n \t\t\tif (xche->next) {\n \t\t\t\tlong l = XDL_MIN(xche->next->i1,\n-\t\t\t\t\t\t xe->xdf1.nrec - 1);\n+\t\t\t\t\t\t (long)xe->xdf1.nrec - 1);\n \t\t\t\tif (l - xecfg->ctxlen <= e1 ||\n \t\t\t\t    get_func_line(xe, xecfg, NULL, l, e1) < 0) {\n \t\t\t\t\txche = xche->next;\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex c1a003326a..1ebcbb4e3a 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -158,7 +158,7 @@ static int is_eol_crlf(xdfile_t *file, int i)\n {\n \tusize size;\n \n-\tif (i < file->nrec - 1)\n+\tif (i < (long)file->nrec - 1)\n \t\t/* All lines before the last *must* end in LF */\n \t\treturn (size = file->recs[i].size) > 1 &&\n \t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n@@ -622,7 +622,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \t\t\tchanges = c;\n \t\ti0 = xscr1->i1;\n \t\ti1 = xscr1->i2;\n-\t\ti2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;\n+\t\ti2 = xscr1->i1 + (long)xe2->xdf2.nrec - (long)xe2->xdf1.nrec;\n \t\tchg0 = xscr1->chg1;\n \t\tchg1 = xscr1->chg2;\n \t\tchg2 = xscr1->chg1;\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 9ec2a5d078..d990fe1c9e 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -153,7 +153,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\tfor (top = blk + bsize; cur < top; ) {\n \t\t\tprev = cur;\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n-\t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n+\t\t\tif (XDL_ALLOC_GROW(xdf->recs, (long)xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n \t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = (u8 const *)prev;\n@@ -332,8 +332,8 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \t\tif (recs1->minimal_perfect_hash != recs2->minimal_perfect_hash)\n \t\t\tbreak;\n \n-\txdf1->dend = xdf1->nrec - i - 1;\n-\txdf2->dend = xdf2->nrec - i - 1;\n+\txdf1->dend = (long)xdf1->nrec - i - 1;\n+\txdf2->dend = (long)xdf2->nrec - i - 1;\n \n \treturn 0;\n }\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex f2b53a6553..41986c6603 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -47,7 +47,7 @@ typedef struct s_xrecord {\n \n typedef struct s_xdfile {\n \txrecord_t *recs;\n-\tlong nrec;\n+\tusize nrec;\n \tlong dstart, dend;\n \tchar *rchg;\n \tlong *rindex;\n-- \ngitgitgadget\n\n"},{"id":"525741","messageId":"f4eda35e2484d0702ea86cea39de2e62a5988d4a.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 16/17] xdiff: make xdfile_t.nreff a usize instead of long","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:19Z","receivedAt":"2025-09-07T19:45:42Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 14 +++++++-------\n xdiff/xtypes.h   |  2 +-\n 2 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex d990fe1c9e..83355f036e 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -257,7 +257,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n  * might be potentially discarded if they happear in a run of discardable.\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n-\tlong i, nm, nreff, mlim;\n+\tlong i, nm, mlim;\n \txrecord_t *recs;\n \txdlclass_t *rcrec;\n \tchar *dis, *dis1, *dis2;\n@@ -284,25 +284,25 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\tdis2[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n-\tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n+\txdf1->nreff = 0;\n+\tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (dis1[i] == YES ||\n \t\t    (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n-\t\t\txdf1->rindex[nreff++] = i;\n+\t\t\txdf1->rindex[xdf1->nreff++] = i;\n \t\t} else\n \t\t\txdf1->rchg[i] = YES;\n \t}\n-\txdf1->nreff = nreff;\n \n-\tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n+\txdf2->nreff = 0;\n+\tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n \t     i <= xdf2->dend; i++, recs++) {\n \t\tif (dis2[i] == YES ||\n \t\t    (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n-\t\t\txdf2->rindex[nreff++] = i;\n+\t\t\txdf2->rindex[xdf2->nreff++] = i;\n \t\t} else\n \t\t\txdf2->rchg[i] = YES;\n \t}\n-\txdf2->nreff = nreff;\n \n \txdl_free(dis);\n \ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 41986c6603..070674d7c4 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -51,7 +51,7 @@ typedef struct s_xdfile {\n \tlong dstart, dend;\n \tchar *rchg;\n \tlong *rindex;\n-\tlong nreff;\n+\tusize nreff;\n } xdfile_t;\n \n typedef struct s_xdfenv {\n-- \ngitgitgadget\n\n"},{"id":"525742","messageId":"00401e775a110f4713c3f6aa72a54476ec699aa9.1757274320.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH 17/17] xdiff: change the types of dstart, dend, rchg, and rindex in xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-07T19:45:20Z","receivedAt":"2025-09-07T19:45:43Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xtypes.h | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 070674d7c4..08301bf932 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -48,9 +48,9 @@ typedef struct s_xrecord {\n typedef struct s_xdfile {\n \txrecord_t *recs;\n \tusize nrec;\n-\tlong dstart, dend;\n-\tchar *rchg;\n-\tlong *rindex;\n+\ti32 dstart, dend;\n+\tu8 *rchg;\n+\tusize *rindex;\n \tusize nreff;\n } xdfile_t;\n \n-- \ngitgitgadget\n"},{"id":"525830","messageId":"xmqqjz29x9r1.fsf@gitster.g","threadId":"64108","inReplyTo":"2a7d5b05c18d4a96f1905b7043d47c62d367cd2a.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 10/17] compat/rust_types.h: define rust primitive types","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-08T15:08:18Z","receivedAt":"2025-09-08T15:08:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +/*\n> + * A typedef for bool is not needed because C bool and Rust bool are\n> + * the same if #include <stdbool.h> is used.\n> + */\n> +\n> +typedef uint8_t   u8;\n> +typedef uint16_t  u16;\n> +typedef uint32_t  u32;\n> +typedef uint64_t  u64;\n> +\n> +typedef int8_t    i8;\n> +typedef int16_t   i16;\n> +typedef int32_t   i32;\n> +typedef int64_t   i64;\n\nThe standard guarantees that these are all of the above are exactly\nN-bits wide, so I can buy the above types.  But before I can buy the\nabove typedefs, don't we need to rename existing variables that\nsquat on these names?\n\n    $ git grep -n -E -e '\\<[ui](8|16|32|64)\\>'\n\ngives some hits, like\n\n    reftable/record.c:678:\tuint8_t i64[8];\n    t/helper/test-parse-options.c:123:\tuint16_t u16 = 0;\n    t/helper/test-parse-options.c:148:\t\tOPT_UNSIGNED(0, \"u16\", &u16, \"get a 16 bit unsigned integer\"),\n\nto avoid confusion?  There are handful other hits.\n\n> +typedef float     f32;\n> +typedef double    f64;\n\nIt may be that they can be used interchangeably in practice on\npopular platforms, but are these guaranteed to be equivalent by some\nstandard?  C only cares about the minimum required range and\nprecision, so you may have allocated enough bytes thinking you can\nfit a f32 but your float may not fit there.\n\nOr does Rust care only about platforms with IEEE 754 and would\nrefuse to port to other exotic architectures so the above worries\nwould not apply?\n\n> +typedef size_t    usize;\n> +typedef ptrdiff_t isize;\n\nInteresting.  I would have expected, \"isize\" that is a signed\nvariant of \"usize\" to be aliased out of ssize_t (simply because it\nis declared that \"usize\" corresponds to \"size_t\" on the previous\nline), not using ptrdiff_t.\n\n> +typedef uint32_t  rust_char;\n> +\n> +#endif /* COMPAT_RUST_TYPES_H */\n"},{"id":"525851","messageId":"CAH=ZcbByi8ouj7mcHpOGtVYnHSq_E5_cFXiuiXJmLP-=4nrsvg@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqjz29x9r1.fsf@gitster.g","subject":"Re: [PATCH 10/17] compat/rust_types.h: define rust primitive types","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-08T16:15:30Z","receivedAt":"2025-09-08T16:15:44Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Mon, Sep 8, 2025 at 9:08 AM Junio C Hamano <gitster@pobox.com> wrote:\n> The standard guarantees that these are all of the above are exactly\n> N-bits wide, so I can buy the above types.  But before I can buy the\n> above typedefs, don't we need to rename existing variables that\n> squat on these names?\n>\n>     $ git grep -n -E -e '\\<[ui](8|16|32|64)\\>'\n>\n> gives some hits, like\n>\n>     reftable/record.c:678:      uint8_t i64[8];\n>     t/helper/test-parse-options.c:123:  uint16_t u16 = 0;\n>     t/helper/test-parse-options.c:148:          OPT_UNSIGNED(0, \"u16\", &u16, \"get a 16 bit unsigned integer\"),\n>\n> to avoid confusion?  There are handful other hits.\n\nThose places should be cleaned up, but it's not an immediate problem\nbecause compat/rust_types.h is not auto included anywhere. These\ntypedefs live in git-compat-util.h in my \"Introduce Rust\" patch series\nand I never had a problem with compilation or testing. One reason I\nincluded compat/posix.h instead of git-compat-util.h is because it\nincludes stdbool.h where git-compat-util.h doesn't. I'll be happy to\ndo that cleanup.\n\n> > +typedef float     f32;\n> > +typedef double    f64;\n>\n> It may be that they can be used interchangeably in practice on\n> popular platforms, but are these guaranteed to be equivalent by some\n> standard?  C only cares about the minimum required range and\n> precision, so you may have allocated enough bytes thinking you can\n> fit a f32 but your float may not fit there.\n>\n> Or does Rust care only about platforms with IEEE 754 and would\n> refuse to port to other exotic architectures so the above worries\n> would not apply?\n\nIf the typedefs in compat/rust_types.h are incorrect for a\nplatform/target then compat/posix.h or compat/rust_types.h should be\nupdated rather than relying on Rust's core::ffi's guess as what it is.\n\nOn the Rust side: core::ffi assumes that a C 'float' is the same as\nf32 and that a C 'double' is an f64[1,2,3]. So I am making the same\nassumption that the Rust maintainers are _and_ I'm keeping ambiguity\non the C side rather than eroding Rust's type precision. If a platform\ndoes not follow these assumptions then the compat/rust_types.h should\nwarn those using that platform or fail to build entirely.\n\n[1] https://doc.rust-lang.org/1.89.0/core/ffi/type.c_float.html\n[2] https://doc.rust-lang.org/1.63.0/src/core/ffi/mod.rs.html#80-81\n[3] https://doc.rust-lang.org/1.89.0/src/core/ffi/primitives.rs.html#34-35\n"},{"id":"525927","messageId":"CABPp-BGn7LYJKh3Nvmb_UOCnprSw13NHApwqvC0G92DBYnOuAw@mail.gmail.com","threadId":"64108","inReplyTo":"9cf9d09c079060bb03556db270a3100ca62c6ba0.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 01/17] xdiff: delete static forward declarations in xprepare","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:55:17Z","receivedAt":"2025-09-09T08:55:30Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:45 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> Move xdl_prepare_env() later in the file to avoid the need\n> for static forward declarations.\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xprepare.c | 116 ++++++++++++++++++++---------------------------\n>  1 file changed, 50 insertions(+), 66 deletions(-)\n>\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index e1d4017b2d..a45c5ee208 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -53,21 +53,6 @@ typedef struct s_xdlclassifier {\n>\n>\n>\n> -static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);\n> -static void xdl_free_classifier(xdlclassifier_t *cf);\n> -static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n> -                              unsigned int hbits, xrecord_t *rec);\n> -static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n> -                          xdlclassifier_t *cf, xdfile_t *xdf);\n> -static void xdl_free_ctx(xdfile_t *xdf);\n> -static int xdl_clean_mmatch(char const *dis, long i, long s, long e);\n> -static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n> -static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);\n> -static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n> -\n> -\n> -\n> -\n>  static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {\n>         cf->flags = flags;\n>\n> @@ -242,57 +227,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {\n>  }\n>\n>\n> -int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n> -                   xdfenv_t *xe) {\n> -       long enl1, enl2, sample;\n> -       xdlclassifier_t cf;\n> -\n> -       memset(&cf, 0, sizeof(cf));\n> -\n> -       /*\n> -        * For histogram diff, we can afford a smaller sample size and\n> -        * thus a poorer estimate of the number of lines, as the hash\n> -        * table (rhash) won't be filled up/grown. The number of lines\n> -        * (nrecs) will be updated correctly anyway by\n> -        * xdl_prepare_ctx().\n> -        */\n> -       sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n> -                 ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n> -\n> -       enl1 = xdl_guess_lines(mf1, sample) + 1;\n> -       enl2 = xdl_guess_lines(mf2, sample) + 1;\n> -\n> -       if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n> -               return -1;\n> -\n> -       if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n> -\n> -               xdl_free_classifier(&cf);\n> -               return -1;\n> -       }\n> -       if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n> -\n> -               xdl_free_ctx(&xe->xdf1);\n> -               xdl_free_classifier(&cf);\n> -               return -1;\n> -       }\n> -\n> -       if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n> -           (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n> -           xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n> -\n> -               xdl_free_ctx(&xe->xdf2);\n> -               xdl_free_ctx(&xe->xdf1);\n> -               xdl_free_classifier(&cf);\n> -               return -1;\n> -       }\n> -\n> -       xdl_free_classifier(&cf);\n> -\n> -       return 0;\n> -}\n> -\n> -\n>  void xdl_free_env(xdfenv_t *xe) {\n>\n>         xdl_free_ctx(&xe->xdf2);\n> @@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2\n>\n>         return 0;\n>  }\n> +\n> +int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n> +                   xdfenv_t *xe) {\n> +       long enl1, enl2, sample;\n> +       xdlclassifier_t cf;\n> +\n> +       memset(&cf, 0, sizeof(cf));\n> +\n> +       /*\n> +        * For histogram diff, we can afford a smaller sample size and\n> +        * thus a poorer estimate of the number of lines, as the hash\n> +        * table (rhash) won't be filled up/grown. The number of lines\n> +        * (nrecs) will be updated correctly anyway by\n> +        * xdl_prepare_ctx().\n> +        */\n> +       sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n> +                 ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n> +\n> +       enl1 = xdl_guess_lines(mf1, sample) + 1;\n> +       enl2 = xdl_guess_lines(mf2, sample) + 1;\n> +\n> +       if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n> +               return -1;\n> +\n> +       if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n> +\n> +               xdl_free_classifier(&cf);\n> +               return -1;\n> +       }\n> +       if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n> +\n> +               xdl_free_ctx(&xe->xdf1);\n> +               xdl_free_classifier(&cf);\n> +               return -1;\n> +       }\n> +\n> +       if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n> +           (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n> +           xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n> +\n> +               xdl_free_ctx(&xe->xdf2);\n> +               xdl_free_ctx(&xe->xdf1);\n> +               xdl_free_classifier(&cf);\n> +               return -1;\n> +           }\n> +\n> +       xdl_free_classifier(&cf);\n> +\n> +       return 0;\n> +}\n> --\n> gitgitgadget\n\nViewing this with --color-moved makes it clear that the changes\nexactly match what you summarize in the commit message.\n"},{"id":"525928","messageId":"CABPp-BHF38TTwzsgJ215GGrz1Za-m1qMX0u4BY7P_UTzOATuaQ@mail.gmail.com","threadId":"64108","inReplyTo":"15832ad2715d377ed9b0c9f3de66cc4532cac2ba.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 02/17] xdiff: delete local variables and initialize/free xdfile_t directly","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:56:05Z","receivedAt":"2025-09-09T08:56:17Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:48 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> xdl_prepare_ctx() uses local variables and assigns them to the\n> corresponding xdfile_t fields if there are no errors. Delete them and\n> use the fields of xdfile_t directly.\n\nIn particular, those local variables are essentially a hand-rolled\nadditional implementation of xdl_free_ctx() inlined into\nxdl_prepare_ctx().  You're just modifying the code to use the existing\nxdl_free_ctx() function so we don't have two ways to free such\nvariables (especially since one of those two was an ugly inlining).\n\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xprepare.c | 79 +++++++++++++++++++-----------------------------\n>  1 file changed, 31 insertions(+), 48 deletions(-)\n>\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index a45c5ee208..2ed1785b09 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -134,99 +134,82 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n>  }\n>\n>\n> +static void xdl_free_ctx(xdfile_t *xdf)\n> +{\n> +\n\nunnecessary blank line here\n\n> +       xdl_free(xdf->rhash);\n> +       xdl_free(xdf->rindex);\n> +       xdl_free(xdf->rchg - 1);\n> +       xdl_free(xdf->ha);\n> +       xdl_free(xdf->recs);\n> +       xdl_cha_free(&xdf->rcha);\n> +}\n> +\n> +\n>  static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n>                            xdlclassifier_t *cf, xdfile_t *xdf) {\n> -       unsigned int hbits;\n> -       long nrec, hsize, bsize;\n> +       long bsize;\n>         unsigned long hav;\n>         char const *blk, *cur, *top, *prev;\n>         xrecord_t *crec;\n> -       xrecord_t **recs;\n> -       xrecord_t **rhash;\n> -       unsigned long *ha;\n> -       char *rchg;\n> -       long *rindex;\n>\n> -       ha = NULL;\n> -       rindex = NULL;\n> -       rchg = NULL;\n> -       rhash = NULL;\n> -       recs = NULL;\n> +       xdf->ha = NULL;\n> +       xdf->rindex = NULL;\n> +       xdf->rchg = NULL;\n> +       xdf->rhash = NULL;\n> +       xdf->recs = NULL;\n>\n>         if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n>                 goto abort;\n> -       if (!XDL_ALLOC_ARRAY(recs, narec))\n> +       if (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n>                 goto abort;\n>\n> -       hbits = xdl_hashbits((unsigned int) narec);\n> -       hsize = 1 << hbits;\n> -       if (!XDL_CALLOC_ARRAY(rhash, hsize))\n> +       xdf->hbits = xdl_hashbits((unsigned int) narec);\n> +       if (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n>                 goto abort;\n>\n> -       nrec = 0;\n> +       xdf->nrec = 0;\n>         if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n>                 for (top = blk + bsize; cur < top; ) {\n>                         prev = cur;\n>                         hav = xdl_hash_record(&cur, top, xpp->flags);\n> -                       if (XDL_ALLOC_GROW(recs, nrec + 1, narec))\n> +                       if (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n>                                 goto abort;\n>                         if (!(crec = xdl_cha_alloc(&xdf->rcha)))\n>                                 goto abort;\n>                         crec->ptr = prev;\n>                         crec->size = (long) (cur - prev);\n>                         crec->ha = hav;\n> -                       recs[nrec++] = crec;\n> -                       if (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)\n> +                       xdf->recs[xdf->nrec++] = crec;\n> +                       if (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n>                                 goto abort;\n>                 }\n>         }\n>\n> -       if (!XDL_CALLOC_ARRAY(rchg, nrec + 2))\n> +       if (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n>                 goto abort;\n>\n>         if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n>             (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n> -               if (!XDL_ALLOC_ARRAY(rindex, nrec + 1))\n> +               if (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n>                         goto abort;\n> -               if (!XDL_ALLOC_ARRAY(ha, nrec + 1))\n> +               if (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n>                         goto abort;\n>         }\n>\n> -       xdf->nrec = nrec;\n> -       xdf->recs = recs;\n> -       xdf->hbits = hbits;\n> -       xdf->rhash = rhash;\n> -       xdf->rchg = rchg + 1;\n> -       xdf->rindex = rindex;\n> +       xdf->rchg += 1;\n>         xdf->nreff = 0;\n> -       xdf->ha = ha;\n>         xdf->dstart = 0;\n> -       xdf->dend = nrec - 1;\n> +       xdf->dend = xdf->nrec - 1;\n>\n>         return 0;\n>\n>  abort:\n> -       xdl_free(ha);\n> -       xdl_free(rindex);\n> -       xdl_free(rchg);\n> -       xdl_free(rhash);\n> -       xdl_free(recs);\n> -       xdl_cha_free(&xdf->rcha);\n> +       xdl_free_ctx(xdf);\n>         return -1;\n>  }\n>\n>\n> -static void xdl_free_ctx(xdfile_t *xdf) {\n> -\n> -       xdl_free(xdf->rhash);\n> -       xdl_free(xdf->rindex);\n> -       xdl_free(xdf->rchg - 1);\n> -       xdl_free(xdf->ha);\n> -       xdl_free(xdf->recs);\n> -       xdl_cha_free(&xdf->rcha);\n> -}\n> -\n> -\n>  void xdl_free_env(xdfenv_t *xe) {\n>\n>         xdl_free_ctx(&xe->xdf2);\n> --\n> gitgitgadget\n\nLooks good.\n"},{"id":"525929","messageId":"CABPp-BHWMZ4-T_DfOnS4AP-5qANjtdxasFojicy3w1ZTdoETBQ@mail.gmail.com","threadId":"64108","inReplyTo":"7d5e387916eaf4afd40afc85b1678306b8a5f076.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 03/17] xdiff: delete unnecessary fields from xrecord_t and xdfile_t","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:56:15Z","receivedAt":"2025-09-09T08:56:27Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:45 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> xrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,\n> but never used for anything by the code. Remove them.\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xprepare.c | 15 ++-------------\n>  xdiff/xtypes.h   |  3 ---\n>  2 files changed, 2 insertions(+), 16 deletions(-)\n>\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index 2ed1785b09..91b0ed54e0 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -91,8 +91,7 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n>  }\n>\n>\n> -static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n> -                              unsigned int hbits, xrecord_t *rec) {\n> +static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n>         long hi;\n>         char const *line;\n>         xdlclass_t *rcrec;\n> @@ -126,10 +125,6 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n>\n>         rec->ha = (unsigned long) rcrec->idx;\n>\n> -       hi = (long) XDL_HASHLONG(rec->ha, hbits);\n> -       rec->next = rhash[hi];\n> -       rhash[hi] = rec;\n> -\n>         return 0;\n>  }\n>\n> @@ -137,7 +132,6 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n>  static void xdl_free_ctx(xdfile_t *xdf)\n>  {\n>\n> -       xdl_free(xdf->rhash);\n>         xdl_free(xdf->rindex);\n>         xdl_free(xdf->rchg - 1);\n>         xdl_free(xdf->ha);\n> @@ -156,7 +150,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>         xdf->ha = NULL;\n>         xdf->rindex = NULL;\n>         xdf->rchg = NULL;\n> -       xdf->rhash = NULL;\n>         xdf->recs = NULL;\n>\n>         if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n> @@ -164,10 +157,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>         if (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n>                 goto abort;\n>\n> -       xdf->hbits = xdl_hashbits((unsigned int) narec);\n> -       if (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n> -               goto abort;\n> -\n>         xdf->nrec = 0;\n>         if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n>                 for (top = blk + bsize; cur < top; ) {\n> @@ -181,7 +170,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>                         crec->size = (long) (cur - prev);\n>                         crec->ha = hav;\n>                         xdf->recs[xdf->nrec++] = crec;\n> -                       if (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n> +                       if (xdl_classify_record(pass, cf, crec) < 0)\n>                                 goto abort;\n>                 }\n>         }\n> diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\n> index 8442bd436e..8b8467360e 100644\n> --- a/xdiff/xtypes.h\n> +++ b/xdiff/xtypes.h\n> @@ -39,7 +39,6 @@ typedef struct s_chastore {\n>  } chastore_t;\n>\n>  typedef struct s_xrecord {\n> -       struct s_xrecord *next;\n>         char const *ptr;\n>         long size;\n>         unsigned long ha;\n> @@ -48,8 +47,6 @@ typedef struct s_xrecord {\n>  typedef struct s_xdfile {\n>         chastore_t rcha;\n>         long nrec;\n> -       unsigned int hbits;\n> -       xrecord_t **rhash;\n>         long dstart, dend;\n>         xrecord_t **recs;\n>         char *rchg;\n> --\n> gitgitgadget\n\nAlways nice to see unused fields get removed.\n"},{"id":"525930","messageId":"CABPp-BFNSj5rLYqYXCbqmg-kKwfV8BewjoCx+7j3vhT6waojFw@mail.gmail.com","threadId":"64108","inReplyTo":"ab17d8c23f0ca48374416ec1eb34128585795736.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 04/17] xdiff: delete xdl_get_rec() in xemit","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:56:41Z","receivedAt":"2025-09-09T08:56:54Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:45 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> This function aliases the fields of xrecord_t, which makes it harder\n> to track the usages of those fields. Delete it.\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xemit.c | 38 +++++++++++++-------------------------\n>  1 file changed, 13 insertions(+), 25 deletions(-)\n>\n> diff --git a/xdiff/xemit.c b/xdiff/xemit.c\n> index 1d40c9cb40..2161ac3cd0 100644\n> --- a/xdiff/xemit.c\n> +++ b/xdiff/xemit.c\n> @@ -22,23 +22,13 @@\n>\n>  #include \"xinclude.h\"\n>\n> -static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n>\n\nCan we remove this line too, to simplify the diff?  (i.e. make there\nonly be one blank line between the include of xinclude.h and\nxdl_emit_record?\n\n> -       *rec = xdf->recs[ri]->ptr;\n> -\n> -       return xdf->recs[ri]->size;\n> -}\n> -\n> -\n> -static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n> -       long size, psize = strlen(pre);\n> -       char const *rec;\n> -\n> -       size = xdl_get_rec(xdf, ri, &rec);\n> -       if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n> +static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n> +{\n\nThe change of the opening curly brace to be on a new line does match\nour general coding guidelines, but cleanups like this should be in a\nseparate patch.\n\n> +       xrecord_t *rec = xdf->recs[ri];\n>\n> +       if (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n>                 return -1;\n> -       }\n\nWhile in this case you were modifying the line in question and thus\nfixing the code to also not use curly braces around a single\nstatement, which is more justified, it still makes the patch slightly\nharder for reviewers to read since you are doing multiple things (what\nyou said in the commit message, plus cleaning up style \"violations\").\nIt'd be better to leave the existing style violations in place, or fix\nthem in a separate patch.\n\n>\n>         return 0;\n>  }\n> @@ -120,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n>  static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n>                            char *buf, long sz)\n>  {\n> -       const char *rec;\n> -       long len = xdl_get_rec(xdf, ri, &rec);\n> +       xrecord_t *rec = xdf->recs[ri];\n> +\n>         if (!xecfg->find_func)\n> -               return def_ff(rec, len, buf, sz);\n> -       return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n> +               return def_ff(rec->ptr, rec->size, buf, sz);\n> +       return xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n>  }\n>\n>  static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n> @@ -160,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n>\n>  static int is_empty_rec(xdfile_t *xdf, long ri)\n>  {\n> -       const char *rec;\n> -       long len = xdl_get_rec(xdf, ri, &rec);\n> +       xrecord_t *rec = xdf->recs[ri];\n> +       long i = 0;\n>\n> -       while (len > 0 && XDL_ISSPACE(*rec)) {\n> -               rec++;\n> -               len--;\n> -       }\n> -       return !len;\n> +       for (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n> +\n> +       return i == rec->size;\n>  }\n\nI agree that the code is easier to follow without the aliasing.\n"},{"id":"525931","messageId":"CABPp-BFuF7i+f=UsbvZpfrFjcsUru7WUyUmCGVQnQwPzfbcp8w@mail.gmail.com","threadId":"64108","inReplyTo":"6cf371ec13d3e57ea7d679a0170951d0b0346349.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 05/17] xdiff: delete struct diffdata_t","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:56:51Z","receivedAt":"2025-09-09T08:57:04Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:45 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> Every field in this struct is an alias for a certain field in xdfile_t.\n>\n> diffdata_t.nrec   -> xdfile_t.nreff\n> diffdata_t.ha     -> xdfile_t.ha\n> diffdata_t.rindex -> xdfile_t.rindex\n> diffdata_t.rchg   -> xdfile_t.recharge\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xdiffi.c | 32 ++++++++------------------------\n>  xdiff/xdiffi.h | 11 ++---------\n>  2 files changed, 10 insertions(+), 33 deletions(-)\n>\n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index 5a96e36dfb..bbf0161f84 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -257,10 +257,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n>   * sub-boxes by calling the box splitting function. Note that the real job\n>   * (marking changed lines) is done in the two boundary reaching checks.\n>   */\n> -int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n> -                diffdata_t *dd2, long off2, long lim2,\n> +int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n> +                xdfile_t *xdf2, long off2, long lim2,\n>                  long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n> -       unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;\n> +       unsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n>\n>         /*\n>          * Shrink the box by walking through each diagonal snake (SW and NE).\n> @@ -273,17 +273,11 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n>          * be obviously changed.\n>          */\n>         if (off1 == lim1) {\n> -               char *rchg2 = dd2->rchg;\n> -               long *rindex2 = dd2->rindex;\n> -\n>                 for (; off2 < lim2; off2++)\n> -                       rchg2[rindex2[off2]] = 1;\n> +                       xdf2->rchg[xdf2->rindex[off2]] = 1;\n>         } else if (off2 == lim2) {\n> -               char *rchg1 = dd1->rchg;\n> -               long *rindex1 = dd1->rindex;\n> -\n>                 for (; off1 < lim1; off1++)\n> -                       rchg1[rindex1[off1]] = 1;\n> +                       xdf1->rchg[xdf1->rindex[off1]] = 1;\n>         } else {\n>                 xdpsplit_t spl;\n>                 spl.i1 = spl.i2 = 0;\n> @@ -300,9 +294,9 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n>                 /*\n>                  * ... et Impera.\n>                  */\n> -               if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,\n> +               if (xdl_recs_cmp(xdf1, off1, spl.i1, xdf2, off2, spl.i2,\n>                                  kvdf, kvdb, spl.min_lo, xenv) < 0 ||\n> -                   xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,\n> +                   xdl_recs_cmp(xdf1, spl.i1, lim1, xdf2, spl.i2, lim2,\n>                                  kvdf, kvdb, spl.min_hi, xenv) < 0) {\n>\n>                         return -1;\n> @@ -318,7 +312,6 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n>         long ndiags;\n>         long *kvd, *kvdf, *kvdb;\n>         xdalgoenv_t xenv;\n> -       diffdata_t dd1, dd2;\n>         int res;\n>\n>         if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)\n> @@ -357,16 +350,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n>         xenv.snake_cnt = XDL_SNAKE_CNT;\n>         xenv.heur_min = XDL_HEUR_MIN_COST;\n>\n> -       dd1.nrec = xe->xdf1.nreff;\n> -       dd1.ha = xe->xdf1.ha;\n> -       dd1.rchg = xe->xdf1.rchg;\n> -       dd1.rindex = xe->xdf1.rindex;\n> -       dd2.nrec = xe->xdf2.nreff;\n> -       dd2.ha = xe->xdf2.ha;\n> -       dd2.rchg = xe->xdf2.rchg;\n> -       dd2.rindex = xe->xdf2.rindex;\n> -\n> -       res = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,\n> +       res = xdl_recs_cmp(&xe->xdf1, 0, xe->xdf1.nreff, &xe->xdf2, 0, xe->xdf2.nreff,\n>                            kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,\n>                            &xenv);\n>         xdl_free(kvd);\n> diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\n> index 126c9d8ff4..49e52c67f9 100644\n> --- a/xdiff/xdiffi.h\n> +++ b/xdiff/xdiffi.h\n> @@ -24,13 +24,6 @@\n>  #define XDIFFI_H\n>\n>\n> -typedef struct s_diffdata {\n> -       long nrec;\n> -       unsigned long const *ha;\n> -       long *rindex;\n> -       char *rchg;\n> -} diffdata_t;\n> -\n>  typedef struct s_xdalgoenv {\n>         long mxcost;\n>         long snake_cnt;\n> @@ -46,8 +39,8 @@ typedef struct s_xdchange {\n>\n>\n>\n> -int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n> -                diffdata_t *dd2, long off2, long lim2,\n> +int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n> +                xdfile_t *xdf2, long off2, long lim2,\n>                  long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);\n>  int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n>                 xdfenv_t *xe);\n> --\n> gitgitgadget\n\nViewing this commit with --color-moved helps highlight in the code\nwhat you say in the commit message.  Makes sense.\n"},{"id":"525932","messageId":"CABPp-BEh1UYwQsFhF_9uHEOi_xfhUJppXcSbJkQv_VuaAbAR0w@mail.gmail.com","threadId":"64108","inReplyTo":"bff456860268a833078b273ff34e3bc26a0c3975.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 06/17] xdiff: delete redundant array xdfile_t.ha","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:57:23Z","receivedAt":"2025-09-09T08:57:35Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:46 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> When 0 <= i < xdfile_t.nreff the following is true:\n> xdfile_t.ha[i] == xdfile_t.recs[xdfile_t.rindex[i]]\n\nI like getting rid of redundant stuff.  One thing to note here is that\nyou're replacing a single indirection with two...\n\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xdiffi.c   | 24 ++++++++++++++----------\n>  xdiff/xprepare.c | 12 ++----------\n>  xdiff/xtypes.h   |  1 -\n>  3 files changed, 16 insertions(+), 21 deletions(-)\n>\n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index bbf0161f84..11cd090b53 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -22,6 +22,11 @@\n>\n>  #include \"xinclude.h\"\n>\n> +static unsigned long get_hash(xdfile_t *xdf, long index)\n> +{\n> +       return xdf->recs[xdf->rindex[index]]->ha;\n> +}\n> +\n>  #define XDL_MAX_COST_MIN 256\n>  #define XDL_HEUR_MIN_COST 256\n>  #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)\n> @@ -42,8 +47,8 @@ typedef struct s_xdpsplit {\n>   * using this algorithm, so a little bit of heuristic is needed to cut the\n>   * search and to return a suboptimal point.\n>   */\n> -static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n> -                     unsigned long const *ha2, long off2, long lim2,\n> +static long xdl_split(xdfile_t *xdf1, long off1, long lim1,\n> +                     xdfile_t *xdf2, long off2, long lim2,\n>                       long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,\n>                       xdalgoenv_t *xenv) {\n>         long dmin = off1 - lim2, dmax = lim1 - off2;\n> @@ -87,7 +92,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n>                                 i1 = kvdf[d + 1];\n>                         prev1 = i1;\n>                         i2 = i1 - d;\n> -                       for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);\n> +                       for (; i1 < lim1 && i2 < lim2 && get_hash(xdf1, i1) == get_hash(xdf2, i2); i1++, i2++);\n\nYou're not going to be happy with me asking, so sorry in advance, but\nI'm really curious...we are now replacing a single indirection with a\ndouble-indirection inside a for loop, which is nested within two other\nfor loops.  Three levels of for-loops to me suggests it might be a hot\ncodepath.  Does this double indirection in these codepaths affect\nperformance?\n\n>                         if (i1 - prev1 > xenv->snake_cnt)\n>                                 got_snake = 1;\n>                         kvdf[d] = i1;\n> @@ -124,7 +129,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n>                                 i1 = kvdb[d + 1] - 1;\n>                         prev1 = i1;\n>                         i2 = i1 - d;\n> -                       for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);\n> +                       for (; i1 > off1 && i2 > off2 && get_hash(xdf1, i1 - 1) == get_hash(xdf2, i2 - 1); i1--, i2--);\n>                         if (prev1 - i1 > xenv->snake_cnt)\n>                                 got_snake = 1;\n>                         kvdb[d] = i1;\n> @@ -159,7 +164,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n>                                 if (v > XDL_K_HEUR * ec && v > best &&\n>                                     off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&\n>                                     off2 + xenv->snake_cnt <= i2 && i2 < lim2) {\n> -                                       for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)\n> +                                       for (k = 1; get_hash(xdf1, i1 - k) == get_hash(xdf2, i2 - k); k++)\n>                                                 if (k == xenv->snake_cnt) {\n>                                                         best = v;\n>                                                         spl->i1 = i1;\n> @@ -183,7 +188,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n>                                 if (v > XDL_K_HEUR * ec && v > best &&\n>                                     off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&\n>                                     off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {\n> -                                       for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)\n> +                                       for (k = 0; get_hash(xdf1, i1 + k) == get_hash(xdf2, i2 + k); k++)\n>                                                 if (k == xenv->snake_cnt - 1) {\n>                                                         best = v;\n>                                                         spl->i1 = i1;\n> @@ -260,13 +265,12 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n>  int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n>                  xdfile_t *xdf2, long off2, long lim2,\n>                  long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n> -       unsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n>\n>         /*\n>          * Shrink the box by walking through each diagonal snake (SW and NE).\n>          */\n> -       for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);\n> -       for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);\n> +       for (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, off1) == get_hash(xdf2, off2); off1++, off2++);\n> +       for (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, lim1 - 1) == get_hash(xdf2, lim2 - 1); lim1--, lim2--);\n>\n>         /*\n>          * If one dimension is empty, then all records on the other one must\n> @@ -285,7 +289,7 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n>                 /*\n>                  * Divide ...\n>                  */\n> -               if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,\n> +               if (xdl_split(xdf1, off1, lim1, xdf2, off2, lim2, kvdf, kvdb,\n>                               need_min, &spl, xenv) < 0) {\n>\n>                         return -1;\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index 91b0ed54e0..59730989a3 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -134,7 +134,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n>\n>         xdl_free(xdf->rindex);\n>         xdl_free(xdf->rchg - 1);\n> -       xdl_free(xdf->ha);\n>         xdl_free(xdf->recs);\n>         xdl_cha_free(&xdf->rcha);\n>  }\n> @@ -147,7 +146,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>         char const *blk, *cur, *top, *prev;\n>         xrecord_t *crec;\n>\n> -       xdf->ha = NULL;\n>         xdf->rindex = NULL;\n>         xdf->rchg = NULL;\n>         xdf->recs = NULL;\n> @@ -182,8 +180,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>             (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n>                 if (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n>                         goto abort;\n> -               if (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n> -                       goto abort;\n>         }\n>\n>         xdf->rchg += 1;\n> @@ -301,9 +297,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>              i <= xdf1->dend; i++, recs++) {\n>                 if (dis1[i] == 1 ||\n>                     (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n> -                       xdf1->rindex[nreff] = i;\n> -                       xdf1->ha[nreff] = (*recs)->ha;\n> -                       nreff++;\n> +                       xdf1->rindex[nreff++] = i;\n>                 } else\n>                         xdf1->rchg[i] = 1;\n>         }\n> @@ -313,9 +307,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>              i <= xdf2->dend; i++, recs++) {\n>                 if (dis2[i] == 1 ||\n>                     (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n> -                       xdf2->rindex[nreff] = i;\n> -                       xdf2->ha[nreff] = (*recs)->ha;\n> -                       nreff++;\n> +                       xdf2->rindex[nreff++] = i;\n>                 } else\n>                         xdf2->rchg[i] = 1;\n>         }\n> diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\n> index 8b8467360e..85848f1685 100644\n> --- a/xdiff/xtypes.h\n> +++ b/xdiff/xtypes.h\n> @@ -52,7 +52,6 @@ typedef struct s_xdfile {\n>         char *rchg;\n>         long *rindex;\n>         long nreff;\n> -       unsigned long *ha;\n>  } xdfile_t;\n>\n>  typedef struct s_xdfenv {\n> --\n> gitgitgadget\n\nOther than the performance question, it looks like you've made a\nstraightforward mechanical change as highlighted in the commit\nmessage.\n"},{"id":"525933","messageId":"CABPp-BGpWoi9naKYMPuL1SXZMb1bAVhAvd8DT5DEfrxHCB5uvw@mail.gmail.com","threadId":"64108","inReplyTo":"db3d4e9a89d4b72dfb7e59dd38c6dad31f5c1cf0.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 07/17] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:57:42Z","receivedAt":"2025-09-09T08:57:53Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:46 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xprepare.c | 16 ++++++----------\n>  1 file changed, 6 insertions(+), 10 deletions(-)\n>\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index 59730989a3..6f1d4b4725 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -32,9 +32,7 @@\n>\n>  typedef struct s_xdlclass {\n>         struct s_xdlclass *next;\n> -       unsigned long ha;\n> -       char const *line;\n> -       long size;\n> +       xrecord_t rec;\n>         long idx;\n>         long len1, len2;\n>  } xdlclass_t;\n> @@ -93,14 +91,12 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n>\n>  static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n>         long hi;\n> -       char const *line;\n>         xdlclass_t *rcrec;\n>\n> -       line = rec->ptr;\n>         hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n>         for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n> -               if (rcrec->ha == rec->ha &&\n> -                               xdl_recmatch(rcrec->line, rcrec->size,\n> +               if (rcrec->rec.ha == rec->ha &&\n> +                               xdl_recmatch(rcrec->rec.ptr, rcrec->rec.size,\n>                                         rec->ptr, rec->size, cf->flags))\n>                         break;\n>\n> @@ -113,9 +109,9 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n>                 if (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))\n>                                 return -1;\n>                 cf->rcrecs[rcrec->idx] = rcrec;\n> -               rcrec->line = line;\n> -               rcrec->size = rec->size;\n> -               rcrec->ha = rec->ha;\n> +               rcrec->rec.ptr = rec->ptr;\n> +               rcrec->rec.size = rec->size;\n> +               rcrec->rec.ha = rec->ha;\n>                 rcrec->len1 = rcrec->len2 = 0;\n>                 rcrec->next = cf->rchash[hi];\n>                 cf->rchash[hi] = rcrec;\n> --\n> gitgitgadget\n\nI can see the changes match the one-line summary.  And I think the\npoint is simplification or reducing redundancy or something...but\ncould a single sentence motivation (stating which of these purposes is\nat play) be added to the commit message?\n"},{"id":"525934","messageId":"CABPp-BHdKu2nsWhpfGY4MexfChxfwv_0mqvpgrV3kbYgdCYKEg@mail.gmail.com","threadId":"64108","inReplyTo":"e7d1933d1c470528de94118fe9c58b47bcc67aca.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 08/17] xdiff: delete chastore from xdfile_t, view with --color-words","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:58:11Z","receivedAt":"2025-09-09T08:58:23Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:46 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nMy personal bias is that things like \"view with --color-words\" makes\nmore sense to include near the end of the commit message, just before\nthe sign-offs.  Not sure if others agree on that.\n\n> The chastore_t type is very unfriendly to Rust FFI. It's also redundant\n> since 'recs' is a vector type that grows every time an xrecord_t is\n> added.\n\nThe second sentence seems to presume the reader knows what chastore_t\ntype is for, and about the confusing dual layering between it and\nrecs.its confusing dual layering.  I liked your more extended\nexplanation in https://lore.kernel.org/git/7ea2dccd71fc502f20614ce217fc9885d1b17413.1756496539.git.gitgitgadget@gmail.com/;\ncould some of that be used here?\n\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xdiffi.c     | 24 ++++++++++----------\n>  xdiff/xemit.c      |  6 ++---\n>  xdiff/xhistogram.c |  2 +-\n>  xdiff/xmerge.c     | 56 +++++++++++++++++++++++-----------------------\n>  xdiff/xpatience.c  | 10 ++++-----\n>  xdiff/xprepare.c   | 19 ++++++----------\n>  xdiff/xtypes.h     |  3 +--\n>  xdiff/xutils.c     | 12 +++++-----\n>  8 files changed, 63 insertions(+), 69 deletions(-)\n>\n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index 11cd090b53..a66125d44a 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -24,7 +24,7 @@\n>\n>  static unsigned long get_hash(xdfile_t *xdf, long index)\n>  {\n> -       return xdf->recs[xdf->rindex[index]]->ha;\n> +       return xdf->recs[xdf->rindex[index]].ha;\n>  }\n>\n>  #define XDL_MAX_COST_MIN 256\n> @@ -489,13 +489,13 @@ static void measure_split(const xdfile_t *xdf, long split,\n>                 m->indent = -1;\n>         } else {\n>                 m->end_of_file = 0;\n> -               m->indent = get_indent(xdf->recs[split]);\n> +               m->indent = get_indent(&xdf->recs[split]);\n>         }\n>\n>         m->pre_blank = 0;\n>         m->pre_indent = -1;\n>         for (i = split - 1; i >= 0; i--) {\n> -               m->pre_indent = get_indent(xdf->recs[i]);\n> +               m->pre_indent = get_indent(&xdf->recs[i]);\n>                 if (m->pre_indent != -1)\n>                         break;\n>                 m->pre_blank += 1;\n> @@ -508,7 +508,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n>         m->post_blank = 0;\n>         m->post_indent = -1;\n>         for (i = split + 1; i < xdf->nrec; i++) {\n> -               m->post_indent = get_indent(xdf->recs[i]);\n> +               m->post_indent = get_indent(&xdf->recs[i]);\n>                 if (m->post_indent != -1)\n>                         break;\n>                 m->post_blank += 1;\n> @@ -752,7 +752,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n>  static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n>  {\n>         if (g->end < xdf->nrec &&\n> -           recs_match(xdf->recs[g->start], xdf->recs[g->end])) {\n> +           recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n>                 xdf->rchg[g->start++] = 0;\n>                 xdf->rchg[g->end++] = 1;\n>\n> @@ -773,7 +773,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n>  static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n>  {\n>         if (g->start > 0 &&\n> -           recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {\n> +           recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n>                 xdf->rchg[--g->start] = 1;\n>                 xdf->rchg[--g->end] = 0;\n>\n> @@ -988,16 +988,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n>\n>         for (xch = xscr; xch; xch = xch->next) {\n>                 int ignore = 1;\n> -               xrecord_t **rec;\n> +               xrecord_t *rec;\n>                 long i;\n>\n>                 rec = &xe->xdf1.recs[xch->i1];\n>                 for (i = 0; i < xch->chg1 && ignore; i++)\n> -                       ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n> +                       ignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n>\n>                 rec = &xe->xdf2.recs[xch->i2];\n>                 for (i = 0; i < xch->chg2 && ignore; i++)\n> -                       ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n> +                       ignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n>\n>                 xch->ignore = ignore;\n>         }\n> @@ -1021,7 +1021,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n>         xdchange_t *xch;\n>\n>         for (xch = xscr; xch; xch = xch->next) {\n> -               xrecord_t **rec;\n> +               xrecord_t *rec;\n>                 int ignore = 1;\n>                 long i;\n>\n> @@ -1033,11 +1033,11 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n>\n>                 rec = &xe->xdf1.recs[xch->i1];\n>                 for (i = 0; i < xch->chg1 && ignore; i++)\n> -                       ignore = record_matches_regex(rec[i], xpp);\n> +                       ignore = record_matches_regex(&rec[i], xpp);\n>\n>                 rec = &xe->xdf2.recs[xch->i2];\n>                 for (i = 0; i < xch->chg2 && ignore; i++)\n> -                       ignore = record_matches_regex(rec[i], xpp);\n> +                       ignore = record_matches_regex(&rec[i], xpp);\n>\n>                 xch->ignore = ignore;\n>         }\n> diff --git a/xdiff/xemit.c b/xdiff/xemit.c\n> index 2161ac3cd0..b2f1f30cd3 100644\n> --- a/xdiff/xemit.c\n> +++ b/xdiff/xemit.c\n> @@ -25,7 +25,7 @@\n>\n>  static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n>  {\n> -       xrecord_t *rec = xdf->recs[ri];\n> +       xrecord_t *rec = &xdf->recs[ri];\n>\n>         if (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n>                 return -1;\n> @@ -110,7 +110,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n>  static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n>                            char *buf, long sz)\n>  {\n> -       xrecord_t *rec = xdf->recs[ri];\n> +       xrecord_t *rec = &xdf->recs[ri];\n>\n>         if (!xecfg->find_func)\n>                 return def_ff(rec->ptr, rec->size, buf, sz);\n> @@ -150,7 +150,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n>\n>  static int is_empty_rec(xdfile_t *xdf, long ri)\n>  {\n> -       xrecord_t *rec = xdf->recs[ri];\n> +       xrecord_t *rec = &xdf->recs[ri];\n>         long i = 0;\n>\n>         for (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n> diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\n> index 040d81e0bc..4d857e8ae2 100644\n> --- a/xdiff/xhistogram.c\n> +++ b/xdiff/xhistogram.c\n> @@ -86,7 +86,7 @@ struct region {\n>         ((LINE_MAP(index, ptr))->cnt)\n>\n>  #define REC(env, s, l) \\\n> -       (env->xdf##s.recs[l - 1])\n> +       (&env->xdf##s.recs[l - 1])\n>\n>  static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n>  {\n> diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\n> index af40c88a5b..fd600cbb5d 100644\n> --- a/xdiff/xmerge.c\n> +++ b/xdiff/xmerge.c\n> @@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n>                 int line_count, long flags)\n>  {\n>         int i;\n> -       xrecord_t **rec1 = xe1->xdf2.recs + i1;\n> -       xrecord_t **rec2 = xe2->xdf2.recs + i2;\n> +       xrecord_t *rec1 = xe1->xdf2.recs + i1;\n> +       xrecord_t *rec2 = xe2->xdf2.recs + i2;\n>\n>         for (i = 0; i < line_count; i++) {\n> -               int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,\n> -                       rec2[i]->ptr, rec2[i]->size, flags);\n> +               int result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n> +                       rec2[i].ptr, rec2[i].size, flags);\n>                 if (!result)\n>                         return -1;\n>         }\n> @@ -111,7 +111,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n>\n>  static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)\n>  {\n> -       xrecord_t **recs;\n> +       xrecord_t *recs;\n>         int size = 0;\n>\n>         recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n> @@ -119,12 +119,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n>         if (count < 1)\n>                 return 0;\n>\n> -       for (i = 0; i < count; size += recs[i++]->size)\n> +       for (i = 0; i < count; size += recs[i++].size)\n>                 if (dest)\n> -                       memcpy(dest + size, recs[i]->ptr, recs[i]->size);\n> +                       memcpy(dest + size, recs[i].ptr, recs[i].size);\n>         if (add_nl) {\n> -               i = recs[count - 1]->size;\n> -               if (i == 0 || recs[count - 1]->ptr[i - 1] != '\\n') {\n> +               i = recs[count - 1].size;\n> +               if (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n>                         if (needs_cr) {\n>                                 if (dest)\n>                                         dest[size] = '\\r';\n> @@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)\n>\n>         if (i < file->nrec - 1)\n>                 /* All lines before the last *must* end in LF */\n> -               return (size = file->recs[i]->size) > 1 &&\n> -                       file->recs[i]->ptr[size - 2] == '\\r';\n> +               return (size = file->recs[i].size) > 1 &&\n> +                       file->recs[i].ptr[size - 2] == '\\r';\n>         if (!file->nrec)\n>                 /* Cannot determine eol style from empty file */\n>                 return -1;\n> -       if ((size = file->recs[i]->size) &&\n> -                       file->recs[i]->ptr[size - 1] == '\\n')\n> +       if ((size = file->recs[i].size) &&\n> +                       file->recs[i].ptr[size - 1] == '\\n')\n>                 /* Last line; ends in LF; Is it CR/LF? */\n>                 return size > 1 &&\n> -                       file->recs[i]->ptr[size - 2] == '\\r';\n> +                       file->recs[i].ptr[size - 2] == '\\r';\n>         if (!i)\n>                 /* The only line has no eol */\n>                 return -1;\n>         /* Determine eol from second-to-last line */\n> -       return (size = file->recs[i - 1]->size) > 1 &&\n> -               file->recs[i - 1]->ptr[size - 2] == '\\r';\n> +       return (size = file->recs[i - 1].size) > 1 &&\n> +               file->recs[i - 1].ptr[size - 2] == '\\r';\n>  }\n>\n>  static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)\n> @@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n>  static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n>                 xpparam_t const *xpp)\n>  {\n> -       xrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n> +       xrecord_t *rec1 = xe1->xdf2.recs, *rec2 = xe2->xdf2.recs;\n>         for (; m; m = m->next) {\n>                 /* let's handle just the conflicts */\n>                 if (m->mode)\n>                         continue;\n>\n>                 while(m->chg1 && m->chg2 &&\n> -                     recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n> +                     recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {\n>                         m->chg1--;\n>                         m->chg2--;\n>                         m->i1++;\n>                         m->i2++;\n>                 }\n>                 while (m->chg1 && m->chg2 &&\n> -                      recmatch(rec1[m->i1 + m->chg1 - 1],\n> -                               rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n> +                      recmatch(&rec1[m->i1 + m->chg1 - 1],\n> +                               &rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n>                         m->chg1--;\n>                         m->chg2--;\n>                 }\n> @@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n>                  * This probably does not work outside git, since\n>                  * we have a very simple mmfile structure.\n>                  */\n> -               t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;\n> -               t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr\n> -                       + xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;\n> -               t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;\n> -               t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr\n> -                       + xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;\n> +               t1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n> +               t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n> +                       + xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n> +               t2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n> +               t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n> +                       + xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n>                 if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n>                         return -1;\n>                 if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||\n> @@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)\n>  static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n>  {\n>         for (; chg; chg--, i++)\n> -               if (line_contains_alnum(xe->xdf2.recs[i]->ptr,\n> -                               xe->xdf2.recs[i]->size))\n> +               if (line_contains_alnum(xe->xdf2.recs[i].ptr,\n> +                               xe->xdf2.recs[i].size))\n>                         return 1;\n>         return 0;\n>  }\n> diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\n> index 77dc411d19..bf69a58527 100644\n> --- a/xdiff/xpatience.c\n> +++ b/xdiff/xpatience.c\n> @@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)\n>  static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n>                           int pass)\n>  {\n> -       xrecord_t **records = pass == 1 ?\n> +       xrecord_t *records = pass == 1 ?\n>                 map->env->xdf1.recs : map->env->xdf2.recs;\n> -       xrecord_t *record = records[line - 1];\n> +       xrecord_t *record = &records[line - 1];\n>         /*\n>          * After xdl_prepare_env() (or more precisely, due to\n>          * xdl_classify_record()), the \"ha\" member of the records (AKA lines)\n> @@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n>                 return;\n>         map->entries[index].line1 = line;\n>         map->entries[index].hash = record->ha;\n> -       map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);\n> +       map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n>         if (!map->first)\n>                 map->first = map->entries + index;\n>         if (map->last) {\n> @@ -246,8 +246,8 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)\n>\n>  static int match(struct hashmap *map, int line1, int line2)\n>  {\n> -       xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];\n> -       xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];\n> +       xrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n> +       xrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n>         return record1->ha == record2->ha;\n>  }\n>\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index 6f1d4b4725..92f9845003 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -131,7 +131,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n>         xdl_free(xdf->rindex);\n>         xdl_free(xdf->rchg - 1);\n>         xdl_free(xdf->recs);\n> -       xdl_cha_free(&xdf->rcha);\n>  }\n>\n>\n> @@ -146,8 +145,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>         xdf->rchg = NULL;\n>         xdf->recs = NULL;\n>\n> -       if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n> -               goto abort;\n>         if (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n>                 goto abort;\n>\n> @@ -158,12 +155,10 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>                         hav = xdl_hash_record(&cur, top, xpp->flags);\n>                         if (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n>                                 goto abort;\n> -                       if (!(crec = xdl_cha_alloc(&xdf->rcha)))\n> -                               goto abort;\n> +                       crec = &xdf->recs[xdf->nrec++];\n>                         crec->ptr = prev;\n>                         crec->size = (long) (cur - prev);\n>                         crec->ha = hav;\n> -                       xdf->recs[xdf->nrec++] = crec;\n>                         if (xdl_classify_record(pass, cf, crec) < 0)\n>                                 goto abort;\n>                 }\n> @@ -263,7 +258,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n>   */\n>  static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n>         long i, nm, nreff, mlim;\n> -       xrecord_t **recs;\n> +       xrecord_t *recs;\n>         xdlclass_t *rcrec;\n>         char *dis, *dis1, *dis2;\n>         int need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n> @@ -276,7 +271,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>         if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n>                 mlim = XDL_MAX_EQLIMIT;\n>         for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n> -               rcrec = cf->rcrecs[(*recs)->ha];\n> +               rcrec = cf->rcrecs[recs->ha];\n>                 nm = rcrec ? rcrec->len2 : 0;\n>                 dis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n>         }\n> @@ -284,7 +279,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>         if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n>                 mlim = XDL_MAX_EQLIMIT;\n>         for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n> -               rcrec = cf->rcrecs[(*recs)->ha];\n> +               rcrec = cf->rcrecs[recs->ha];\n>                 nm = rcrec ? rcrec->len1 : 0;\n>                 dis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n>         }\n> @@ -320,13 +315,13 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>   */\n>  static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n>         long i, lim;\n> -       xrecord_t **recs1, **recs2;\n> +       xrecord_t *recs1, *recs2;\n>\n>         recs1 = xdf1->recs;\n>         recs2 = xdf2->recs;\n>         for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n>              i++, recs1++, recs2++)\n> -               if ((*recs1)->ha != (*recs2)->ha)\n> +               if (recs1->ha != recs2->ha)\n>                         break;\n>\n>         xdf1->dstart = xdf2->dstart = i;\n> @@ -334,7 +329,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n>         recs1 = xdf1->recs + xdf1->nrec - 1;\n>         recs2 = xdf2->recs + xdf2->nrec - 1;\n>         for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n> -               if ((*recs1)->ha != (*recs2)->ha)\n> +               if (recs1->ha != recs2->ha)\n>                         break;\n>\n>         xdf1->dend = xdf1->nrec - i - 1;\n> diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\n> index 85848f1685..3d26cbf1ec 100644\n> --- a/xdiff/xtypes.h\n> +++ b/xdiff/xtypes.h\n> @@ -45,10 +45,9 @@ typedef struct s_xrecord {\n>  } xrecord_t;\n>\n>  typedef struct s_xdfile {\n> -       chastore_t rcha;\n> +       xrecord_t *recs;\n>         long nrec;\n>         long dstart, dend;\n> -       xrecord_t **recs;\n>         char *rchg;\n>         long *rindex;\n>         long nreff;\n> diff --git a/xdiff/xutils.c b/xdiff/xutils.c\n> index 444a108f87..332982b509 100644\n> --- a/xdiff/xutils.c\n> +++ b/xdiff/xutils.c\n> @@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n>         mmfile_t subfile1, subfile2;\n>         xdfenv_t env;\n>\n> -       subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;\n> -       subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +\n> -               diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;\n> -       subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;\n> -       subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +\n> -               diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;\n> +       subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n> +       subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n> +               diff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n> +       subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n> +       subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n> +               diff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n>         if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n>                 return -1;\n>\n> --\n> gitgitgadget\n\nYou weren't kidding with the --color-words callout; there's an awful\nlot of places where you only change one or two characters (e.g. '->'\nbecoming '.'); that's much easier to see when viewing the diff with\nthat flag.\n\nAnyway, looks good.\n"},{"id":"525935","messageId":"CABPp-BEnvJ2x5sC-fYHHKm7Cqu=CWtNzGBDT10JU-yS4hw2dpQ@mail.gmail.com","threadId":"64108","inReplyTo":"d1657f51012c8ee97f81bf5e3550aa47a0c5b70c.1757274320.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 09/17] xdiff: treat xdfile_t.rchg like an enum","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-09T08:58:22Z","receivedAt":"2025-09-09T08:58:36Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sun, Sep 7, 2025 at 12:46 PM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> Define macros NO(0), YES(1), MAYBE(2) as the enum values for rchg to\n> make the code easier to follow. Perhaps 'rchg' should be renamed to\n> 'changed'?\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>  xdiff/xdiff.h      |  4 ++++\n>  xdiff/xdiffi.c     | 29 ++++++++++++++---------------\n>  xdiff/xhistogram.c |  8 ++++----\n>  xdiff/xpatience.c  |  8 ++++----\n>  xdiff/xprepare.c   | 24 ++++++++++++------------\n>  5 files changed, 38 insertions(+), 35 deletions(-)\n>\n> diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\n> index 2cecde5afe..7092879829 100644\n> --- a/xdiff/xdiff.h\n> +++ b/xdiff/xdiff.h\n> @@ -27,6 +27,10 @@\n>  extern \"C\" {\n>  #endif /* #ifdef __cplusplus */\n>\n> +#define NO 0\n> +#define YES 1\n> +#define MAYBE 2\n> +\n>  /* xpparm_t.flags */\n>  #define XDF_NEED_MINIMAL (1 << 0)\n>\n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index a66125d44a..44fd27823a 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n>          */\n>         if (off1 == lim1) {\n>                 for (; off2 < lim2; off2++)\n> -                       xdf2->rchg[xdf2->rindex[off2]] = 1;\n> +                       xdf2->rchg[xdf2->rindex[off2]] = YES;\n>         } else if (off2 == lim2) {\n>                 for (; off1 < lim1; off1++)\n> -                       xdf1->rchg[xdf1->rindex[off1]] = 1;\n> +                       xdf1->rchg[xdf1->rindex[off1]] = YES;\n>         } else {\n>                 xdpsplit_t spl;\n>                 spl.i1 = spl.i2 = 0;\n> @@ -708,7 +708,7 @@ struct xdlgroup {\n>  static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n>  {\n>         g->start = g->end = 0;\n> -       while (xdf->rchg[g->end])\n> +       while (xdf->rchg[g->end] == YES)\n\nYou've got a few places like this where the old code would have\nbehaved differently if there were some MAYBE values.  I presume you've\ncarefully vetted that those can't happen at these points in the code,\nbut it might be worth calling that out in the commit message for\nreviewers who'll otherwise wonder if there's a behavior change that\nhas occurred.\n\n>                 g->end++;\n>  }\n>\n> @@ -722,7 +722,7 @@ static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n>                 return -1;\n>\n>         g->start = g->end + 1;\n> -       for (g->end = g->start; xdf->rchg[g->end]; g->end++)\n> +       for (g->end = g->start; xdf->rchg[g->end] == YES; g->end++)\n\nHere's another of those where you assume MAYBE isn't possible.\n\n>                 ;\n>\n>         return 0;\n> @@ -738,7 +738,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n>                 return -1;\n>\n>         g->end = g->start - 1;\n> -       for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n> +       for (g->start = g->end; xdf->rchg[g->start - 1] == YES; g->start--)\n\n...and another.\n\n>                 ;\n>\n>         return 0;\n> @@ -753,10 +753,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n>  {\n>         if (g->end < xdf->nrec &&\n>             recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n> -               xdf->rchg[g->start++] = 0;\n> -               xdf->rchg[g->end++] = 1;\n> +               xdf->rchg[g->start++] = NO;\n> +               xdf->rchg[g->end++] = YES;\n>\n> -               while (xdf->rchg[g->end])\n> +               while (xdf->rchg[g->end] == YES)\n\n...and another.\n\n>                         g->end++;\n>\n>                 return 0;\n> @@ -774,10 +774,10 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n>  {\n>         if (g->start > 0 &&\n>             recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n> -               xdf->rchg[--g->start] = 1;\n> -               xdf->rchg[--g->end] = 0;\n> +               xdf->rchg[--g->start] = YES;\n> +               xdf->rchg[--g->end] = NO;\n>\n> -               while (xdf->rchg[g->start - 1])\n> +               while (xdf->rchg[g->start - 1] == YES)\n\n...and another.\n\n>                         g->start--;\n>\n>                 return 0;\n> @@ -932,16 +932,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n>\n>  int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n>         xdchange_t *cscr = NULL, *xch;\n> -       char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n>         long i1, i2, l1, l2;\n>\n>         /*\n>          * Trivial. Collects \"groups\" of changes and creates an edit script.\n>          */\n>         for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n> -               if (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n> -                       for (l1 = i1; rchg1[i1 - 1]; i1--);\n> -                       for (l2 = i2; rchg2[i2 - 1]; i2--);\n> +               if (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n> +                       for (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n> +                       for (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n\nThe changes in this xdl_build_script() function appear to be\northogonal to what was described in the commit message.  If it's a\nseparate cleanup, perhaps justify it in a separate patch?\n\n>\n>                         if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n>                                 xdl_free_script(cscr);\n> diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\n> index 4d857e8ae2..c2e85b8ab9 100644\n> --- a/xdiff/xhistogram.c\n> +++ b/xdiff/xhistogram.c\n> @@ -318,11 +318,11 @@ redo:\n>\n>         if (!count1) {\n>                 while(count2--)\n> -                       env->xdf2.rchg[line2++ - 1] = 1;\n> +                       env->xdf2.rchg[line2++ - 1] = YES;\n>                 return 0;\n>         } else if (!count2) {\n>                 while(count1--)\n> -                       env->xdf1.rchg[line1++ - 1] = 1;\n> +                       env->xdf1.rchg[line1++ - 1] = YES;\n>                 return 0;\n>         }\n>\n> @@ -335,9 +335,9 @@ redo:\n>         else {\n>                 if (lcs.begin1 == 0 && lcs.begin2 == 0) {\n>                         while (count1--)\n> -                               env->xdf1.rchg[line1++ - 1] = 1;\n> +                               env->xdf1.rchg[line1++ - 1] = YES;\n>                         while (count2--)\n> -                               env->xdf2.rchg[line2++ - 1] = 1;\n> +                               env->xdf2.rchg[line2++ - 1] = YES;\n>                         result = 0;\n>                 } else {\n>                         result = histogram_diff(xpp, env,\n> diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\n> index bf69a58527..20cda5e258 100644\n> --- a/xdiff/xpatience.c\n> +++ b/xdiff/xpatience.c\n> @@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n>         /* trivial case: one side is empty */\n>         if (!count1) {\n>                 while(count2--)\n> -                       env->xdf2.rchg[line2++ - 1] = 1;\n> +                       env->xdf2.rchg[line2++ - 1] = YES;\n>                 return 0;\n>         } else if (!count2) {\n>                 while(count1--)\n> -                       env->xdf1.rchg[line1++ - 1] = 1;\n> +                       env->xdf1.rchg[line1++ - 1] = YES;\n>                 return 0;\n>         }\n>\n> @@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n>         /* are there any matching lines at all? */\n>         if (!map.has_matches) {\n>                 while(count1--)\n> -                       env->xdf1.rchg[line1++ - 1] = 1;\n> +                       env->xdf1.rchg[line1++ - 1] = YES;\n>                 while(count2--)\n> -                       env->xdf2.rchg[line2++ - 1] = 1;\n> +                       env->xdf2.rchg[line2++ - 1] = YES;\n>                 xdl_free(map.entries);\n>                 return 0;\n>         }\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index 92f9845003..36437f91bb 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -215,9 +215,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n>          * current line (i) is already a multimatch line.\n>          */\n>         for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n> -               if (!dis[i - r])\n> +               if (dis[i - r] == NO)\n>                         rdis0++;\n> -               else if (dis[i - r] == 2)\n> +               else if (dis[i - r] == MAYBE)\n>                         rpdis0++;\n>                 else\n>                         break;\n> @@ -231,9 +231,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n>         if (rdis0 == 0)\n>                 return 0;\n>         for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n> -               if (!dis[i + r])\n> +               if (dis[i + r] == NO)\n>                         rdis1++;\n> -               else if (dis[i + r] == 2)\n> +               else if (dis[i + r] == MAYBE)\n>                         rpdis1++;\n>                 else\n>                         break;\n> @@ -273,7 +273,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>         for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n>                 rcrec = cf->rcrecs[recs->ha];\n>                 nm = rcrec ? rcrec->len2 : 0;\n> -               dis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n> +               dis1[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n>         }\n>\n>         if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n> @@ -281,26 +281,26 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>         for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n>                 rcrec = cf->rcrecs[recs->ha];\n>                 nm = rcrec ? rcrec->len1 : 0;\n> -               dis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n> +               dis2[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n>         }\n>\n>         for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n>              i <= xdf1->dend; i++, recs++) {\n> -               if (dis1[i] == 1 ||\n> -                   (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n> +               if (dis1[i] == YES ||\n> +                   (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n>                         xdf1->rindex[nreff++] = i;\n>                 } else\n> -                       xdf1->rchg[i] = 1;\n> +                       xdf1->rchg[i] = YES;\n>         }\n>         xdf1->nreff = nreff;\n>\n>         for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n>              i <= xdf2->dend; i++, recs++) {\n> -               if (dis2[i] == 1 ||\n> -                   (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n> +               if (dis2[i] == YES ||\n> +                   (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n>                         xdf2->rindex[nreff++] = i;\n>                 } else\n> -                       xdf2->rchg[i] = 1;\n> +                       xdf2->rchg[i] = YES;\n>         }\n>         xdf2->nreff = nreff;\n>\n> --\n> gitgitgadget\n\nEverything else looks like the straightforward translation you called\nout in your commit message.\n"},{"id":"525964","messageId":"aae1ad41-8604-45bc-8ec3-03180e6152ff@gmail.com","threadId":"64108","inReplyTo":"CABPp-BHdKu2nsWhpfGY4MexfChxfwv_0mqvpgrV3kbYgdCYKEg@mail.gmail.com","subject":"Re: [PATCH 08/17] xdiff: delete chastore from xdfile_t, view with --color-words","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-09T13:50:36Z","receivedAt":"2025-09-09T13:50:43Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 09/09/2025 09:58, Elijah Newren wrote:\n> On Sun, Sep 7, 2025 at 12:46 PM Ezekiel Newren via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>>\n>> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>> The chastore_t type is very unfriendly to Rust FFI. It's also redundant\n>> since 'recs' is a vector type that grows every time an xrecord_t is\n>> added.\n> \n> The second sentence seems to presume the reader knows what chastore_t\n> type is for, and about the confusing dual layering between it and\n> recs.its confusing dual layering.  I liked your more extended\n> explanation in https://lore.kernel.org/git/7ea2dccd71fc502f20614ce217fc9885d1b17413.1756496539.git.gitgitgadget@gmail.com/;\n> could some of that be used here?\n\nI agree that's a better explaination. I also think it would be helpful \nto spell out the implications of this change. If I understand the change \ncorrectly we now store all the records in a contiguous array, rather \nthan having the records in a arena and storing a separate array of \npointers to those records. As sizeof(xrecord_t) is pretty small the \nchange to contiguous storage hopefully wont cause any allocation issues, \nthough I guess it does mean we end up copying more data as we grow the \narray compared to using an arena.\n\nOverall these first few patches look like a really nice cleanup.\n\nThanks\n\nPhillip\n\n>>\n>> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n>> ---\n>>   xdiff/xdiffi.c     | 24 ++++++++++----------\n>>   xdiff/xemit.c      |  6 ++---\n>>   xdiff/xhistogram.c |  2 +-\n>>   xdiff/xmerge.c     | 56 +++++++++++++++++++++++-----------------------\n>>   xdiff/xpatience.c  | 10 ++++-----\n>>   xdiff/xprepare.c   | 19 ++++++----------\n>>   xdiff/xtypes.h     |  3 +--\n>>   xdiff/xutils.c     | 12 +++++-----\n>>   8 files changed, 63 insertions(+), 69 deletions(-)\n>>\n>> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n>> index 11cd090b53..a66125d44a 100644\n>> --- a/xdiff/xdiffi.c\n>> +++ b/xdiff/xdiffi.c\n>> @@ -24,7 +24,7 @@\n>>\n>>   static unsigned long get_hash(xdfile_t *xdf, long index)\n>>   {\n>> -       return xdf->recs[xdf->rindex[index]]->ha;\n>> +       return xdf->recs[xdf->rindex[index]].ha;\n>>   }\n>>\n>>   #define XDL_MAX_COST_MIN 256\n>> @@ -489,13 +489,13 @@ static void measure_split(const xdfile_t *xdf, long split,\n>>                  m->indent = -1;\n>>          } else {\n>>                  m->end_of_file = 0;\n>> -               m->indent = get_indent(xdf->recs[split]);\n>> +               m->indent = get_indent(&xdf->recs[split]);\n>>          }\n>>\n>>          m->pre_blank = 0;\n>>          m->pre_indent = -1;\n>>          for (i = split - 1; i >= 0; i--) {\n>> -               m->pre_indent = get_indent(xdf->recs[i]);\n>> +               m->pre_indent = get_indent(&xdf->recs[i]);\n>>                  if (m->pre_indent != -1)\n>>                          break;\n>>                  m->pre_blank += 1;\n>> @@ -508,7 +508,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n>>          m->post_blank = 0;\n>>          m->post_indent = -1;\n>>          for (i = split + 1; i < xdf->nrec; i++) {\n>> -               m->post_indent = get_indent(xdf->recs[i]);\n>> +               m->post_indent = get_indent(&xdf->recs[i]);\n>>                  if (m->post_indent != -1)\n>>                          break;\n>>                  m->post_blank += 1;\n>> @@ -752,7 +752,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n>>   static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n>>   {\n>>          if (g->end < xdf->nrec &&\n>> -           recs_match(xdf->recs[g->start], xdf->recs[g->end])) {\n>> +           recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n>>                  xdf->rchg[g->start++] = 0;\n>>                  xdf->rchg[g->end++] = 1;\n>>\n>> @@ -773,7 +773,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n>>   static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n>>   {\n>>          if (g->start > 0 &&\n>> -           recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {\n>> +           recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n>>                  xdf->rchg[--g->start] = 1;\n>>                  xdf->rchg[--g->end] = 0;\n>>\n>> @@ -988,16 +988,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n>>\n>>          for (xch = xscr; xch; xch = xch->next) {\n>>                  int ignore = 1;\n>> -               xrecord_t **rec;\n>> +               xrecord_t *rec;\n>>                  long i;\n>>\n>>                  rec = &xe->xdf1.recs[xch->i1];\n>>                  for (i = 0; i < xch->chg1 && ignore; i++)\n>> -                       ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n>> +                       ignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n>>\n>>                  rec = &xe->xdf2.recs[xch->i2];\n>>                  for (i = 0; i < xch->chg2 && ignore; i++)\n>> -                       ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n>> +                       ignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n>>\n>>                  xch->ignore = ignore;\n>>          }\n>> @@ -1021,7 +1021,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n>>          xdchange_t *xch;\n>>\n>>          for (xch = xscr; xch; xch = xch->next) {\n>> -               xrecord_t **rec;\n>> +               xrecord_t *rec;\n>>                  int ignore = 1;\n>>                  long i;\n>>\n>> @@ -1033,11 +1033,11 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n>>\n>>                  rec = &xe->xdf1.recs[xch->i1];\n>>                  for (i = 0; i < xch->chg1 && ignore; i++)\n>> -                       ignore = record_matches_regex(rec[i], xpp);\n>> +                       ignore = record_matches_regex(&rec[i], xpp);\n>>\n>>                  rec = &xe->xdf2.recs[xch->i2];\n>>                  for (i = 0; i < xch->chg2 && ignore; i++)\n>> -                       ignore = record_matches_regex(rec[i], xpp);\n>> +                       ignore = record_matches_regex(&rec[i], xpp);\n>>\n>>                  xch->ignore = ignore;\n>>          }\n>> diff --git a/xdiff/xemit.c b/xdiff/xemit.c\n>> index 2161ac3cd0..b2f1f30cd3 100644\n>> --- a/xdiff/xemit.c\n>> +++ b/xdiff/xemit.c\n>> @@ -25,7 +25,7 @@\n>>\n>>   static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n>>   {\n>> -       xrecord_t *rec = xdf->recs[ri];\n>> +       xrecord_t *rec = &xdf->recs[ri];\n>>\n>>          if (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n>>                  return -1;\n>> @@ -110,7 +110,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n>>   static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n>>                             char *buf, long sz)\n>>   {\n>> -       xrecord_t *rec = xdf->recs[ri];\n>> +       xrecord_t *rec = &xdf->recs[ri];\n>>\n>>          if (!xecfg->find_func)\n>>                  return def_ff(rec->ptr, rec->size, buf, sz);\n>> @@ -150,7 +150,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n>>\n>>   static int is_empty_rec(xdfile_t *xdf, long ri)\n>>   {\n>> -       xrecord_t *rec = xdf->recs[ri];\n>> +       xrecord_t *rec = &xdf->recs[ri];\n>>          long i = 0;\n>>\n>>          for (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n>> diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\n>> index 040d81e0bc..4d857e8ae2 100644\n>> --- a/xdiff/xhistogram.c\n>> +++ b/xdiff/xhistogram.c\n>> @@ -86,7 +86,7 @@ struct region {\n>>          ((LINE_MAP(index, ptr))->cnt)\n>>\n>>   #define REC(env, s, l) \\\n>> -       (env->xdf##s.recs[l - 1])\n>> +       (&env->xdf##s.recs[l - 1])\n>>\n>>   static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n>>   {\n>> diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\n>> index af40c88a5b..fd600cbb5d 100644\n>> --- a/xdiff/xmerge.c\n>> +++ b/xdiff/xmerge.c\n>> @@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n>>                  int line_count, long flags)\n>>   {\n>>          int i;\n>> -       xrecord_t **rec1 = xe1->xdf2.recs + i1;\n>> -       xrecord_t **rec2 = xe2->xdf2.recs + i2;\n>> +       xrecord_t *rec1 = xe1->xdf2.recs + i1;\n>> +       xrecord_t *rec2 = xe2->xdf2.recs + i2;\n>>\n>>          for (i = 0; i < line_count; i++) {\n>> -               int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,\n>> -                       rec2[i]->ptr, rec2[i]->size, flags);\n>> +               int result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n>> +                       rec2[i].ptr, rec2[i].size, flags);\n>>                  if (!result)\n>>                          return -1;\n>>          }\n>> @@ -111,7 +111,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n>>\n>>   static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)\n>>   {\n>> -       xrecord_t **recs;\n>> +       xrecord_t *recs;\n>>          int size = 0;\n>>\n>>          recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n>> @@ -119,12 +119,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n>>          if (count < 1)\n>>                  return 0;\n>>\n>> -       for (i = 0; i < count; size += recs[i++]->size)\n>> +       for (i = 0; i < count; size += recs[i++].size)\n>>                  if (dest)\n>> -                       memcpy(dest + size, recs[i]->ptr, recs[i]->size);\n>> +                       memcpy(dest + size, recs[i].ptr, recs[i].size);\n>>          if (add_nl) {\n>> -               i = recs[count - 1]->size;\n>> -               if (i == 0 || recs[count - 1]->ptr[i - 1] != '\\n') {\n>> +               i = recs[count - 1].size;\n>> +               if (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n>>                          if (needs_cr) {\n>>                                  if (dest)\n>>                                          dest[size] = '\\r';\n>> @@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)\n>>\n>>          if (i < file->nrec - 1)\n>>                  /* All lines before the last *must* end in LF */\n>> -               return (size = file->recs[i]->size) > 1 &&\n>> -                       file->recs[i]->ptr[size - 2] == '\\r';\n>> +               return (size = file->recs[i].size) > 1 &&\n>> +                       file->recs[i].ptr[size - 2] == '\\r';\n>>          if (!file->nrec)\n>>                  /* Cannot determine eol style from empty file */\n>>                  return -1;\n>> -       if ((size = file->recs[i]->size) &&\n>> -                       file->recs[i]->ptr[size - 1] == '\\n')\n>> +       if ((size = file->recs[i].size) &&\n>> +                       file->recs[i].ptr[size - 1] == '\\n')\n>>                  /* Last line; ends in LF; Is it CR/LF? */\n>>                  return size > 1 &&\n>> -                       file->recs[i]->ptr[size - 2] == '\\r';\n>> +                       file->recs[i].ptr[size - 2] == '\\r';\n>>          if (!i)\n>>                  /* The only line has no eol */\n>>                  return -1;\n>>          /* Determine eol from second-to-last line */\n>> -       return (size = file->recs[i - 1]->size) > 1 &&\n>> -               file->recs[i - 1]->ptr[size - 2] == '\\r';\n>> +       return (size = file->recs[i - 1].size) > 1 &&\n>> +               file->recs[i - 1].ptr[size - 2] == '\\r';\n>>   }\n>>\n>>   static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)\n>> @@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n>>   static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n>>                  xpparam_t const *xpp)\n>>   {\n>> -       xrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n>> +       xrecord_t *rec1 = xe1->xdf2.recs, *rec2 = xe2->xdf2.recs;\n>>          for (; m; m = m->next) {\n>>                  /* let's handle just the conflicts */\n>>                  if (m->mode)\n>>                          continue;\n>>\n>>                  while(m->chg1 && m->chg2 &&\n>> -                     recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n>> +                     recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {\n>>                          m->chg1--;\n>>                          m->chg2--;\n>>                          m->i1++;\n>>                          m->i2++;\n>>                  }\n>>                  while (m->chg1 && m->chg2 &&\n>> -                      recmatch(rec1[m->i1 + m->chg1 - 1],\n>> -                               rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n>> +                      recmatch(&rec1[m->i1 + m->chg1 - 1],\n>> +                               &rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n>>                          m->chg1--;\n>>                          m->chg2--;\n>>                  }\n>> @@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n>>                   * This probably does not work outside git, since\n>>                   * we have a very simple mmfile structure.\n>>                   */\n>> -               t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;\n>> -               t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr\n>> -                       + xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;\n>> -               t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;\n>> -               t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr\n>> -                       + xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;\n>> +               t1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n>> +               t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n>> +                       + xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n>> +               t2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n>> +               t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n>> +                       + xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n>>                  if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n>>                          return -1;\n>>                  if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||\n>> @@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)\n>>   static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n>>   {\n>>          for (; chg; chg--, i++)\n>> -               if (line_contains_alnum(xe->xdf2.recs[i]->ptr,\n>> -                               xe->xdf2.recs[i]->size))\n>> +               if (line_contains_alnum(xe->xdf2.recs[i].ptr,\n>> +                               xe->xdf2.recs[i].size))\n>>                          return 1;\n>>          return 0;\n>>   }\n>> diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\n>> index 77dc411d19..bf69a58527 100644\n>> --- a/xdiff/xpatience.c\n>> +++ b/xdiff/xpatience.c\n>> @@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)\n>>   static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n>>                            int pass)\n>>   {\n>> -       xrecord_t **records = pass == 1 ?\n>> +       xrecord_t *records = pass == 1 ?\n>>                  map->env->xdf1.recs : map->env->xdf2.recs;\n>> -       xrecord_t *record = records[line - 1];\n>> +       xrecord_t *record = &records[line - 1];\n>>          /*\n>>           * After xdl_prepare_env() (or more precisely, due to\n>>           * xdl_classify_record()), the \"ha\" member of the records (AKA lines)\n>> @@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n>>                  return;\n>>          map->entries[index].line1 = line;\n>>          map->entries[index].hash = record->ha;\n>> -       map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);\n>> +       map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n>>          if (!map->first)\n>>                  map->first = map->entries + index;\n>>          if (map->last) {\n>> @@ -246,8 +246,8 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)\n>>\n>>   static int match(struct hashmap *map, int line1, int line2)\n>>   {\n>> -       xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];\n>> -       xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];\n>> +       xrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n>> +       xrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n>>          return record1->ha == record2->ha;\n>>   }\n>>\n>> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n>> index 6f1d4b4725..92f9845003 100644\n>> --- a/xdiff/xprepare.c\n>> +++ b/xdiff/xprepare.c\n>> @@ -131,7 +131,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n>>          xdl_free(xdf->rindex);\n>>          xdl_free(xdf->rchg - 1);\n>>          xdl_free(xdf->recs);\n>> -       xdl_cha_free(&xdf->rcha);\n>>   }\n>>\n>>\n>> @@ -146,8 +145,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>>          xdf->rchg = NULL;\n>>          xdf->recs = NULL;\n>>\n>> -       if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n>> -               goto abort;\n>>          if (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n>>                  goto abort;\n>>\n>> @@ -158,12 +155,10 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>>                          hav = xdl_hash_record(&cur, top, xpp->flags);\n>>                          if (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n>>                                  goto abort;\n>> -                       if (!(crec = xdl_cha_alloc(&xdf->rcha)))\n>> -                               goto abort;\n>> +                       crec = &xdf->recs[xdf->nrec++];\n>>                          crec->ptr = prev;\n>>                          crec->size = (long) (cur - prev);\n>>                          crec->ha = hav;\n>> -                       xdf->recs[xdf->nrec++] = crec;\n>>                          if (xdl_classify_record(pass, cf, crec) < 0)\n>>                                  goto abort;\n>>                  }\n>> @@ -263,7 +258,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n>>    */\n>>   static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n>>          long i, nm, nreff, mlim;\n>> -       xrecord_t **recs;\n>> +       xrecord_t *recs;\n>>          xdlclass_t *rcrec;\n>>          char *dis, *dis1, *dis2;\n>>          int need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n>> @@ -276,7 +271,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>>          if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n>>                  mlim = XDL_MAX_EQLIMIT;\n>>          for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n>> -               rcrec = cf->rcrecs[(*recs)->ha];\n>> +               rcrec = cf->rcrecs[recs->ha];\n>>                  nm = rcrec ? rcrec->len2 : 0;\n>>                  dis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n>>          }\n>> @@ -284,7 +279,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>>          if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n>>                  mlim = XDL_MAX_EQLIMIT;\n>>          for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n>> -               rcrec = cf->rcrecs[(*recs)->ha];\n>> +               rcrec = cf->rcrecs[recs->ha];\n>>                  nm = rcrec ? rcrec->len1 : 0;\n>>                  dis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n>>          }\n>> @@ -320,13 +315,13 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>>    */\n>>   static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n>>          long i, lim;\n>> -       xrecord_t **recs1, **recs2;\n>> +       xrecord_t *recs1, *recs2;\n>>\n>>          recs1 = xdf1->recs;\n>>          recs2 = xdf2->recs;\n>>          for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n>>               i++, recs1++, recs2++)\n>> -               if ((*recs1)->ha != (*recs2)->ha)\n>> +               if (recs1->ha != recs2->ha)\n>>                          break;\n>>\n>>          xdf1->dstart = xdf2->dstart = i;\n>> @@ -334,7 +329,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n>>          recs1 = xdf1->recs + xdf1->nrec - 1;\n>>          recs2 = xdf2->recs + xdf2->nrec - 1;\n>>          for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n>> -               if ((*recs1)->ha != (*recs2)->ha)\n>> +               if (recs1->ha != recs2->ha)\n>>                          break;\n>>\n>>          xdf1->dend = xdf1->nrec - i - 1;\n>> diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\n>> index 85848f1685..3d26cbf1ec 100644\n>> --- a/xdiff/xtypes.h\n>> +++ b/xdiff/xtypes.h\n>> @@ -45,10 +45,9 @@ typedef struct s_xrecord {\n>>   } xrecord_t;\n>>\n>>   typedef struct s_xdfile {\n>> -       chastore_t rcha;\n>> +       xrecord_t *recs;\n>>          long nrec;\n>>          long dstart, dend;\n>> -       xrecord_t **recs;\n>>          char *rchg;\n>>          long *rindex;\n>>          long nreff;\n>> diff --git a/xdiff/xutils.c b/xdiff/xutils.c\n>> index 444a108f87..332982b509 100644\n>> --- a/xdiff/xutils.c\n>> +++ b/xdiff/xutils.c\n>> @@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n>>          mmfile_t subfile1, subfile2;\n>>          xdfenv_t env;\n>>\n>> -       subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;\n>> -       subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +\n>> -               diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;\n>> -       subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;\n>> -       subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +\n>> -               diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;\n>> +       subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n>> +       subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n>> +               diff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n>> +       subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n>> +       subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n>> +               diff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n>>          if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n>>                  return -1;\n>>\n>> --\n>> gitgitgadget\n> \n> You weren't kidding with the --color-words callout; there's an awful\n> lot of places where you only change one or two characters (e.g. '->'\n> becoming '.'); that's much easier to see when viewing the diff with\n> that flag.\n> \n> Anyway, looks good.\n> \n\n"},{"id":"525993","messageId":"xmqqwm67tlg9.fsf@gitster.g","threadId":"64108","inReplyTo":"CABPp-BHdKu2nsWhpfGY4MexfChxfwv_0mqvpgrV3kbYgdCYKEg@mail.gmail.com","subject":"Re: [PATCH 08/17] xdiff: delete chastore from xdfile_t, view with --color-words","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-09T20:33:42Z","receivedAt":"2025-09-09T20:33:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Sun, Sep 7, 2025 at 12:46 PM Ezekiel Newren via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>>\n>> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> My personal bias is that things like \"view with --color-words\" makes\n> more sense to include near the end of the commit message, just before\n> the sign-offs.  Not sure if others agree on that.\n\nFWIW, I am with you.  Certainly not on the title.  It is even fine\nimmediately below the three-dash line before the diffstat.\n\nThanks.  I am enjoying to follow along the series by reading your\nreviews.\n"},{"id":"526100","messageId":"C3E33A5B-9BBD-4973-A6AA-9EB16044FCB3@gmail.com","threadId":"64108","inReplyTo":"CABPp-BHdKu2nsWhpfGY4MexfChxfwv_0mqvpgrV3kbYgdCYKEg@mail.gmail.com","subject":"Re: [PATCH 08/17] xdiff: delete chastore from xdfile_t, view with --color-words","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-09-10T22:02:13Z","receivedAt":"2025-09-10T22:02:24Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"\n> Le 9 sept. 2025 à 04:58, Elijah Newren <newren@gmail.com> a écrit :\n> \n> ﻿On Sun, Sep 7, 2025 at 12:46 PM Ezekiel Newren via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> \n>> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> \n> My personal bias is that things like \"view with --color-words\" makes\n> more sense to include near the end of the commit message, just before\n> the sign-offs.  Not sure if others agree on that.\n\nI’ve been using a Best-viewed-with trailer; I saw Peff do something similar once, I think."},{"id":"526508","messageId":"xmqqcy7qm582.fsf@gitster.g","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"Re: [PATCH 00/17] Use rust types in xdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-16T21:56:29Z","receivedAt":"2025-09-16T21:56:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> This patch series has 2 parts:\n>\n>  * Patches 1-9: Clean up xdiff, this can be merged without part 2.\n>  * Patches 10-17: Define Rust types in compat/rust_types.h and then start\n>    refactoring xdiff with Rust types. This depends on part 1.\n>\n> The cleanup in this patch series makes the structs xrecord_t and xdfile_t\n> Rust FFI friendly. My opinion is that part 1 should be merged soon, while\n> part 2 can be discussed further.\n\nI think we saw that the earlier part were read carefully by Elijah\n(and others may have read without finding anything worth commenting\non), so should we split this into two parts and start merging the\nearly 9 down to 'next' and then to 'master'?\n\nThanks.\n"},{"id":"526510","messageId":"CAH=ZcbCra35t7sxnwTvaw2oQeh6VL719v4Hx1fjDTGiU5Gmh-w@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqcy7qm582.fsf@gitster.g","subject":"Re: [PATCH 00/17] Use rust types in xdiff.","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-16T22:01:03Z","receivedAt":"2025-09-16T22:01:16Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Tue, Sep 16, 2025 at 3:56 PM Junio C Hamano <gitster@pobox.com> wrote:\n> I think we saw that the earlier part were read carefully by Elijah\n> (and others may have read without finding anything worth commenting\n> on), so should we split this into two parts and start merging the\n> early 9 down to 'next' and then to 'master'?\n\nI agree. 1-9 are ready to go. Do I need to create a new version of\nthis patch series? let it stand as is until it's been merged into\nmaster, or something else?\n"},{"id":"526537","messageId":"CABPp-BH-oaV+fJ4u50oofy54ycE5oKoYJ6O1XgEt_JfDSgXvxg@mail.gmail.com","threadId":"64108","inReplyTo":"CAH=ZcbCra35t7sxnwTvaw2oQeh6VL719v4Hx1fjDTGiU5Gmh-w@mail.gmail.com","subject":"Re: [PATCH 00/17] Use rust types in xdiff.","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-17T02:16:28Z","receivedAt":"2025-09-17T02:16:40Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Sep 16, 2025 at 3:01 PM Ezekiel Newren <ezekielnewren@gmail.com> wrote:\n>\n> On Tue, Sep 16, 2025 at 3:56 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > I think we saw that the earlier part were read carefully by Elijah\n> > (and others may have read without finding anything worth commenting\n> > on), so should we split this into two parts and start merging the\n> > early 9 down to 'next' and then to 'master'?\n>\n> I agree. 1-9 are ready to go. Do I need to create a new version of\n> this patch series? let it stand as is until it's been merged into\n> master, or something else?\n\nI think 1-9 are close to ready to go, but there's several small\ncleanups that would be nice to have in a v2 on patches 2, 4, 7, 8, 9.\nSee my comments on the patches, but it's things like adding detail to\ncommit messages or otherwise touching those up, removing orthogonal\nstyle cleanups (or making them a separate patch), and removing extra\nblank lines.  Could we get a re-roll of just the first 9 patches with\nthese addressed?  Then I think it'd be ready to merge down to 'next'.\n"},{"id":"526543","messageId":"xmqqzfation6.fsf@gitster.g","threadId":"64108","inReplyTo":"CAH=ZcbCra35t7sxnwTvaw2oQeh6VL719v4Hx1fjDTGiU5Gmh-w@mail.gmail.com","subject":"Re: [PATCH 00/17] Use rust types in xdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-17T06:22:53Z","receivedAt":"2025-09-17T06:22:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ezekiel Newren <ezekielnewren@gmail.com> writes:\n\n> On Tue, Sep 16, 2025 at 3:56 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> I think we saw that the earlier part were read carefully by Elijah\n>> (and others may have read without finding anything worth commenting\n>> on), so should we split this into two parts and start merging the\n>> early 9 down to 'next' and then to 'master'?\n>\n> I agree. 1-9 are ready to go. Do I need to create a new version of\n> this patch series? let it stand as is until it's been merged into\n> master, or something else?\n\nI have\n\n - en/xdiff-cleanup topic that ends with cb1c89e5 (xdiff: treat\n   xdfile_t.rchg like an enum, 2025-09-07);\n\n - en/xdiff-rust topic that\n   - forks from v2.51.0\n   - merges (with --no-ff) en/xdiff-cleanup\n   - has the remaining patches rebased on the above merge\n\nand 'seen' will have the first one and then the other one merged.\nLater, let's merge the former to 'next' and 'master', with the\nunderstanding that everybody is already happy with its contents and\nwe won't need to touch anything in that first-half of the topic\nuntil we merge it down to 'master'.\n\nThe remainder hasn't been commented on very much (yet).  If you have\nimprovements, please do update these topmost 8 patches so that we\ncan replace the part of en/xdiff-rust above the merge.\n\nThanks.\n\n"},{"id":"526573","messageId":"xmqqjz1xi3ri.fsf@gitster.g","threadId":"64108","inReplyTo":"CABPp-BH-oaV+fJ4u50oofy54ycE5oKoYJ6O1XgEt_JfDSgXvxg@mail.gmail.com","subject":"Re: [PATCH 00/17] Use rust types in xdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-17T13:53:53Z","receivedAt":"2025-09-17T13:53:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Tue, Sep 16, 2025 at 3:01 PM Ezekiel Newren <ezekielnewren@gmail.com> wrote:\n>>\n>> On Tue, Sep 16, 2025 at 3:56 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> > I think we saw that the earlier part were read carefully by Elijah\n>> > (and others may have read without finding anything worth commenting\n>> > on), so should we split this into two parts and start merging the\n>> > early 9 down to 'next' and then to 'master'?\n>>\n>> I agree. 1-9 are ready to go. Do I need to create a new version of\n>> this patch series? let it stand as is until it's been merged into\n>> master, or something else?\n>\n> I think 1-9 are close to ready to go, but there's several small\n> cleanups that would be nice to have in a v2 on patches 2, 4, 7, 8, 9.\n> See my comments on the patches, but it's things like adding detail to\n> commit messages or otherwise touching those up, removing orthogonal\n> style cleanups (or making them a separate patch), and removing extra\n> blank lines.  Could we get a re-roll of just the first 9 patches with\n> these addressed?  Then I think it'd be ready to merge down to 'next'.\n\nThanks for being careful.  I went back to the review thread for\nadditional and/or unaddressed comments, and I agree that we may want\na bit of final polish.\n\nThanks.\n"},{"id":"526734","messageId":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.git.git.1757274320.gitgitgadget@gmail.com","subject":"[PATCH v2 00/10] Use rust types in xdiff.","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:19Z","receivedAt":"2025-09-18T23:56:32Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"Changes since v1, to address review feedback.\n\n * Only include the clean up patches; The remaining patches will be split\n   into a separate series.\n * Commit message clarifications.\n * Minor style cleanups.\n * Performance impacts included in commit message of patch 8.\n\n\nRelevant part of the original cover letter follows:\n===================================================\n\nThis patch series involves ZERO Rust code and toolchains, which avoids the\ndebate about Rust's portability and timeline. Instead, it shows how Git can\nimmediately benefit from Rust's design choices without using it at all. The\nrationale for using Rust types on the C and Rust side is addressed in the\ncommit that creates compat/rust_types.h.\n\nThis patch series has 2 parts:\n\n * Patches 1-9: Clean up xdiff, this can be merged without part 2.\n * Patches 10-17: Define Rust types in compat/rust_types.h and then start\n   refactoring xdiff with Rust types. This depends on part 1.\n\nThe cleanup in this patch series makes the structs xrecord_t and xdfile_t\nRust FFI friendly. My opinion is that part 1 should be merged soon, while\npart 2 can be discussed further.\n\nBefore:\n\ntypedef struct s_xrecord {\n\tstruct s_xrecord *next;\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\tchastore_t rcha;\n\tlong nrec;\n\tunsigned int hbits;\n\txrecord_t **rhash;\n\tlong dstart, dend;\n\txrecord_t **recs;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n\tunsigned long *ha;\n} xdfile_t;\n\n\nAfter cleanup:\n\ntypedef struct s_xrecord {\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\txrecord_t *recs;\n\tlong nrec;\n\tlong dstart, dend;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n} xdfile_t;\n\n\n===\n\nEzekiel Newren (10):\n  xdiff: delete static forward declarations in xprepare\n  xdiff: delete local variables and initialize/free xdfile_t directly\n  xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  xdiff: delete xdl_get_rec() in xemit\n  xdiff: delete struct diffdata_t\n  xdiff: delete redundant array xdfile_t.ha\n  xdiff: delete fields ha, line, size in xdlclass_t in favor of an\n    xrecord_t\n  xdiff: delete chastore from xdfile_t\n  xdiff: delete rchg aliasing\n  xdiff: treat xdfile_t.rchg like an enum\n\n xdiff/xdiff.h      |   4 +\n xdiff/xdiffi.c     | 101 ++++++++---------\n xdiff/xdiffi.h     |  11 +-\n xdiff/xemit.c      |  38 +++----\n xdiff/xhistogram.c |  10 +-\n xdiff/xmerge.c     |  56 +++++-----\n xdiff/xpatience.c  |  18 ++--\n xdiff/xprepare.c   | 262 +++++++++++++++++----------------------------\n xdiff/xtypes.h     |   7 +-\n xdiff/xutils.c     |  12 +--\n 10 files changed, 212 insertions(+), 307 deletions(-)\n\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2048%2Fezekielnewren%2Fuse_rust_types_in_xdiff-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2048/ezekielnewren/use_rust_types_in_xdiff-v2\nPull-Request: https://github.com/git/git/pull/2048\n\nRange-diff vs v1:\n\n  1:  9cf9d09c07 !  1:  784cffcef5 xdiff: delete static forward declarations in xprepare\n     @@ Commit message\n          Move xdl_prepare_env() later in the file to avoid the need\n          for static forward declarations.\n      \n     +    Best-viewed-with: --color-moved\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xprepare.c ##\n  2:  15832ad271 !  2:  b79157e64f xdiff: delete local variables and initialize/free xdfile_t directly\n     @@ Metadata\n       ## Commit message ##\n          xdiff: delete local variables and initialize/free xdfile_t directly\n      \n     -    xdl_prepare_ctx() uses local variables and assigns them to the\n     -    corresponding xdfile_t fields if there are no errors. Delete them and\n     -    use the fields of xdfile_t directly.\n     +    These local variables are essentially a hand-rolled additional\n     +    implementation of xdl_free_ctx() inlined into xdl_prepare_ctx(). Modify\n     +    the code to use the existing xdl_free_ctx() function so there aren't\n     +    two ways to free such variables.\n      \n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n     @@ xdiff/xprepare.c: static int xdl_classify_record(unsigned int pass, xdlclassifie\n       \n      +static void xdl_free_ctx(xdfile_t *xdf)\n      +{\n     -+\n      +\txdl_free(xdf->rhash);\n      +\txdl_free(xdf->rindex);\n      +\txdl_free(xdf->rchg - 1);\n  3:  7d5e387916 !  3:  2e8de5be03 xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n     @@ Commit message\n          xrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,\n          but never used for anything by the code. Remove them.\n      \n     +    Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xprepare.c ##\n     @@ xdiff/xprepare.c: static int xdl_classify_record(unsigned int pass, xdlclassifie\n       \treturn 0;\n       }\n       \n     -@@ xdiff/xprepare.c: static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n     + \n       static void xdl_free_ctx(xdfile_t *xdf)\n       {\n     - \n      -\txdl_free(xdf->rhash);\n       \txdl_free(xdf->rindex);\n       \txdl_free(xdf->rchg - 1);\n  4:  ab17d8c23f !  4:  ddfee67e06 xdiff: delete xdl_get_rec() in xemit\n     @@ xdiff/xemit.c\n       #include \"xinclude.h\"\n       \n      -static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n     - \n     +-\n      -\t*rec = xdf->recs[ri]->ptr;\n      -\n      -\treturn xdf->recs[ri]->size;\n     @@ xdiff/xemit.c\n      +{\n      +\txrecord_t *rec = xdf->recs[ri];\n       \n     -+\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n     ++\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n       \t\treturn -1;\n     --\t}\n     + \t}\n       \n     - \treturn 0;\n     - }\n      @@ xdiff/xemit.c: static long def_ff(const char *rec, long len, char *buf, long sz)\n       static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n       \t\t\t   char *buf, long sz)\n  5:  6cf371ec13 =  5:  807ce3e5aa xdiff: delete struct diffdata_t\n  6:  bff4568602 !  6:  0bacb1191d xdiff: delete redundant array xdfile_t.ha\n     @@ Commit message\n          When 0 <= i < xdfile_t.nreff the following is true:\n          xdfile_t.ha[i] == xdfile_t.recs[xdfile_t.rindex[i]]\n      \n     +    This makes the code about 5% slower. The fields rindex and ha are\n     +    specific to the classic diff (myers and minimal). I plan on creating a\n     +    struct for classic diff, but there'a alot of cleanup that needs to be\n     +    done before that can happen and leaving ha in would make those cleanups\n     +    harder to follow.\n     +\n     +    A subsequent commit will delete the chastore cha from xdfile_t. That\n     +    later commit will investigate deleting ha and cha independently and\n     +    together.\n     +\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xdiffi.c ##\n     @@ xdiff/xdiffi.c: int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n      \n       ## xdiff/xprepare.c ##\n      @@ xdiff/xprepare.c: static void xdl_free_ctx(xdfile_t *xdf)\n     - \n     + {\n       \txdl_free(xdf->rindex);\n       \txdl_free(xdf->rchg - 1);\n      -\txdl_free(xdf->ha);\n  7:  db3d4e9a89 !  7:  e1e94107c9 xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t\n     @@ Metadata\n       ## Commit message ##\n          xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t\n      \n     +    The fields from xdlclass_t are aliases of xrecord_t:\n     +    xdlclass_t.line -> xrecord_t.ptr\n     +    xdlclass_t.size -> xrecord_t.size\n     +    xdlclass_t.ha   -> xrecord_t.ha\n     +\n     +    Remove aliasing from xdlclass_t, to reduce future refactoring mistakes.\n     +\n     +    Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xprepare.c ##\n  8:  e7d1933d1c !  8:  fae26d2a04 xdiff: delete chastore from xdfile_t, view with --color-words\n     @@ Metadata\n      Author: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## Commit message ##\n     -    xdiff: delete chastore from xdfile_t, view with --color-words\n     +    xdiff: delete chastore from xdfile_t\n      \n     -    The chastore_t type is very unfriendly to Rust FFI. It's also redundant\n     -    since 'recs' is a vector type that grows every time an xrecord_t is\n     -    added.\n     +    xdfile_t currently uses chastore_t which is an arena allocator. I\n     +    think that xrecord_t used to be a linked list and recs didn't exist\n     +    originally. When recs was added I think they forgot to remove\n     +    xdfile_t.next, but was overlooked. This dual data structure setup\n     +    makes the code somewhat confusing.\n      \n     +    Additionally the C type chastore_t isn't FFI friendly, and provides\n     +    little to no performance benefit over using realloc to grow an array.\n     +\n     +    Performance impact of deleting fields from xdfile_t:\n     +    Deleting ha is about 5% slower.\n     +    Deleting cha is about 5% faster.\n     +\n     +    Delete ha, but keep cha\n     +      time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n     +      Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +        Time (mean ± σ):      1.269 s ±  0.017 s    [User: 1.135 s, System: 0.128 s]\n     +        Range (min … max):    1.249 s …  1.286 s    10 runs\n     +\n     +      Benchmark 2: build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +        Time (mean ± σ):      1.339 s ±  0.017 s    [User: 1.234 s, System: 0.099 s]\n     +        Range (min … max):    1.320 s …  1.358 s    10 runs\n     +\n     +      Summary\n     +        build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n     +          1.06 ± 0.02 times faster than build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +\n     +    Delete cha, but keep ha\n     +      time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n     +      Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +        Time (mean ± σ):      1.290 s ±  0.001 s    [User: 1.154 s, System: 0.130 s]\n     +        Range (min … max):    1.288 s …  1.292 s    10 runs\n     +\n     +      Benchmark 2: build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +        Time (mean ± σ):      1.232 s ±  0.017 s    [User: 1.105 s, System: 0.121 s]\n     +        Range (min … max):    1.205 s …  1.249 s    10 runs\n     +\n     +      Summary\n     +        build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n     +          1.05 ± 0.01 times faster than build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +\n     +    Delete ha AND chastore\n     +      time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha_and_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n     +      Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +        Time (mean ± σ):      1.291 s ±  0.002 s    [User: 1.156 s, System: 0.129 s]\n     +        Range (min … max):    1.287 s …  1.295 s    10 runs\n     +\n     +      Benchmark 2: build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +        Time (mean ± σ):      1.306 s ±  0.001 s    [User: 1.195 s, System: 0.105 s]\n     +        Range (min … max):    1.305 s …  1.308 s    10 runs\n     +\n     +      Summary\n     +        build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n     +          1.01 ± 0.00 times faster than build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n     +\n     +    Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xdiffi.c ##\n     @@ xdiff/xemit.c\n      -\txrecord_t *rec = xdf->recs[ri];\n      +\txrecord_t *rec = &xdf->recs[ri];\n       \n     - \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n     + \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n       \t\treturn -1;\n      @@ xdiff/xemit.c: static long def_ff(const char *rec, long len, char *buf, long sz)\n       static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n  -:  ---------- >  9:  fd54135560 xdiff: delete rchg aliasing\n  9:  d1657f5101 ! 10:  9a5ac3c488 xdiff: treat xdfile_t.rchg like an enum\n     @@ Commit message\n          make the code easier to follow. Perhaps 'rchg' should be renamed to\n          'changed'?\n      \n     +    A few of the code changes might appear to change behavior, such as:\n     +        -    while (xdf->rchg[g->start - 1])\n     +        +    while (xdf->rchg[g->start - 1] == YES)\n     +    because it appears the value of MAYBE is being ignored.  However, MAYBE\n     +    is only ever assigned as a value to a temporary array (dis1 & dis2) and\n     +    then as a last step use that temporary array to decide if it wants to\n     +    change xdfile_t.rchg[i] to YES or leave it as NO.  As such, rchg will\n     +    never have a value of MAYBE and thus there is no behavioral change.\n     +\n     +    Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xdiff.h ##\n     @@ xdiff/xdiffi.c: static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n       \t\t\tg->start--;\n       \n       \t\treturn 0;\n     -@@ xdiff/xdiffi.c: int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n     - \n     - int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n     - \txdchange_t *cscr = NULL, *xch;\n     --\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n     - \tlong i1, i2, l1, l2;\n     - \n     - \t/*\n     - \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n     - \t */\n     - \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n     --\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n     --\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n     --\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n     -+\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n     -+\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n     -+\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n     - \n     - \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n     - \t\t\t\txdl_free_script(cscr);\n      \n       ## xdiff/xhistogram.c ##\n      @@ xdiff/xhistogram.c: redo:\n 10:  2a7d5b05c1 <  -:  ---------- compat/rust_types.h: define rust primitive types\n 11:  ec54380ed3 <  -:  ---------- xdiff: include compat/rust_types.h\n 12:  182f93b60b <  -:  ---------- xdiff: make xrecord_t.ptr a u8 instead of char\n 13:  f7aaef8f36 <  -:  ---------- xdiff: make xrecord_t.size a usize instead of long\n 14:  af96763036 <  -:  ---------- xdiff: split xrecord_t.ha into line_hash and minimal_perfect_hash\n 15:  0a180f69ff <  -:  ---------- xdiff: make xdfile_t.nrec a usize instead of long\n 16:  f4eda35e24 <  -:  ---------- xdiff: make xdfile_t.nreff a usize instead of long\n 17:  00401e775a <  -:  ---------- xdiff: change the types of dstart, dend, rchg, and rindex in xdfile_t\n\n-- \ngitgitgadget\n"},{"id":"526735","messageId":"784cffcef564b31a32c401b35f33610b85126f7b.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:20Z","receivedAt":"2025-09-18T23:56:33Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nMove xdl_prepare_env() later in the file to avoid the need\nfor static forward declarations.\n\nBest-viewed-with: --color-moved\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 116 ++++++++++++++++++++---------------------------\n 1 file changed, 50 insertions(+), 66 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e1d4017b2d..a45c5ee208 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -53,21 +53,6 @@ typedef struct s_xdlclassifier {\n \n \n \n-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);\n-static void xdl_free_classifier(xdlclassifier_t *cf);\n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec);\n-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n-\t\t\t   xdlclassifier_t *cf, xdfile_t *xdf);\n-static void xdl_free_ctx(xdfile_t *xdf);\n-static int xdl_clean_mmatch(char const *dis, long i, long s, long e);\n-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-\n-\n-\n-\n static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {\n \tcf->flags = flags;\n \n@@ -242,57 +227,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {\n }\n \n \n-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n-\t\t    xdfenv_t *xe) {\n-\tlong enl1, enl2, sample;\n-\txdlclassifier_t cf;\n-\n-\tmemset(&cf, 0, sizeof(cf));\n-\n-\t/*\n-\t * For histogram diff, we can afford a smaller sample size and\n-\t * thus a poorer estimate of the number of lines, as the hash\n-\t * table (rhash) won't be filled up/grown. The number of lines\n-\t * (nrecs) will be updated correctly anyway by\n-\t * xdl_prepare_ctx().\n-\t */\n-\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n-\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n-\n-\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n-\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n-\n-\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n-\t\treturn -1;\n-\n-\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n-\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n-\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n-\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf2);\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\txdl_free_classifier(&cf);\n-\n-\treturn 0;\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n@@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2\n \n \treturn 0;\n }\n+\n+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n+\t\t    xdfenv_t *xe) {\n+\tlong enl1, enl2, sample;\n+\txdlclassifier_t cf;\n+\n+\tmemset(&cf, 0, sizeof(cf));\n+\n+\t/*\n+\t * For histogram diff, we can afford a smaller sample size and\n+\t * thus a poorer estimate of the number of lines, as the hash\n+\t * table (rhash) won't be filled up/grown. The number of lines\n+\t * (nrecs) will be updated correctly anyway by\n+\t * xdl_prepare_ctx().\n+\t */\n+\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n+\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n+\n+\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n+\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n+\n+\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n+\t\treturn -1;\n+\n+\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n+\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n+\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n+\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf2);\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t    }\n+\n+\txdl_free_classifier(&cf);\n+\n+\treturn 0;\n+}\n-- \ngitgitgadget\n\n"},{"id":"526736","messageId":"b79157e64f0950d25a23c50a8ea83cfddf67ddf5.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 02/10] xdiff: delete local variables and initialize/free xdfile_t directly","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:21Z","receivedAt":"2025-09-18T23:56:36Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThese local variables are essentially a hand-rolled additional\nimplementation of xdl_free_ctx() inlined into xdl_prepare_ctx(). Modify\nthe code to use the existing xdl_free_ctx() function so there aren't\ntwo ways to free such variables.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 78 +++++++++++++++++++-----------------------------\n 1 file changed, 30 insertions(+), 48 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex a45c5ee208..fe02fd7925 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -134,99 +134,81 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n }\n \n \n+static void xdl_free_ctx(xdfile_t *xdf)\n+{\n+\txdl_free(xdf->rhash);\n+\txdl_free(xdf->rindex);\n+\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->ha);\n+\txdl_free(xdf->recs);\n+\txdl_cha_free(&xdf->rcha);\n+}\n+\n+\n static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n \t\t\t   xdlclassifier_t *cf, xdfile_t *xdf) {\n-\tunsigned int hbits;\n-\tlong nrec, hsize, bsize;\n+\tlong bsize;\n \tunsigned long hav;\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n-\txrecord_t **recs;\n-\txrecord_t **rhash;\n-\tunsigned long *ha;\n-\tchar *rchg;\n-\tlong *rindex;\n \n-\tha = NULL;\n-\trindex = NULL;\n-\trchg = NULL;\n-\trhash = NULL;\n-\trecs = NULL;\n+\txdf->ha = NULL;\n+\txdf->rindex = NULL;\n+\txdf->rchg = NULL;\n+\txdf->rhash = NULL;\n+\txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n \t\tgoto abort;\n-\tif (!XDL_ALLOC_ARRAY(recs, narec))\n+\tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\thbits = xdl_hashbits((unsigned int) narec);\n-\thsize = 1 << hbits;\n-\tif (!XDL_CALLOC_ARRAY(rhash, hsize))\n+\txdf->hbits = xdl_hashbits((unsigned int) narec);\n+\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n \t\tgoto abort;\n \n-\tnrec = 0;\n+\txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n \t\t\tprev = cur;\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n-\t\t\tif (XDL_ALLOC_GROW(recs, nrec + 1, narec))\n+\t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n \t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n \t\t\t\tgoto abort;\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\trecs[nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)\n+\t\t\txdf->recs[xdf->nrec++] = crec;\n+\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(rchg, nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n-\t\tif (!XDL_ALLOC_ARRAY(rindex, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(ha, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n \t\t\tgoto abort;\n \t}\n \n-\txdf->nrec = nrec;\n-\txdf->recs = recs;\n-\txdf->hbits = hbits;\n-\txdf->rhash = rhash;\n-\txdf->rchg = rchg + 1;\n-\txdf->rindex = rindex;\n+\txdf->rchg += 1;\n \txdf->nreff = 0;\n-\txdf->ha = ha;\n \txdf->dstart = 0;\n-\txdf->dend = nrec - 1;\n+\txdf->dend = xdf->nrec - 1;\n \n \treturn 0;\n \n abort:\n-\txdl_free(ha);\n-\txdl_free(rindex);\n-\txdl_free(rchg);\n-\txdl_free(rhash);\n-\txdl_free(recs);\n-\txdl_cha_free(&xdf->rcha);\n+\txdl_free_ctx(xdf);\n \treturn -1;\n }\n \n \n-static void xdl_free_ctx(xdfile_t *xdf) {\n-\n-\txdl_free(xdf->rhash);\n-\txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n-\txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n-- \ngitgitgadget\n\n"},{"id":"526737","messageId":"2e8de5be03f7166059d735a99573520e35ff9a31.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 03/10] xdiff: delete unnecessary fields from xrecord_t and xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:22Z","receivedAt":"2025-09-18T23:56:38Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,\nbut never used for anything by the code. Remove them.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 15 ++-------------\n xdiff/xtypes.h   |  3 ---\n 2 files changed, 2 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex fe02fd7925..7acca1cb38 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -91,8 +91,7 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n }\n \n \n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec) {\n+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n \tchar const *line;\n \txdlclass_t *rcrec;\n@@ -126,17 +125,12 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \n \trec->ha = (unsigned long) rcrec->idx;\n \n-\thi = (long) XDL_HASHLONG(rec->ha, hbits);\n-\trec->next = rhash[hi];\n-\trhash[hi] = rec;\n-\n \treturn 0;\n }\n \n \n static void xdl_free_ctx(xdfile_t *xdf)\n {\n-\txdl_free(xdf->rhash);\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->ha);\n@@ -155,7 +149,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n-\txdf->rhash = NULL;\n \txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n@@ -163,10 +156,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\txdf->hbits = xdl_hashbits((unsigned int) narec);\n-\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n-\t\tgoto abort;\n-\n \txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n@@ -180,7 +169,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n \t\t\txdf->recs[xdf->nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n+\t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8442bd436e..8b8467360e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -39,7 +39,6 @@ typedef struct s_chastore {\n } chastore_t;\n \n typedef struct s_xrecord {\n-\tstruct s_xrecord *next;\n \tchar const *ptr;\n \tlong size;\n \tunsigned long ha;\n@@ -48,8 +47,6 @@ typedef struct s_xrecord {\n typedef struct s_xdfile {\n \tchastore_t rcha;\n \tlong nrec;\n-\tunsigned int hbits;\n-\txrecord_t **rhash;\n \tlong dstart, dend;\n \txrecord_t **recs;\n \tchar *rchg;\n-- \ngitgitgadget\n\n"},{"id":"526738","messageId":"ddfee67e06bb56cfa8f11187dc27135bfce63647.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 04/10] xdiff: delete xdl_get_rec() in xemit","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:23Z","receivedAt":"2025-09-18T23:56:39Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThis function aliases the fields of xrecord_t, which makes it harder\nto track the usages of those fields. Delete it.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 38 +++++++++++++-------------------------\n 1 file changed, 13 insertions(+), 25 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 1d40c9cb40..b3793e81e2 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -22,21 +22,11 @@\n \n #include \"xinclude.h\"\n \n-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n-\n-\t*rec = xdf->recs[ri]->ptr;\n-\n-\treturn xdf->recs[ri]->size;\n-}\n-\n-\n-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n-\tlong size, psize = strlen(pre);\n-\tchar const *rec;\n-\n-\tsize = xdl_get_rec(xdf, ri, &rec);\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n+{\n+\txrecord_t *rec = xdf->recs[ri];\n \n+\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n \t\treturn -1;\n \t}\n \n@@ -120,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\txrecord_t *rec = xdf->recs[ri];\n+\n \tif (!xecfg->find_func)\n-\t\treturn def_ff(rec, len, buf, sz);\n-\treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n+\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n+\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n }\n \n static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n@@ -160,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\txrecord_t *rec = xdf->recs[ri];\n+\tlong i = 0;\n \n-\twhile (len > 0 && XDL_ISSPACE(*rec)) {\n-\t\trec++;\n-\t\tlen--;\n-\t}\n-\treturn !len;\n+\tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n+\n+\treturn i == rec->size;\n }\n \n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n-- \ngitgitgadget\n\n"},{"id":"526739","messageId":"807ce3e5aae0253dd58c7a4d0bd04ca341b070c8.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 05/10] xdiff: delete struct diffdata_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:24Z","receivedAt":"2025-09-18T23:56:40Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nEvery field in this struct is an alias for a certain field in xdfile_t.\n\ndiffdata_t.nrec   -> xdfile_t.nreff\ndiffdata_t.ha     -> xdfile_t.ha\ndiffdata_t.rindex -> xdfile_t.rindex\ndiffdata_t.rchg   -> xdfile_t.rchg\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 32 ++++++++------------------------\n xdiff/xdiffi.h | 11 ++---------\n 2 files changed, 10 insertions(+), 33 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 5a96e36dfb..bbf0161f84 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -257,10 +257,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n  * sub-boxes by calling the box splitting function. Note that the real job\n  * (marking changed lines) is done in the two boundary reaching checks.\n  */\n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;\n+\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n@@ -273,17 +273,11 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t * be obviously changed.\n \t */\n \tif (off1 == lim1) {\n-\t\tchar *rchg2 = dd2->rchg;\n-\t\tlong *rindex2 = dd2->rindex;\n-\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\trchg2[rindex2[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n \t} else if (off2 == lim2) {\n-\t\tchar *rchg1 = dd1->rchg;\n-\t\tlong *rindex1 = dd1->rindex;\n-\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\trchg1[rindex1[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -300,9 +294,9 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t\t/*\n \t\t * ... et Impera.\n \t\t */\n-\t\tif (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,\n+\t\tif (xdl_recs_cmp(xdf1, off1, spl.i1, xdf2, off2, spl.i2,\n \t\t\t\t kvdf, kvdb, spl.min_lo, xenv) < 0 ||\n-\t\t    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,\n+\t\t    xdl_recs_cmp(xdf1, spl.i1, lim1, xdf2, spl.i2, lim2,\n \t\t\t\t kvdf, kvdb, spl.min_hi, xenv) < 0) {\n \n \t\t\treturn -1;\n@@ -318,7 +312,6 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \tlong ndiags;\n \tlong *kvd, *kvdf, *kvdb;\n \txdalgoenv_t xenv;\n-\tdiffdata_t dd1, dd2;\n \tint res;\n \n \tif (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)\n@@ -357,16 +350,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \txenv.snake_cnt = XDL_SNAKE_CNT;\n \txenv.heur_min = XDL_HEUR_MIN_COST;\n \n-\tdd1.nrec = xe->xdf1.nreff;\n-\tdd1.ha = xe->xdf1.ha;\n-\tdd1.rchg = xe->xdf1.rchg;\n-\tdd1.rindex = xe->xdf1.rindex;\n-\tdd2.nrec = xe->xdf2.nreff;\n-\tdd2.ha = xe->xdf2.ha;\n-\tdd2.rchg = xe->xdf2.rchg;\n-\tdd2.rindex = xe->xdf2.rindex;\n-\n-\tres = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,\n+\tres = xdl_recs_cmp(&xe->xdf1, 0, xe->xdf1.nreff, &xe->xdf2, 0, xe->xdf2.nreff,\n \t\t\t   kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,\n \t\t\t   &xenv);\n \txdl_free(kvd);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 126c9d8ff4..49e52c67f9 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -24,13 +24,6 @@\n #define XDIFFI_H\n \n \n-typedef struct s_diffdata {\n-\tlong nrec;\n-\tunsigned long const *ha;\n-\tlong *rindex;\n-\tchar *rchg;\n-} diffdata_t;\n-\n typedef struct s_xdalgoenv {\n \tlong mxcost;\n \tlong snake_cnt;\n@@ -46,8 +39,8 @@ typedef struct s_xdchange {\n \n \n \n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);\n int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\txdfenv_t *xe);\n-- \ngitgitgadget\n\n"},{"id":"526740","messageId":"0bacb1191dad2748d2afa79665f1293b0381bde1.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 06/10] xdiff: delete redundant array xdfile_t.ha","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:25Z","receivedAt":"2025-09-18T23:56:41Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen 0 <= i < xdfile_t.nreff the following is true:\nxdfile_t.ha[i] == xdfile_t.recs[xdfile_t.rindex[i]]\n\nThis makes the code about 5% slower. The fields rindex and ha are\nspecific to the classic diff (myers and minimal). I plan on creating a\nstruct for classic diff, but there'a alot of cleanup that needs to be\ndone before that can happen and leaving ha in would make those cleanups\nharder to follow.\n\nA subsequent commit will delete the chastore cha from xdfile_t. That\nlater commit will investigate deleting ha and cha independently and\ntogether.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c   | 24 ++++++++++++++----------\n xdiff/xprepare.c | 12 ++----------\n xdiff/xtypes.h   |  1 -\n 3 files changed, 16 insertions(+), 21 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex bbf0161f84..11cd090b53 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -22,6 +22,11 @@\n \n #include \"xinclude.h\"\n \n+static unsigned long get_hash(xdfile_t *xdf, long index)\n+{\n+\treturn xdf->recs[xdf->rindex[index]]->ha;\n+}\n+\n #define XDL_MAX_COST_MIN 256\n #define XDL_HEUR_MIN_COST 256\n #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)\n@@ -42,8 +47,8 @@ typedef struct s_xdpsplit {\n  * using this algorithm, so a little bit of heuristic is needed to cut the\n  * search and to return a suboptimal point.\n  */\n-static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n-\t\t      unsigned long const *ha2, long off2, long lim2,\n+static long xdl_split(xdfile_t *xdf1, long off1, long lim1,\n+\t\t      xdfile_t *xdf2, long off2, long lim2,\n \t\t      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,\n \t\t      xdalgoenv_t *xenv) {\n \tlong dmin = off1 - lim2, dmax = lim1 - off2;\n@@ -87,7 +92,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdf[d + 1];\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);\n+\t\t\tfor (; i1 < lim1 && i2 < lim2 && get_hash(xdf1, i1) == get_hash(xdf2, i2); i1++, i2++);\n \t\t\tif (i1 - prev1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdf[d] = i1;\n@@ -124,7 +129,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdb[d + 1] - 1;\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);\n+\t\t\tfor (; i1 > off1 && i2 > off2 && get_hash(xdf1, i1 - 1) == get_hash(xdf2, i2 - 1); i1--, i2--);\n \t\t\tif (prev1 - i1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdb[d] = i1;\n@@ -159,7 +164,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&\n \t\t\t\t    off2 + xenv->snake_cnt <= i2 && i2 < lim2) {\n-\t\t\t\t\tfor (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)\n+\t\t\t\t\tfor (k = 1; get_hash(xdf1, i1 - k) == get_hash(xdf2, i2 - k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -183,7 +188,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&\n \t\t\t\t    off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {\n-\t\t\t\t\tfor (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)\n+\t\t\t\t\tfor (k = 0; get_hash(xdf1, i1 + k) == get_hash(xdf2, i2 + k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt - 1) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -260,13 +265,12 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n \t */\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, off1) == get_hash(xdf2, off2); off1++, off2++);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, lim1 - 1) == get_hash(xdf2, lim2 - 1); lim1--, lim2--);\n \n \t/*\n \t * If one dimension is empty, then all records on the other one must\n@@ -285,7 +289,7 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t/*\n \t\t * Divide ...\n \t\t */\n-\t\tif (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,\n+\t\tif (xdl_split(xdf1, off1, lim1, xdf2, off2, lim2, kvdf, kvdb,\n \t\t\t      need_min, &spl, xenv) < 0) {\n \n \t\t\treturn -1;\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 7acca1cb38..c39b65fea9 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -133,7 +133,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n {\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n \txdl_free(xdf->recs);\n \txdl_cha_free(&xdf->rcha);\n }\n@@ -146,7 +145,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n \n-\txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n@@ -181,8 +179,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n \t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n-\t\t\tgoto abort;\n \t}\n \n \txdf->rchg += 1;\n@@ -300,9 +296,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (dis1[i] == 1 ||\n \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n-\t\t\txdf1->rindex[nreff] = i;\n-\t\t\txdf1->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf1->rchg[i] = 1;\n \t}\n@@ -312,9 +306,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf2->dend; i++, recs++) {\n \t\tif (dis2[i] == 1 ||\n \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n-\t\t\txdf2->rindex[nreff] = i;\n-\t\t\txdf2->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf2->rchg[i] = 1;\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8b8467360e..85848f1685 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -52,7 +52,6 @@ typedef struct s_xdfile {\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\n-\tunsigned long *ha;\n } xdfile_t;\n \n typedef struct s_xdfenv {\n-- \ngitgitgadget\n\n"},{"id":"526741","messageId":"e1e94107c9722b751d6111460b17e02a7ffd96d1.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 07/10] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:26Z","receivedAt":"2025-09-18T23:56:43Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe fields from xdlclass_t are aliases of xrecord_t:\nxdlclass_t.line -> xrecord_t.ptr\nxdlclass_t.size -> xrecord_t.size\nxdlclass_t.ha   -> xrecord_t.ha\n\nRemove aliasing from xdlclass_t, to reduce future refactoring mistakes.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 16 ++++++----------\n 1 file changed, 6 insertions(+), 10 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex c39b65fea9..43cebf6721 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -32,9 +32,7 @@\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n-\tunsigned long ha;\n-\tchar const *line;\n-\tlong size;\n+\txrecord_t rec;\n \tlong idx;\n \tlong len1, len2;\n } xdlclass_t;\n@@ -93,14 +91,12 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n \n static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n-\tchar const *line;\n \txdlclass_t *rcrec;\n \n-\tline = rec->ptr;\n \thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n-\t\tif (rcrec->ha == rec->ha &&\n-\t\t\t\txdl_recmatch(rcrec->line, rcrec->size,\n+\t\tif (rcrec->rec.ha == rec->ha &&\n+\t\t\t\txdl_recmatch(rcrec->rec.ptr, rcrec->rec.size,\n \t\t\t\t\trec->ptr, rec->size, cf->flags))\n \t\t\tbreak;\n \n@@ -113,9 +109,9 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \t\tif (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))\n \t\t\t\treturn -1;\n \t\tcf->rcrecs[rcrec->idx] = rcrec;\n-\t\trcrec->line = line;\n-\t\trcrec->size = rec->size;\n-\t\trcrec->ha = rec->ha;\n+\t\trcrec->rec.ptr = rec->ptr;\n+\t\trcrec->rec.size = rec->size;\n+\t\trcrec->rec.ha = rec->ha;\n \t\trcrec->len1 = rcrec->len2 = 0;\n \t\trcrec->next = cf->rchash[hi];\n \t\tcf->rchash[hi] = rcrec;\n-- \ngitgitgadget\n\n"},{"id":"526742","messageId":"fae26d2a0438cd0c6ca6d9f26cbcae11fdf0a4bb.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 08/10] xdiff: delete chastore from xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:27Z","receivedAt":"2025-09-18T23:56:46Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxdfile_t currently uses chastore_t which is an arena allocator. I\nthink that xrecord_t used to be a linked list and recs didn't exist\noriginally. When recs was added I think they forgot to remove\nxdfile_t.next, but was overlooked. This dual data structure setup\nmakes the code somewhat confusing.\n\nAdditionally the C type chastore_t isn't FFI friendly, and provides\nlittle to no performance benefit over using realloc to grow an array.\n\nPerformance impact of deleting fields from xdfile_t:\nDeleting ha is about 5% slower.\nDeleting cha is about 5% faster.\n\nDelete ha, but keep cha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.269 s ±  0.017 s    [User: 1.135 s, System: 0.128 s]\n    Range (min … max):    1.249 s …  1.286 s    10 runs\n\n  Benchmark 2: build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.339 s ±  0.017 s    [User: 1.234 s, System: 0.099 s]\n    Range (min … max):    1.320 s …  1.358 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.06 ± 0.02 times faster than build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete cha, but keep ha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.290 s ±  0.001 s    [User: 1.154 s, System: 0.130 s]\n    Range (min … max):    1.288 s …  1.292 s    10 runs\n\n  Benchmark 2: build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.232 s ±  0.017 s    [User: 1.105 s, System: 0.121 s]\n    Range (min … max):    1.205 s …  1.249 s    10 runs\n\n  Summary\n    build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.05 ± 0.01 times faster than build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete ha AND chastore\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha_and_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.291 s ±  0.002 s    [User: 1.156 s, System: 0.129 s]\n    Range (min … max):    1.287 s …  1.295 s    10 runs\n\n  Benchmark 2: build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.306 s ±  0.001 s    [User: 1.195 s, System: 0.105 s]\n    Range (min … max):    1.305 s …  1.308 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.01 ± 0.00 times faster than build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 24 ++++++++++----------\n xdiff/xemit.c      |  6 ++---\n xdiff/xhistogram.c |  2 +-\n xdiff/xmerge.c     | 56 +++++++++++++++++++++++-----------------------\n xdiff/xpatience.c  | 10 ++++-----\n xdiff/xprepare.c   | 19 ++++++----------\n xdiff/xtypes.h     |  3 +--\n xdiff/xutils.c     | 12 +++++-----\n 8 files changed, 63 insertions(+), 69 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 11cd090b53..a66125d44a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -24,7 +24,7 @@\n \n static unsigned long get_hash(xdfile_t *xdf, long index)\n {\n-\treturn xdf->recs[xdf->rindex[index]]->ha;\n+\treturn xdf->recs[xdf->rindex[index]].ha;\n }\n \n #define XDL_MAX_COST_MIN 256\n@@ -489,13 +489,13 @@ static void measure_split(const xdfile_t *xdf, long split,\n \t\tm->indent = -1;\n \t} else {\n \t\tm->end_of_file = 0;\n-\t\tm->indent = get_indent(xdf->recs[split]);\n+\t\tm->indent = get_indent(&xdf->recs[split]);\n \t}\n \n \tm->pre_blank = 0;\n \tm->pre_indent = -1;\n \tfor (i = split - 1; i >= 0; i--) {\n-\t\tm->pre_indent = get_indent(xdf->recs[i]);\n+\t\tm->pre_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->pre_indent != -1)\n \t\t\tbreak;\n \t\tm->pre_blank += 1;\n@@ -508,7 +508,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n \tm->post_blank = 0;\n \tm->post_indent = -1;\n \tfor (i = split + 1; i < xdf->nrec; i++) {\n-\t\tm->post_indent = get_indent(xdf->recs[i]);\n+\t\tm->post_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->post_indent != -1)\n \t\t\tbreak;\n \t\tm->post_blank += 1;\n@@ -752,7 +752,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n-\t    recs_match(xdf->recs[g->start], xdf->recs[g->end])) {\n+\t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n \t\txdf->rchg[g->start++] = 0;\n \t\txdf->rchg[g->end++] = 1;\n \n@@ -773,7 +773,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n-\t    recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {\n+\t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n \t\txdf->rchg[--g->start] = 1;\n \t\txdf->rchg[--g->end] = 0;\n \n@@ -988,16 +988,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n \t\tint ignore = 1;\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tlong i;\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\txch->ignore = ignore;\n \t}\n@@ -1021,7 +1021,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \txdchange_t *xch;\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tint ignore = 1;\n \t\tlong i;\n \n@@ -1033,11 +1033,11 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\txch->ignore = ignore;\n \t}\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex b3793e81e2..79c14f8b4c 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -24,7 +24,7 @@\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n \t\treturn -1;\n@@ -110,7 +110,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n@@ -150,7 +150,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \tlong i = 0;\n \n \tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 040d81e0bc..4d857e8ae2 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -86,7 +86,7 @@ struct region {\n \t((LINE_MAP(index, ptr))->cnt)\n \n #define REC(env, s, l) \\\n-\t(env->xdf##s.recs[l - 1])\n+\t(&env->xdf##s.recs[l - 1])\n \n static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n {\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex af40c88a5b..fd600cbb5d 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \t\tint line_count, long flags)\n {\n \tint i;\n-\txrecord_t **rec1 = xe1->xdf2.recs + i1;\n-\txrecord_t **rec2 = xe2->xdf2.recs + i2;\n+\txrecord_t *rec1 = xe1->xdf2.recs + i1;\n+\txrecord_t *rec2 = xe2->xdf2.recs + i2;\n \n \tfor (i = 0; i < line_count; i++) {\n-\t\tint result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,\n-\t\t\trec2[i]->ptr, rec2[i]->size, flags);\n+\t\tint result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n+\t\t\trec2[i].ptr, rec2[i].size, flags);\n \t\tif (!result)\n \t\t\treturn -1;\n \t}\n@@ -111,7 +111,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \n static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)\n {\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \tint size = 0;\n \n \trecs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n@@ -119,12 +119,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n \tif (count < 1)\n \t\treturn 0;\n \n-\tfor (i = 0; i < count; size += recs[i++]->size)\n+\tfor (i = 0; i < count; size += recs[i++].size)\n \t\tif (dest)\n-\t\t\tmemcpy(dest + size, recs[i]->ptr, recs[i]->size);\n+\t\t\tmemcpy(dest + size, recs[i].ptr, recs[i].size);\n \tif (add_nl) {\n-\t\ti = recs[count - 1]->size;\n-\t\tif (i == 0 || recs[count - 1]->ptr[i - 1] != '\\n') {\n+\t\ti = recs[count - 1].size;\n+\t\tif (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n \t\t\tif (needs_cr) {\n \t\t\t\tif (dest)\n \t\t\t\t\tdest[size] = '\\r';\n@@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)\n \n \tif (i < file->nrec - 1)\n \t\t/* All lines before the last *must* end in LF */\n-\t\treturn (size = file->recs[i]->size) > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\treturn (size = file->recs[i].size) > 1 &&\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!file->nrec)\n \t\t/* Cannot determine eol style from empty file */\n \t\treturn -1;\n-\tif ((size = file->recs[i]->size) &&\n-\t\t\tfile->recs[i]->ptr[size - 1] == '\\n')\n+\tif ((size = file->recs[i].size) &&\n+\t\t\tfile->recs[i].ptr[size - 1] == '\\n')\n \t\t/* Last line; ends in LF; Is it CR/LF? */\n \t\treturn size > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!i)\n \t\t/* The only line has no eol */\n \t\treturn -1;\n \t/* Determine eol from second-to-last line */\n-\treturn (size = file->recs[i - 1]->size) > 1 &&\n-\t\tfile->recs[i - 1]->ptr[size - 2] == '\\r';\n+\treturn (size = file->recs[i - 1].size) > 1 &&\n+\t\tfile->recs[i - 1].ptr[size - 2] == '\\r';\n }\n \n static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)\n@@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\txpparam_t const *xpp)\n {\n-\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n+\txrecord_t *rec1 = xe1->xdf2.recs, *rec2 = xe2->xdf2.recs;\n \tfor (; m; m = m->next) {\n \t\t/* let's handle just the conflicts */\n \t\tif (m->mode)\n \t\t\tcontinue;\n \n \t\twhile(m->chg1 && m->chg2 &&\n-\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n+\t\t      recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t\tm->i1++;\n \t\t\tm->i2++;\n \t\t}\n \t\twhile (m->chg1 && m->chg2 &&\n-\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n-\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n+\t\t       recmatch(&rec1[m->i1 + m->chg1 - 1],\n+\t\t\t\t&rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t}\n@@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t * This probably does not work outside git, since\n \t\t * we have a very simple mmfile structure.\n \t\t */\n-\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;\n-\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr\n-\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;\n-\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;\n-\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr\n-\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;\n+\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n+\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n+\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n+\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n+\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n+\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n \t\tif (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n \t\t\treturn -1;\n \t\tif (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||\n@@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)\n static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n {\n \tfor (; chg; chg--, i++)\n-\t\tif (line_contains_alnum(xe->xdf2.recs[i]->ptr,\n-\t\t\t\txe->xdf2.recs[i]->size))\n+\t\tif (line_contains_alnum(xe->xdf2.recs[i].ptr,\n+\t\t\t\txe->xdf2.recs[i].size))\n \t\t\treturn 1;\n \treturn 0;\n }\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 77dc411d19..bf69a58527 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)\n static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\t\t  int pass)\n {\n-\txrecord_t **records = pass == 1 ?\n+\txrecord_t *records = pass == 1 ?\n \t\tmap->env->xdf1.recs : map->env->xdf2.recs;\n-\txrecord_t *record = records[line - 1];\n+\txrecord_t *record = &records[line - 1];\n \t/*\n \t * After xdl_prepare_env() (or more precisely, due to\n \t * xdl_classify_record()), the \"ha\" member of the records (AKA lines)\n@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\treturn;\n \tmap->entries[index].line1 = line;\n \tmap->entries[index].hash = record->ha;\n-\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);\n+\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n \tif (!map->first)\n \t\tmap->first = map->entries + index;\n \tif (map->last) {\n@@ -246,8 +246,8 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)\n \n static int match(struct hashmap *map, int line1, int line2)\n {\n-\txrecord_t *record1 = map->env->xdf1.recs[line1 - 1];\n-\txrecord_t *record2 = map->env->xdf2.recs[line2 - 1];\n+\txrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n+\txrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n \treturn record1->ha == record2->ha;\n }\n \ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 43cebf6721..5c7e858b6b 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -130,7 +130,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n }\n \n \n@@ -145,8 +144,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n \n-\tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n-\t\tgoto abort;\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n@@ -157,12 +154,10 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n \t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n-\t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n-\t\t\t\tgoto abort;\n+\t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\txdf->recs[xdf->nrec++] = crec;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n@@ -262,7 +257,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, nm, nreff, mlim;\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \txdlclass_t *rcrec;\n \tchar *dis, *dis1, *dis2;\n \tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n@@ -275,7 +270,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n \t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -283,7 +278,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n \t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -319,13 +314,13 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n  */\n static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, lim;\n-\txrecord_t **recs1, **recs2;\n+\txrecord_t *recs1, *recs2;\n \n \trecs1 = xdf1->recs;\n \trecs2 = xdf2->recs;\n \tfor (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n \t     i++, recs1++, recs2++)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dstart = xdf2->dstart = i;\n@@ -333,7 +328,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \trecs1 = xdf1->recs + xdf1->nrec - 1;\n \trecs2 = xdf2->recs + xdf2->nrec - 1;\n \tfor (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dend = xdf1->nrec - i - 1;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 85848f1685..3d26cbf1ec 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -45,10 +45,9 @@ typedef struct s_xrecord {\n } xrecord_t;\n \n typedef struct s_xdfile {\n-\tchastore_t rcha;\n+\txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\txrecord_t **recs;\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 444a108f87..332982b509 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tmmfile_t subfile1, subfile2;\n \txdfenv_t env;\n \n-\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;\n-\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +\n-\t\tdiff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;\n-\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;\n-\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +\n-\t\tdiff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;\n+\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n+\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n+\t\tdiff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n+\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n+\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n+\t\tdiff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-- \ngitgitgadget\n\n"},{"id":"526743","messageId":"9a5ac3c48899e146e07b076b826b00a7c001b44a.1758239789.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v2 10/10] xdiff: treat xdfile_t.rchg like an enum","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-18T23:56:29Z","receivedAt":"2025-09-18T23:56:48Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nDefine macros NO(0), YES(1), MAYBE(2) as the enum values for rchg to\nmake the code easier to follow. Perhaps 'rchg' should be renamed to\n'changed'?\n\nA few of the code changes might appear to change behavior, such as:\n    -    while (xdf->rchg[g->start - 1])\n    +    while (xdf->rchg[g->start - 1] == YES)\nbecause it appears the value of MAYBE is being ignored.  However, MAYBE\nis only ever assigned as a value to a temporary array (dis1 & dis2) and\nthen as a last step use that temporary array to decide if it wants to\nchange xdfile_t.rchg[i] to YES or leave it as NO.  As such, rchg will\nnever have a value of MAYBE and thus there is no behavioral change.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiff.h      |  4 ++++\n xdiff/xdiffi.c     | 22 +++++++++++-----------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 24 ++++++++++++------------\n 5 files changed, 35 insertions(+), 31 deletions(-)\n\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 2cecde5afe..7092879829 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -27,6 +27,10 @@\n extern \"C\" {\n #endif /* #ifdef __cplusplus */\n \n+#define NO 0\n+#define YES 1\n+#define MAYBE 2\n+\n /* xpparm_t.flags */\n #define XDF_NEED_MINIMAL (1 << 0)\n \ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 83c4cff6f7..44fd27823a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = YES;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = YES;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -708,7 +708,7 @@ struct xdlgroup {\n static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tg->start = g->end = 0;\n-\twhile (xdf->rchg[g->end])\n+\twhile (xdf->rchg[g->end] == YES)\n \t\tg->end++;\n }\n \n@@ -722,7 +722,7 @@ static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->start = g->end + 1;\n-\tfor (g->end = g->start; xdf->rchg[g->end]; g->end++)\n+\tfor (g->end = g->start; xdf->rchg[g->end] == YES; g->end++)\n \t\t;\n \n \treturn 0;\n@@ -738,7 +738,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->end = g->start - 1;\n-\tfor (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n+\tfor (g->start = g->end; xdf->rchg[g->start - 1] == YES; g->start--)\n \t\t;\n \n \treturn 0;\n@@ -753,10 +753,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->rchg[g->start++] = 0;\n-\t\txdf->rchg[g->end++] = 1;\n+\t\txdf->rchg[g->start++] = NO;\n+\t\txdf->rchg[g->end++] = YES;\n \n-\t\twhile (xdf->rchg[g->end])\n+\t\twhile (xdf->rchg[g->end] == YES)\n \t\t\tg->end++;\n \n \t\treturn 0;\n@@ -774,10 +774,10 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->rchg[--g->start] = 1;\n-\t\txdf->rchg[--g->end] = 0;\n+\t\txdf->rchg[--g->start] = YES;\n+\t\txdf->rchg[--g->end] = NO;\n \n-\t\twhile (xdf->rchg[g->start - 1])\n+\t\twhile (xdf->rchg[g->start - 1] == YES)\n \t\t\tg->start--;\n \n \t\treturn 0;\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 4d857e8ae2..c2e85b8ab9 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex bf69a58527..20cda5e258 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 5c7e858b6b..c11875d07f 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -214,9 +214,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \t * current line (i) is already a multimatch line.\n \t */\n \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n-\t\tif (!dis[i - r])\n+\t\tif (dis[i - r] == NO)\n \t\t\trdis0++;\n-\t\telse if (dis[i - r] == 2)\n+\t\telse if (dis[i - r] == MAYBE)\n \t\t\trpdis0++;\n \t\telse\n \t\t\tbreak;\n@@ -230,9 +230,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \tif (rdis0 == 0)\n \t\treturn 0;\n \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n-\t\tif (!dis[i + r])\n+\t\tif (dis[i + r] == NO)\n \t\t\trdis1++;\n-\t\telse if (dis[i + r] == 2)\n+\t\telse if (dis[i + r] == MAYBE)\n \t\t\trpdis1++;\n \t\telse\n \t\t\tbreak;\n@@ -272,7 +272,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n-\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tdis1[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n@@ -280,26 +280,26 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n-\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tdis2[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n-\t\tif (dis1[i] == 1 ||\n-\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n+\t\tif (dis1[i] == YES ||\n+\t\t    (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf1->rchg[i] = 1;\n+\t\t\txdf1->rchg[i] = YES;\n \t}\n \txdf1->nreff = nreff;\n \n \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n \t     i <= xdf2->dend; i++, recs++) {\n-\t\tif (dis2[i] == 1 ||\n-\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n+\t\tif (dis2[i] == YES ||\n+\t\t    (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf2->rchg[i] = 1;\n+\t\t\txdf2->rchg[i] = YES;\n \t}\n \txdf2->nreff = nreff;\n \n-- \ngitgitgadget\n"},{"id":"526744","messageId":"xmqqa52r702e.fsf@gitster.g","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 00/10] Use rust types in xdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-19T00:33:45Z","receivedAt":"2025-09-19T00:33:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> Changes since v1, to address review feedback.\n>\n>  * Only include the clean up patches; The remaining patches will be split\n>    into a separate series.\n>  * Commit message clarifications.\n>  * Minor style cleanups.\n>  * Performance impacts included in commit message of patch 8.\n>\n>\n> Relevant part of the original cover letter follows:\n> ===================================================\n>\n> This patch series involves ZERO Rust code and toolchains, which avoids the\n> debate about Rust's portability and timeline. Instead, it shows how Git can\n> immediately benefit from Rust's design choices without using it at all. The\n> rationale for using Rust types on the C and Rust side is addressed in the\n> commit that creates compat/rust_types.h.\n>\n> This patch series has 2 parts:\n>\n>  * Patches 1-9: Clean up xdiff, this can be merged without part 2.\n>  * Patches 10-17: Define Rust types in compat/rust_types.h and then start\n>    refactoring xdiff with Rust types. This depends on part 1.\n\nThis is probably stale.  If the patch numbering is to be trusted, we\nare missing [09/10] (at least we haven't seen it in the list archive\n30 minutes after the other messages in the series landed there), so\nthe \"clean up xdiff\" stage consists of 10 patches, and this cover\nletter does not need to talk about \"Patches 10-17\" (yet).\n\nWill see if lore.kernel.org catches up in the morning and process\nthem.  Thanks for working on the topic.\n\n"},{"id":"526746","messageId":"CAH=ZcbBfzcd33SD2f2GLmwSN2MHic7zG0cVBqO_9kfPsBRR=BA@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqa52r702e.fsf@gitster.g","subject":"Re: [PATCH v2 00/10] Use rust types in xdiff.","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-19T00:41:56Z","receivedAt":"2025-09-19T00:42:09Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Thu, Sep 18, 2025 at 6:33 PM Junio C Hamano <gitster@pobox.com> wrote:\n> This is probably stale.  If the patch numbering is to be trusted, we\n> are missing [09/10] (at least we haven't seen it in the list archive\n> 30 minutes after the other messages in the series landed there), so\n> the \"clean up xdiff\" stage consists of 10 patches, and this cover\n> letter does not need to talk about \"Patches 10-17\" (yet).\n>\n> Will see if lore.kernel.org catches up in the morning and process\n> them.  Thanks for working on the topic.\n\nYou're welcome. There are exactly 10 patches. Once this gets merged\ninto 'next' I can post part 2 under a different patch series. Part 2\nuses [ui]int(8|16|32|64)_t and s?size_t everywhere.\n"},{"id":"526773","messageId":"CAH=ZcbAjvOxzVN6NekrjwDLzuuZq6eTMFF2Oum=phNxmHLSkcA@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqa52r702e.fsf@gitster.g","subject":"Re: [PATCH v2 00/10] Use rust types in xdiff.","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-19T15:15:16Z","receivedAt":"2025-09-19T15:15:29Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Thu, Sep 18, 2025 at 6:33 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Will see if lore.kernel.org catches up in the morning and process\n> them.  Thanks for working on the topic.\n\nSince patch 9 hasn't shown up. I'll resubmit with a slightly cleaned\nup cover letter.\n"},{"id":"526774","messageId":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v2.git.git.1758239789.gitgitgadget@gmail.com","subject":"[PATCH v3 00/10] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:22Z","receivedAt":"2025-09-19T15:16:35Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"Changes since v2.\n\n * No patch changes, just resending to get patch 9 to show up on the mailing\n   list.\n * A few tweaks to the cover letter.\n\nChanges since v1, to address review feedback.\n\n * Only include the clean up patches; The remaining patches will be split\n   into a separate series.\n * Commit message clarifications.\n * Minor style cleanups.\n * Performance impacts included in commit message of patch 8.\n\n\nRelevant part of the original cover letter follows:\n===================================================\n\nBefore:\n\ntypedef struct s_xrecord {\n\tstruct s_xrecord *next;\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\tchastore_t rcha;\n\tlong nrec;\n\tunsigned int hbits;\n\txrecord_t **rhash;\n\tlong dstart, dend;\n\txrecord_t **recs;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n\tunsigned long *ha;\n} xdfile_t;\n\n\nAfter cleanup:\n\ntypedef struct s_xrecord {\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\txrecord_t *recs;\n\tlong nrec;\n\tlong dstart, dend;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n} xdfile_t;\n\n\n===\n\nEzekiel Newren (10):\n  xdiff: delete static forward declarations in xprepare\n  xdiff: delete local variables and initialize/free xdfile_t directly\n  xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  xdiff: delete xdl_get_rec() in xemit\n  xdiff: delete struct diffdata_t\n  xdiff: delete redundant array xdfile_t.ha\n  xdiff: delete fields ha, line, size in xdlclass_t in favor of an\n    xrecord_t\n  xdiff: delete chastore from xdfile_t\n  xdiff: delete rchg aliasing\n  xdiff: treat xdfile_t.rchg like an enum\n\n xdiff/xdiff.h      |   4 +\n xdiff/xdiffi.c     | 101 ++++++++---------\n xdiff/xdiffi.h     |  11 +-\n xdiff/xemit.c      |  38 +++----\n xdiff/xhistogram.c |  10 +-\n xdiff/xmerge.c     |  56 +++++-----\n xdiff/xpatience.c  |  18 ++--\n xdiff/xprepare.c   | 262 +++++++++++++++++----------------------------\n xdiff/xtypes.h     |   7 +-\n xdiff/xutils.c     |  12 +--\n 10 files changed, 212 insertions(+), 307 deletions(-)\n\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2048%2Fezekielnewren%2Fuse_rust_types_in_xdiff-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2048/ezekielnewren/use_rust_types_in_xdiff-v3\nPull-Request: https://github.com/git/git/pull/2048\n\nRange-diff vs v2:\n\n  1:  784cffcef5 =  1:  784cffcef5 xdiff: delete static forward declarations in xprepare\n  2:  b79157e64f =  2:  b79157e64f xdiff: delete local variables and initialize/free xdfile_t directly\n  3:  2e8de5be03 =  3:  2e8de5be03 xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  4:  ddfee67e06 =  4:  ddfee67e06 xdiff: delete xdl_get_rec() in xemit\n  5:  807ce3e5aa =  5:  807ce3e5aa xdiff: delete struct diffdata_t\n  6:  0bacb1191d =  6:  0bacb1191d xdiff: delete redundant array xdfile_t.ha\n  7:  e1e94107c9 =  7:  e1e94107c9 xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t\n  8:  fae26d2a04 =  8:  fae26d2a04 xdiff: delete chastore from xdfile_t\n  9:  fd54135560 =  9:  fd54135560 xdiff: delete rchg aliasing\n 10:  9a5ac3c488 = 10:  1e404c3290 xdiff: treat xdfile_t.rchg like an enum\n\n-- \ngitgitgadget\n"},{"id":"526775","messageId":"784cffcef564b31a32c401b35f33610b85126f7b.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:23Z","receivedAt":"2025-09-19T15:16:37Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nMove xdl_prepare_env() later in the file to avoid the need\nfor static forward declarations.\n\nBest-viewed-with: --color-moved\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 116 ++++++++++++++++++++---------------------------\n 1 file changed, 50 insertions(+), 66 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e1d4017b2d..a45c5ee208 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -53,21 +53,6 @@ typedef struct s_xdlclassifier {\n \n \n \n-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);\n-static void xdl_free_classifier(xdlclassifier_t *cf);\n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec);\n-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n-\t\t\t   xdlclassifier_t *cf, xdfile_t *xdf);\n-static void xdl_free_ctx(xdfile_t *xdf);\n-static int xdl_clean_mmatch(char const *dis, long i, long s, long e);\n-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-\n-\n-\n-\n static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {\n \tcf->flags = flags;\n \n@@ -242,57 +227,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {\n }\n \n \n-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n-\t\t    xdfenv_t *xe) {\n-\tlong enl1, enl2, sample;\n-\txdlclassifier_t cf;\n-\n-\tmemset(&cf, 0, sizeof(cf));\n-\n-\t/*\n-\t * For histogram diff, we can afford a smaller sample size and\n-\t * thus a poorer estimate of the number of lines, as the hash\n-\t * table (rhash) won't be filled up/grown. The number of lines\n-\t * (nrecs) will be updated correctly anyway by\n-\t * xdl_prepare_ctx().\n-\t */\n-\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n-\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n-\n-\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n-\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n-\n-\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n-\t\treturn -1;\n-\n-\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n-\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n-\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n-\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf2);\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\txdl_free_classifier(&cf);\n-\n-\treturn 0;\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n@@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2\n \n \treturn 0;\n }\n+\n+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n+\t\t    xdfenv_t *xe) {\n+\tlong enl1, enl2, sample;\n+\txdlclassifier_t cf;\n+\n+\tmemset(&cf, 0, sizeof(cf));\n+\n+\t/*\n+\t * For histogram diff, we can afford a smaller sample size and\n+\t * thus a poorer estimate of the number of lines, as the hash\n+\t * table (rhash) won't be filled up/grown. The number of lines\n+\t * (nrecs) will be updated correctly anyway by\n+\t * xdl_prepare_ctx().\n+\t */\n+\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n+\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n+\n+\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n+\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n+\n+\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n+\t\treturn -1;\n+\n+\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n+\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n+\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n+\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf2);\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t    }\n+\n+\txdl_free_classifier(&cf);\n+\n+\treturn 0;\n+}\n-- \ngitgitgadget\n\n"},{"id":"526776","messageId":"b79157e64f0950d25a23c50a8ea83cfddf67ddf5.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 02/10] xdiff: delete local variables and initialize/free xdfile_t directly","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:24Z","receivedAt":"2025-09-19T15:16:38Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThese local variables are essentially a hand-rolled additional\nimplementation of xdl_free_ctx() inlined into xdl_prepare_ctx(). Modify\nthe code to use the existing xdl_free_ctx() function so there aren't\ntwo ways to free such variables.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 78 +++++++++++++++++++-----------------------------\n 1 file changed, 30 insertions(+), 48 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex a45c5ee208..fe02fd7925 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -134,99 +134,81 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n }\n \n \n+static void xdl_free_ctx(xdfile_t *xdf)\n+{\n+\txdl_free(xdf->rhash);\n+\txdl_free(xdf->rindex);\n+\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->ha);\n+\txdl_free(xdf->recs);\n+\txdl_cha_free(&xdf->rcha);\n+}\n+\n+\n static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n \t\t\t   xdlclassifier_t *cf, xdfile_t *xdf) {\n-\tunsigned int hbits;\n-\tlong nrec, hsize, bsize;\n+\tlong bsize;\n \tunsigned long hav;\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n-\txrecord_t **recs;\n-\txrecord_t **rhash;\n-\tunsigned long *ha;\n-\tchar *rchg;\n-\tlong *rindex;\n \n-\tha = NULL;\n-\trindex = NULL;\n-\trchg = NULL;\n-\trhash = NULL;\n-\trecs = NULL;\n+\txdf->ha = NULL;\n+\txdf->rindex = NULL;\n+\txdf->rchg = NULL;\n+\txdf->rhash = NULL;\n+\txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n \t\tgoto abort;\n-\tif (!XDL_ALLOC_ARRAY(recs, narec))\n+\tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\thbits = xdl_hashbits((unsigned int) narec);\n-\thsize = 1 << hbits;\n-\tif (!XDL_CALLOC_ARRAY(rhash, hsize))\n+\txdf->hbits = xdl_hashbits((unsigned int) narec);\n+\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n \t\tgoto abort;\n \n-\tnrec = 0;\n+\txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n \t\t\tprev = cur;\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n-\t\t\tif (XDL_ALLOC_GROW(recs, nrec + 1, narec))\n+\t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n \t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n \t\t\t\tgoto abort;\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\trecs[nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)\n+\t\t\txdf->recs[xdf->nrec++] = crec;\n+\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(rchg, nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n-\t\tif (!XDL_ALLOC_ARRAY(rindex, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(ha, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n \t\t\tgoto abort;\n \t}\n \n-\txdf->nrec = nrec;\n-\txdf->recs = recs;\n-\txdf->hbits = hbits;\n-\txdf->rhash = rhash;\n-\txdf->rchg = rchg + 1;\n-\txdf->rindex = rindex;\n+\txdf->rchg += 1;\n \txdf->nreff = 0;\n-\txdf->ha = ha;\n \txdf->dstart = 0;\n-\txdf->dend = nrec - 1;\n+\txdf->dend = xdf->nrec - 1;\n \n \treturn 0;\n \n abort:\n-\txdl_free(ha);\n-\txdl_free(rindex);\n-\txdl_free(rchg);\n-\txdl_free(rhash);\n-\txdl_free(recs);\n-\txdl_cha_free(&xdf->rcha);\n+\txdl_free_ctx(xdf);\n \treturn -1;\n }\n \n \n-static void xdl_free_ctx(xdfile_t *xdf) {\n-\n-\txdl_free(xdf->rhash);\n-\txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n-\txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n-- \ngitgitgadget\n\n"},{"id":"526777","messageId":"2e8de5be03f7166059d735a99573520e35ff9a31.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 03/10] xdiff: delete unnecessary fields from xrecord_t and xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:25Z","receivedAt":"2025-09-19T15:16:40Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,\nbut never used for anything by the code. Remove them.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 15 ++-------------\n xdiff/xtypes.h   |  3 ---\n 2 files changed, 2 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex fe02fd7925..7acca1cb38 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -91,8 +91,7 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n }\n \n \n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec) {\n+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n \tchar const *line;\n \txdlclass_t *rcrec;\n@@ -126,17 +125,12 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \n \trec->ha = (unsigned long) rcrec->idx;\n \n-\thi = (long) XDL_HASHLONG(rec->ha, hbits);\n-\trec->next = rhash[hi];\n-\trhash[hi] = rec;\n-\n \treturn 0;\n }\n \n \n static void xdl_free_ctx(xdfile_t *xdf)\n {\n-\txdl_free(xdf->rhash);\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->ha);\n@@ -155,7 +149,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n-\txdf->rhash = NULL;\n \txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n@@ -163,10 +156,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\txdf->hbits = xdl_hashbits((unsigned int) narec);\n-\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n-\t\tgoto abort;\n-\n \txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n@@ -180,7 +169,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n \t\t\txdf->recs[xdf->nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n+\t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8442bd436e..8b8467360e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -39,7 +39,6 @@ typedef struct s_chastore {\n } chastore_t;\n \n typedef struct s_xrecord {\n-\tstruct s_xrecord *next;\n \tchar const *ptr;\n \tlong size;\n \tunsigned long ha;\n@@ -48,8 +47,6 @@ typedef struct s_xrecord {\n typedef struct s_xdfile {\n \tchastore_t rcha;\n \tlong nrec;\n-\tunsigned int hbits;\n-\txrecord_t **rhash;\n \tlong dstart, dend;\n \txrecord_t **recs;\n \tchar *rchg;\n-- \ngitgitgadget\n\n"},{"id":"526778","messageId":"ddfee67e06bb56cfa8f11187dc27135bfce63647.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 04/10] xdiff: delete xdl_get_rec() in xemit","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:26Z","receivedAt":"2025-09-19T15:16:42Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThis function aliases the fields of xrecord_t, which makes it harder\nto track the usages of those fields. Delete it.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 38 +++++++++++++-------------------------\n 1 file changed, 13 insertions(+), 25 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 1d40c9cb40..b3793e81e2 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -22,21 +22,11 @@\n \n #include \"xinclude.h\"\n \n-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n-\n-\t*rec = xdf->recs[ri]->ptr;\n-\n-\treturn xdf->recs[ri]->size;\n-}\n-\n-\n-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n-\tlong size, psize = strlen(pre);\n-\tchar const *rec;\n-\n-\tsize = xdl_get_rec(xdf, ri, &rec);\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n+{\n+\txrecord_t *rec = xdf->recs[ri];\n \n+\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n \t\treturn -1;\n \t}\n \n@@ -120,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\txrecord_t *rec = xdf->recs[ri];\n+\n \tif (!xecfg->find_func)\n-\t\treturn def_ff(rec, len, buf, sz);\n-\treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n+\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n+\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n }\n \n static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n@@ -160,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\txrecord_t *rec = xdf->recs[ri];\n+\tlong i = 0;\n \n-\twhile (len > 0 && XDL_ISSPACE(*rec)) {\n-\t\trec++;\n-\t\tlen--;\n-\t}\n-\treturn !len;\n+\tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n+\n+\treturn i == rec->size;\n }\n \n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n-- \ngitgitgadget\n\n"},{"id":"526779","messageId":"807ce3e5aae0253dd58c7a4d0bd04ca341b070c8.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 05/10] xdiff: delete struct diffdata_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:27Z","receivedAt":"2025-09-19T15:16:44Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nEvery field in this struct is an alias for a certain field in xdfile_t.\n\ndiffdata_t.nrec   -> xdfile_t.nreff\ndiffdata_t.ha     -> xdfile_t.ha\ndiffdata_t.rindex -> xdfile_t.rindex\ndiffdata_t.rchg   -> xdfile_t.rchg\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 32 ++++++++------------------------\n xdiff/xdiffi.h | 11 ++---------\n 2 files changed, 10 insertions(+), 33 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 5a96e36dfb..bbf0161f84 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -257,10 +257,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n  * sub-boxes by calling the box splitting function. Note that the real job\n  * (marking changed lines) is done in the two boundary reaching checks.\n  */\n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;\n+\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n@@ -273,17 +273,11 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t * be obviously changed.\n \t */\n \tif (off1 == lim1) {\n-\t\tchar *rchg2 = dd2->rchg;\n-\t\tlong *rindex2 = dd2->rindex;\n-\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\trchg2[rindex2[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n \t} else if (off2 == lim2) {\n-\t\tchar *rchg1 = dd1->rchg;\n-\t\tlong *rindex1 = dd1->rindex;\n-\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\trchg1[rindex1[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -300,9 +294,9 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t\t/*\n \t\t * ... et Impera.\n \t\t */\n-\t\tif (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,\n+\t\tif (xdl_recs_cmp(xdf1, off1, spl.i1, xdf2, off2, spl.i2,\n \t\t\t\t kvdf, kvdb, spl.min_lo, xenv) < 0 ||\n-\t\t    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,\n+\t\t    xdl_recs_cmp(xdf1, spl.i1, lim1, xdf2, spl.i2, lim2,\n \t\t\t\t kvdf, kvdb, spl.min_hi, xenv) < 0) {\n \n \t\t\treturn -1;\n@@ -318,7 +312,6 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \tlong ndiags;\n \tlong *kvd, *kvdf, *kvdb;\n \txdalgoenv_t xenv;\n-\tdiffdata_t dd1, dd2;\n \tint res;\n \n \tif (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)\n@@ -357,16 +350,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \txenv.snake_cnt = XDL_SNAKE_CNT;\n \txenv.heur_min = XDL_HEUR_MIN_COST;\n \n-\tdd1.nrec = xe->xdf1.nreff;\n-\tdd1.ha = xe->xdf1.ha;\n-\tdd1.rchg = xe->xdf1.rchg;\n-\tdd1.rindex = xe->xdf1.rindex;\n-\tdd2.nrec = xe->xdf2.nreff;\n-\tdd2.ha = xe->xdf2.ha;\n-\tdd2.rchg = xe->xdf2.rchg;\n-\tdd2.rindex = xe->xdf2.rindex;\n-\n-\tres = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,\n+\tres = xdl_recs_cmp(&xe->xdf1, 0, xe->xdf1.nreff, &xe->xdf2, 0, xe->xdf2.nreff,\n \t\t\t   kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,\n \t\t\t   &xenv);\n \txdl_free(kvd);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 126c9d8ff4..49e52c67f9 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -24,13 +24,6 @@\n #define XDIFFI_H\n \n \n-typedef struct s_diffdata {\n-\tlong nrec;\n-\tunsigned long const *ha;\n-\tlong *rindex;\n-\tchar *rchg;\n-} diffdata_t;\n-\n typedef struct s_xdalgoenv {\n \tlong mxcost;\n \tlong snake_cnt;\n@@ -46,8 +39,8 @@ typedef struct s_xdchange {\n \n \n \n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);\n int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\txdfenv_t *xe);\n-- \ngitgitgadget\n\n"},{"id":"526780","messageId":"0bacb1191dad2748d2afa79665f1293b0381bde1.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 06/10] xdiff: delete redundant array xdfile_t.ha","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:28Z","receivedAt":"2025-09-19T15:16:45Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen 0 <= i < xdfile_t.nreff the following is true:\nxdfile_t.ha[i] == xdfile_t.recs[xdfile_t.rindex[i]]\n\nThis makes the code about 5% slower. The fields rindex and ha are\nspecific to the classic diff (myers and minimal). I plan on creating a\nstruct for classic diff, but there'a alot of cleanup that needs to be\ndone before that can happen and leaving ha in would make those cleanups\nharder to follow.\n\nA subsequent commit will delete the chastore cha from xdfile_t. That\nlater commit will investigate deleting ha and cha independently and\ntogether.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c   | 24 ++++++++++++++----------\n xdiff/xprepare.c | 12 ++----------\n xdiff/xtypes.h   |  1 -\n 3 files changed, 16 insertions(+), 21 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex bbf0161f84..11cd090b53 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -22,6 +22,11 @@\n \n #include \"xinclude.h\"\n \n+static unsigned long get_hash(xdfile_t *xdf, long index)\n+{\n+\treturn xdf->recs[xdf->rindex[index]]->ha;\n+}\n+\n #define XDL_MAX_COST_MIN 256\n #define XDL_HEUR_MIN_COST 256\n #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)\n@@ -42,8 +47,8 @@ typedef struct s_xdpsplit {\n  * using this algorithm, so a little bit of heuristic is needed to cut the\n  * search and to return a suboptimal point.\n  */\n-static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n-\t\t      unsigned long const *ha2, long off2, long lim2,\n+static long xdl_split(xdfile_t *xdf1, long off1, long lim1,\n+\t\t      xdfile_t *xdf2, long off2, long lim2,\n \t\t      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,\n \t\t      xdalgoenv_t *xenv) {\n \tlong dmin = off1 - lim2, dmax = lim1 - off2;\n@@ -87,7 +92,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdf[d + 1];\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);\n+\t\t\tfor (; i1 < lim1 && i2 < lim2 && get_hash(xdf1, i1) == get_hash(xdf2, i2); i1++, i2++);\n \t\t\tif (i1 - prev1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdf[d] = i1;\n@@ -124,7 +129,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdb[d + 1] - 1;\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);\n+\t\t\tfor (; i1 > off1 && i2 > off2 && get_hash(xdf1, i1 - 1) == get_hash(xdf2, i2 - 1); i1--, i2--);\n \t\t\tif (prev1 - i1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdb[d] = i1;\n@@ -159,7 +164,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&\n \t\t\t\t    off2 + xenv->snake_cnt <= i2 && i2 < lim2) {\n-\t\t\t\t\tfor (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)\n+\t\t\t\t\tfor (k = 1; get_hash(xdf1, i1 - k) == get_hash(xdf2, i2 - k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -183,7 +188,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&\n \t\t\t\t    off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {\n-\t\t\t\t\tfor (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)\n+\t\t\t\t\tfor (k = 0; get_hash(xdf1, i1 + k) == get_hash(xdf2, i2 + k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt - 1) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -260,13 +265,12 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n \t */\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, off1) == get_hash(xdf2, off2); off1++, off2++);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, lim1 - 1) == get_hash(xdf2, lim2 - 1); lim1--, lim2--);\n \n \t/*\n \t * If one dimension is empty, then all records on the other one must\n@@ -285,7 +289,7 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t/*\n \t\t * Divide ...\n \t\t */\n-\t\tif (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,\n+\t\tif (xdl_split(xdf1, off1, lim1, xdf2, off2, lim2, kvdf, kvdb,\n \t\t\t      need_min, &spl, xenv) < 0) {\n \n \t\t\treturn -1;\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 7acca1cb38..c39b65fea9 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -133,7 +133,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n {\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n \txdl_free(xdf->recs);\n \txdl_cha_free(&xdf->rcha);\n }\n@@ -146,7 +145,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n \n-\txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n@@ -181,8 +179,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n \t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n-\t\t\tgoto abort;\n \t}\n \n \txdf->rchg += 1;\n@@ -300,9 +296,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (dis1[i] == 1 ||\n \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n-\t\t\txdf1->rindex[nreff] = i;\n-\t\t\txdf1->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf1->rchg[i] = 1;\n \t}\n@@ -312,9 +306,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf2->dend; i++, recs++) {\n \t\tif (dis2[i] == 1 ||\n \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n-\t\t\txdf2->rindex[nreff] = i;\n-\t\t\txdf2->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf2->rchg[i] = 1;\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8b8467360e..85848f1685 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -52,7 +52,6 @@ typedef struct s_xdfile {\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\n-\tunsigned long *ha;\n } xdfile_t;\n \n typedef struct s_xdfenv {\n-- \ngitgitgadget\n\n"},{"id":"526781","messageId":"e1e94107c9722b751d6111460b17e02a7ffd96d1.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 07/10] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:29Z","receivedAt":"2025-09-19T15:16:47Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe fields from xdlclass_t are aliases of xrecord_t:\nxdlclass_t.line -> xrecord_t.ptr\nxdlclass_t.size -> xrecord_t.size\nxdlclass_t.ha   -> xrecord_t.ha\n\nRemove aliasing from xdlclass_t, to reduce future refactoring mistakes.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 16 ++++++----------\n 1 file changed, 6 insertions(+), 10 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex c39b65fea9..43cebf6721 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -32,9 +32,7 @@\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n-\tunsigned long ha;\n-\tchar const *line;\n-\tlong size;\n+\txrecord_t rec;\n \tlong idx;\n \tlong len1, len2;\n } xdlclass_t;\n@@ -93,14 +91,12 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n \n static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n-\tchar const *line;\n \txdlclass_t *rcrec;\n \n-\tline = rec->ptr;\n \thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n-\t\tif (rcrec->ha == rec->ha &&\n-\t\t\t\txdl_recmatch(rcrec->line, rcrec->size,\n+\t\tif (rcrec->rec.ha == rec->ha &&\n+\t\t\t\txdl_recmatch(rcrec->rec.ptr, rcrec->rec.size,\n \t\t\t\t\trec->ptr, rec->size, cf->flags))\n \t\t\tbreak;\n \n@@ -113,9 +109,9 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \t\tif (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))\n \t\t\t\treturn -1;\n \t\tcf->rcrecs[rcrec->idx] = rcrec;\n-\t\trcrec->line = line;\n-\t\trcrec->size = rec->size;\n-\t\trcrec->ha = rec->ha;\n+\t\trcrec->rec.ptr = rec->ptr;\n+\t\trcrec->rec.size = rec->size;\n+\t\trcrec->rec.ha = rec->ha;\n \t\trcrec->len1 = rcrec->len2 = 0;\n \t\trcrec->next = cf->rchash[hi];\n \t\tcf->rchash[hi] = rcrec;\n-- \ngitgitgadget\n\n"},{"id":"526782","messageId":"fae26d2a0438cd0c6ca6d9f26cbcae11fdf0a4bb.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 08/10] xdiff: delete chastore from xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:30Z","receivedAt":"2025-09-19T15:16:49Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxdfile_t currently uses chastore_t which is an arena allocator. I\nthink that xrecord_t used to be a linked list and recs didn't exist\noriginally. When recs was added I think they forgot to remove\nxdfile_t.next, but was overlooked. This dual data structure setup\nmakes the code somewhat confusing.\n\nAdditionally the C type chastore_t isn't FFI friendly, and provides\nlittle to no performance benefit over using realloc to grow an array.\n\nPerformance impact of deleting fields from xdfile_t:\nDeleting ha is about 5% slower.\nDeleting cha is about 5% faster.\n\nDelete ha, but keep cha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.269 s ±  0.017 s    [User: 1.135 s, System: 0.128 s]\n    Range (min … max):    1.249 s …  1.286 s    10 runs\n\n  Benchmark 2: build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.339 s ±  0.017 s    [User: 1.234 s, System: 0.099 s]\n    Range (min … max):    1.320 s …  1.358 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.06 ± 0.02 times faster than build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete cha, but keep ha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.290 s ±  0.001 s    [User: 1.154 s, System: 0.130 s]\n    Range (min … max):    1.288 s …  1.292 s    10 runs\n\n  Benchmark 2: build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.232 s ±  0.017 s    [User: 1.105 s, System: 0.121 s]\n    Range (min … max):    1.205 s …  1.249 s    10 runs\n\n  Summary\n    build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.05 ± 0.01 times faster than build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete ha AND chastore\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha_and_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.291 s ±  0.002 s    [User: 1.156 s, System: 0.129 s]\n    Range (min … max):    1.287 s …  1.295 s    10 runs\n\n  Benchmark 2: build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.306 s ±  0.001 s    [User: 1.195 s, System: 0.105 s]\n    Range (min … max):    1.305 s …  1.308 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.01 ± 0.00 times faster than build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 24 ++++++++++----------\n xdiff/xemit.c      |  6 ++---\n xdiff/xhistogram.c |  2 +-\n xdiff/xmerge.c     | 56 +++++++++++++++++++++++-----------------------\n xdiff/xpatience.c  | 10 ++++-----\n xdiff/xprepare.c   | 19 ++++++----------\n xdiff/xtypes.h     |  3 +--\n xdiff/xutils.c     | 12 +++++-----\n 8 files changed, 63 insertions(+), 69 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 11cd090b53..a66125d44a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -24,7 +24,7 @@\n \n static unsigned long get_hash(xdfile_t *xdf, long index)\n {\n-\treturn xdf->recs[xdf->rindex[index]]->ha;\n+\treturn xdf->recs[xdf->rindex[index]].ha;\n }\n \n #define XDL_MAX_COST_MIN 256\n@@ -489,13 +489,13 @@ static void measure_split(const xdfile_t *xdf, long split,\n \t\tm->indent = -1;\n \t} else {\n \t\tm->end_of_file = 0;\n-\t\tm->indent = get_indent(xdf->recs[split]);\n+\t\tm->indent = get_indent(&xdf->recs[split]);\n \t}\n \n \tm->pre_blank = 0;\n \tm->pre_indent = -1;\n \tfor (i = split - 1; i >= 0; i--) {\n-\t\tm->pre_indent = get_indent(xdf->recs[i]);\n+\t\tm->pre_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->pre_indent != -1)\n \t\t\tbreak;\n \t\tm->pre_blank += 1;\n@@ -508,7 +508,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n \tm->post_blank = 0;\n \tm->post_indent = -1;\n \tfor (i = split + 1; i < xdf->nrec; i++) {\n-\t\tm->post_indent = get_indent(xdf->recs[i]);\n+\t\tm->post_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->post_indent != -1)\n \t\t\tbreak;\n \t\tm->post_blank += 1;\n@@ -752,7 +752,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n-\t    recs_match(xdf->recs[g->start], xdf->recs[g->end])) {\n+\t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n \t\txdf->rchg[g->start++] = 0;\n \t\txdf->rchg[g->end++] = 1;\n \n@@ -773,7 +773,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n-\t    recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {\n+\t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n \t\txdf->rchg[--g->start] = 1;\n \t\txdf->rchg[--g->end] = 0;\n \n@@ -988,16 +988,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n \t\tint ignore = 1;\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tlong i;\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\txch->ignore = ignore;\n \t}\n@@ -1021,7 +1021,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \txdchange_t *xch;\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tint ignore = 1;\n \t\tlong i;\n \n@@ -1033,11 +1033,11 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\txch->ignore = ignore;\n \t}\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex b3793e81e2..79c14f8b4c 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -24,7 +24,7 @@\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n \t\treturn -1;\n@@ -110,7 +110,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n@@ -150,7 +150,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \tlong i = 0;\n \n \tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 040d81e0bc..4d857e8ae2 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -86,7 +86,7 @@ struct region {\n \t((LINE_MAP(index, ptr))->cnt)\n \n #define REC(env, s, l) \\\n-\t(env->xdf##s.recs[l - 1])\n+\t(&env->xdf##s.recs[l - 1])\n \n static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n {\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex af40c88a5b..fd600cbb5d 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \t\tint line_count, long flags)\n {\n \tint i;\n-\txrecord_t **rec1 = xe1->xdf2.recs + i1;\n-\txrecord_t **rec2 = xe2->xdf2.recs + i2;\n+\txrecord_t *rec1 = xe1->xdf2.recs + i1;\n+\txrecord_t *rec2 = xe2->xdf2.recs + i2;\n \n \tfor (i = 0; i < line_count; i++) {\n-\t\tint result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,\n-\t\t\trec2[i]->ptr, rec2[i]->size, flags);\n+\t\tint result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n+\t\t\trec2[i].ptr, rec2[i].size, flags);\n \t\tif (!result)\n \t\t\treturn -1;\n \t}\n@@ -111,7 +111,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \n static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)\n {\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \tint size = 0;\n \n \trecs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n@@ -119,12 +119,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n \tif (count < 1)\n \t\treturn 0;\n \n-\tfor (i = 0; i < count; size += recs[i++]->size)\n+\tfor (i = 0; i < count; size += recs[i++].size)\n \t\tif (dest)\n-\t\t\tmemcpy(dest + size, recs[i]->ptr, recs[i]->size);\n+\t\t\tmemcpy(dest + size, recs[i].ptr, recs[i].size);\n \tif (add_nl) {\n-\t\ti = recs[count - 1]->size;\n-\t\tif (i == 0 || recs[count - 1]->ptr[i - 1] != '\\n') {\n+\t\ti = recs[count - 1].size;\n+\t\tif (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n \t\t\tif (needs_cr) {\n \t\t\t\tif (dest)\n \t\t\t\t\tdest[size] = '\\r';\n@@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)\n \n \tif (i < file->nrec - 1)\n \t\t/* All lines before the last *must* end in LF */\n-\t\treturn (size = file->recs[i]->size) > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\treturn (size = file->recs[i].size) > 1 &&\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!file->nrec)\n \t\t/* Cannot determine eol style from empty file */\n \t\treturn -1;\n-\tif ((size = file->recs[i]->size) &&\n-\t\t\tfile->recs[i]->ptr[size - 1] == '\\n')\n+\tif ((size = file->recs[i].size) &&\n+\t\t\tfile->recs[i].ptr[size - 1] == '\\n')\n \t\t/* Last line; ends in LF; Is it CR/LF? */\n \t\treturn size > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!i)\n \t\t/* The only line has no eol */\n \t\treturn -1;\n \t/* Determine eol from second-to-last line */\n-\treturn (size = file->recs[i - 1]->size) > 1 &&\n-\t\tfile->recs[i - 1]->ptr[size - 2] == '\\r';\n+\treturn (size = file->recs[i - 1].size) > 1 &&\n+\t\tfile->recs[i - 1].ptr[size - 2] == '\\r';\n }\n \n static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)\n@@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\txpparam_t const *xpp)\n {\n-\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n+\txrecord_t *rec1 = xe1->xdf2.recs, *rec2 = xe2->xdf2.recs;\n \tfor (; m; m = m->next) {\n \t\t/* let's handle just the conflicts */\n \t\tif (m->mode)\n \t\t\tcontinue;\n \n \t\twhile(m->chg1 && m->chg2 &&\n-\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n+\t\t      recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t\tm->i1++;\n \t\t\tm->i2++;\n \t\t}\n \t\twhile (m->chg1 && m->chg2 &&\n-\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n-\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n+\t\t       recmatch(&rec1[m->i1 + m->chg1 - 1],\n+\t\t\t\t&rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t}\n@@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t * This probably does not work outside git, since\n \t\t * we have a very simple mmfile structure.\n \t\t */\n-\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;\n-\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr\n-\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;\n-\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;\n-\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr\n-\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;\n+\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n+\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n+\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n+\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n+\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n+\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n \t\tif (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n \t\t\treturn -1;\n \t\tif (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||\n@@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)\n static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n {\n \tfor (; chg; chg--, i++)\n-\t\tif (line_contains_alnum(xe->xdf2.recs[i]->ptr,\n-\t\t\t\txe->xdf2.recs[i]->size))\n+\t\tif (line_contains_alnum(xe->xdf2.recs[i].ptr,\n+\t\t\t\txe->xdf2.recs[i].size))\n \t\t\treturn 1;\n \treturn 0;\n }\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 77dc411d19..bf69a58527 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)\n static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\t\t  int pass)\n {\n-\txrecord_t **records = pass == 1 ?\n+\txrecord_t *records = pass == 1 ?\n \t\tmap->env->xdf1.recs : map->env->xdf2.recs;\n-\txrecord_t *record = records[line - 1];\n+\txrecord_t *record = &records[line - 1];\n \t/*\n \t * After xdl_prepare_env() (or more precisely, due to\n \t * xdl_classify_record()), the \"ha\" member of the records (AKA lines)\n@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\treturn;\n \tmap->entries[index].line1 = line;\n \tmap->entries[index].hash = record->ha;\n-\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);\n+\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n \tif (!map->first)\n \t\tmap->first = map->entries + index;\n \tif (map->last) {\n@@ -246,8 +246,8 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)\n \n static int match(struct hashmap *map, int line1, int line2)\n {\n-\txrecord_t *record1 = map->env->xdf1.recs[line1 - 1];\n-\txrecord_t *record2 = map->env->xdf2.recs[line2 - 1];\n+\txrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n+\txrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n \treturn record1->ha == record2->ha;\n }\n \ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 43cebf6721..5c7e858b6b 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -130,7 +130,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n }\n \n \n@@ -145,8 +144,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n \n-\tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n-\t\tgoto abort;\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n@@ -157,12 +154,10 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n \t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n-\t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n-\t\t\t\tgoto abort;\n+\t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\txdf->recs[xdf->nrec++] = crec;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n@@ -262,7 +257,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, nm, nreff, mlim;\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \txdlclass_t *rcrec;\n \tchar *dis, *dis1, *dis2;\n \tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n@@ -275,7 +270,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n \t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -283,7 +278,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n \t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -319,13 +314,13 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n  */\n static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, lim;\n-\txrecord_t **recs1, **recs2;\n+\txrecord_t *recs1, *recs2;\n \n \trecs1 = xdf1->recs;\n \trecs2 = xdf2->recs;\n \tfor (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n \t     i++, recs1++, recs2++)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dstart = xdf2->dstart = i;\n@@ -333,7 +328,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \trecs1 = xdf1->recs + xdf1->nrec - 1;\n \trecs2 = xdf2->recs + xdf2->nrec - 1;\n \tfor (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dend = xdf1->nrec - i - 1;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 85848f1685..3d26cbf1ec 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -45,10 +45,9 @@ typedef struct s_xrecord {\n } xrecord_t;\n \n typedef struct s_xdfile {\n-\tchastore_t rcha;\n+\txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\txrecord_t **recs;\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 444a108f87..332982b509 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tmmfile_t subfile1, subfile2;\n \txdfenv_t env;\n \n-\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;\n-\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +\n-\t\tdiff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;\n-\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;\n-\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +\n-\t\tdiff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;\n+\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n+\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n+\t\tdiff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n+\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n+\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n+\t\tdiff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-- \ngitgitgadget\n\n"},{"id":"526783","messageId":"fd541355609b715ebe94f9e025450165bd91d170.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 09/10] xdiff: delete rchg aliasing","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:31Z","receivedAt":"2025-09-19T15:16:52Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex a66125d44a..83c4cff6f7 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -932,16 +932,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n \n int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n \txdchange_t *cscr = NULL, *xch;\n-\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n \tlong i1, i2, l1, l2;\n \n \t/*\n \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n \t */\n \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n-\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n-\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n-\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n+\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n+\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n+\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n \n \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n \t\t\t\txdl_free_script(cscr);\n-- \ngitgitgadget\n\n"},{"id":"526784","messageId":"1e404c3290bd4ba8b4973a597b976c301138cf73.1758294992.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v3 10/10] xdiff: treat xdfile_t.rchg like an enum","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-19T15:16:32Z","receivedAt":"2025-09-19T15:16:53Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nDefine macros NO(0), YES(1), MAYBE(2) as the enum values for rchg to\nmake the code easier to follow. Perhaps 'rchg' should be renamed to\n'changed'?\n\nA few of the code changes might appear to change behavior, such as:\n    -    while (xdf->rchg[g->start - 1])\n    +    while (xdf->rchg[g->start - 1] == YES)\nbecause it appears the value of MAYBE is being ignored.  However, MAYBE\nis only ever assigned as a value to a temporary array (dis1 & dis2) and\nthen as a last step use that temporary array to decide if it wants to\nchange xdfile_t.rchg[i] to YES or leave it as NO.  As such, rchg will\nnever have a value of MAYBE and thus there is no behavioral change.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiff.h      |  4 ++++\n xdiff/xdiffi.c     | 22 +++++++++++-----------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 24 ++++++++++++------------\n 5 files changed, 35 insertions(+), 31 deletions(-)\n\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 2cecde5afe..7092879829 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -27,6 +27,10 @@\n extern \"C\" {\n #endif /* #ifdef __cplusplus */\n \n+#define NO 0\n+#define YES 1\n+#define MAYBE 2\n+\n /* xpparm_t.flags */\n #define XDF_NEED_MINIMAL (1 << 0)\n \ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 83c4cff6f7..44fd27823a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = YES;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = YES;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -708,7 +708,7 @@ struct xdlgroup {\n static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tg->start = g->end = 0;\n-\twhile (xdf->rchg[g->end])\n+\twhile (xdf->rchg[g->end] == YES)\n \t\tg->end++;\n }\n \n@@ -722,7 +722,7 @@ static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->start = g->end + 1;\n-\tfor (g->end = g->start; xdf->rchg[g->end]; g->end++)\n+\tfor (g->end = g->start; xdf->rchg[g->end] == YES; g->end++)\n \t\t;\n \n \treturn 0;\n@@ -738,7 +738,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->end = g->start - 1;\n-\tfor (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n+\tfor (g->start = g->end; xdf->rchg[g->start - 1] == YES; g->start--)\n \t\t;\n \n \treturn 0;\n@@ -753,10 +753,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->rchg[g->start++] = 0;\n-\t\txdf->rchg[g->end++] = 1;\n+\t\txdf->rchg[g->start++] = NO;\n+\t\txdf->rchg[g->end++] = YES;\n \n-\t\twhile (xdf->rchg[g->end])\n+\t\twhile (xdf->rchg[g->end] == YES)\n \t\t\tg->end++;\n \n \t\treturn 0;\n@@ -774,10 +774,10 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->rchg[--g->start] = 1;\n-\t\txdf->rchg[--g->end] = 0;\n+\t\txdf->rchg[--g->start] = YES;\n+\t\txdf->rchg[--g->end] = NO;\n \n-\t\twhile (xdf->rchg[g->start - 1])\n+\t\twhile (xdf->rchg[g->start - 1] == YES)\n \t\t\tg->start--;\n \n \t\treturn 0;\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 4d857e8ae2..c2e85b8ab9 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex bf69a58527..20cda5e258 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 5c7e858b6b..c11875d07f 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -214,9 +214,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \t * current line (i) is already a multimatch line.\n \t */\n \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n-\t\tif (!dis[i - r])\n+\t\tif (dis[i - r] == NO)\n \t\t\trdis0++;\n-\t\telse if (dis[i - r] == 2)\n+\t\telse if (dis[i - r] == MAYBE)\n \t\t\trpdis0++;\n \t\telse\n \t\t\tbreak;\n@@ -230,9 +230,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \tif (rdis0 == 0)\n \t\treturn 0;\n \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n-\t\tif (!dis[i + r])\n+\t\tif (dis[i + r] == NO)\n \t\t\trdis1++;\n-\t\telse if (dis[i + r] == 2)\n+\t\telse if (dis[i + r] == MAYBE)\n \t\t\trpdis1++;\n \t\telse\n \t\t\tbreak;\n@@ -272,7 +272,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n-\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tdis1[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n@@ -280,26 +280,26 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n-\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tdis2[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n-\t\tif (dis1[i] == 1 ||\n-\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n+\t\tif (dis1[i] == YES ||\n+\t\t    (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf1->rchg[i] = 1;\n+\t\t\txdf1->rchg[i] = YES;\n \t}\n \txdf1->nreff = nreff;\n \n \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n \t     i <= xdf2->dend; i++, recs++) {\n-\t\tif (dis2[i] == 1 ||\n-\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n+\t\tif (dis2[i] == YES ||\n+\t\t    (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf2->rchg[i] = 1;\n+\t\t\txdf2->rchg[i] = YES;\n \t}\n \txdf2->nreff = nreff;\n \n-- \ngitgitgadget\n"},{"id":"526842","messageId":"CABPp-BEjX6Nw90P=sgaSVrKLJX9HW6A8P0fyadBD0snURc10fg@mail.gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 00/10] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-19T23:30:00Z","receivedAt":"2025-09-19T23:31:48Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Sep 19, 2025 at 8:16 AM Ezekiel Newren via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> Changes since v2.\n>\n>  * No patch changes, just resending to get patch 9 to show up on the mailing\n>    list.\n>  * A few tweaks to the cover letter.\n>\n> Changes since v1, to address review feedback.\n>\n>  * Only include the clean up patches; The remaining patches will be split\n>    into a separate series.\n>  * Commit message clarifications.\n>  * Minor style cleanups.\n>  * Performance impacts included in commit message of patch 8.\n\nI read over this latest round and it addresses all my feedback from\nv1.  On top of all the nice code cleanups that this series provides, I\nappreciate the new detailed performance comparisons in the commit\nmessage in patch 8; while this series as a whole doesn't make the code\nappreciably faster yet, it's really cool that you've highlighted\nanother potential performance optimization (beyond the hashing one you\nalready highlighted elsewhere on the list) that we'll likely be able\nto realize once you get some further preparatory refactoring done.\nLooking forward to it.\n\nI think this round is ready to merge to next.\n"},{"id":"526843","messageId":"CAH=ZcbAnkrVTB3yi203t9-yq3LM+ucbvtu6Hh0Sp20YU3FeQZQ@mail.gmail.com","threadId":"64108","inReplyTo":"CABPp-BEjX6Nw90P=sgaSVrKLJX9HW6A8P0fyadBD0snURc10fg@mail.gmail.com","subject":"Re: [PATCH v3 00/10] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-19T23:37:12Z","receivedAt":"2025-09-19T23:37:25Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Fri, Sep 19, 2025 at 5:31 PM Elijah Newren <newren@gmail.com> wrote:\n> I read over this latest round and it addresses all my feedback from\n> v1.  On top of all the nice code cleanups that this series provides, I\n> appreciate the new detailed performance comparisons in the commit\n> message in patch 8; while this series as a whole doesn't make the code\n> appreciably faster yet, it's really cool that you've highlighted\n> another potential performance optimization (beyond the hashing one you\n> already highlighted elsewhere on the list) that we'll likely be able\n> to realize once you get some further preparatory refactoring done.\n> Looking forward to it.\n\nThanks for the positive feedback.\n\n> I think this round is ready to merge to next.\n\nI agree.\n"},{"id":"526857","messageId":"xmqqh5wxxcwq.fsf@gitster.g","threadId":"64108","inReplyTo":"784cffcef564b31a32c401b35f33610b85126f7b.1758294992.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-20T17:16:21Z","receivedAt":"2025-09-20T17:16:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> Move xdl_prepare_env() later in the file to avoid the need\n> for static forward declarations.\n>\n> Best-viewed-with: --color-moved\n\nTwo comments.\n\n - This is a bit unusual to see in the trailer.\n\n - It turned out that it was a very effective way to spot a typo for\n   me.  You should try it yourself before you send out your patches\n   ;-).\n\n> -int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n> -\t\t    xdfenv_t *xe) {\n> -\tlong enl1, enl2, sample;\n> -\txdlclassifier_t cf;\n> -\n> -\tmemset(&cf, 0, sizeof(cf));\n> ...\n> -\t\txdl_free_ctx(&xe->xdf1);\n> -\t\txdl_free_classifier(&cf);\n> -\t\treturn -1;\n> -\t}\n\nThe \"--color-moved\" painted the line above, with a single closing\nbrace, as removed, which stood out.  It turns out that ...\n\n> @@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2\n>  \n>  \treturn 0;\n>  }\n> +\n> +int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n> +\t\t    xdfenv_t *xe) {\n> +\tlong enl1, enl2, sample;\n> +\txdlclassifier_t cf;\n> +\n> +\tmemset(&cf, 0, sizeof(cf));\n> ...\n> +\t\txdl_free_ctx(&xe->xdf2);\n> +\t\txdl_free_ctx(&xe->xdf1);\n> +\t\txdl_free_classifier(&cf);\n> +\t\treturn -1;\n> +\t    }\n\n... the corresponding line in the postimage was shown as newly\nadded.  That was because it was indented incorrectly.\n\n> +\txdl_free_classifier(&cf);\n> +\n> +\treturn 0;\n> +}\n\nIf I do not spot any other issues in the series, I may just \"rebase\n-i\" to correct this single line to reduce the risk of mistakes,\ninstead of asking you to send an update.  We'll see.\n\nThe change is sensible, and the proposed log message does a good\njob, too.\n\nThanks.\n"},{"id":"526858","messageId":"xmqqcy7lxbyz.fsf@gitster.g","threadId":"64108","inReplyTo":"b79157e64f0950d25a23c50a8ea83cfddf67ddf5.1758294992.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 02/10] xdiff: delete local variables and initialize/free xdfile_t directly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-20T17:36:36Z","receivedAt":"2025-09-20T17:36:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> These local variables are essentially a hand-rolled additional\n> implementation of xdl_free_ctx() inlined into xdl_prepare_ctx(). Modify\n> the code to use the existing xdl_free_ctx() function so there aren't\n> two ways to free such variables.\n\nSensible.\n\n> +static void xdl_free_ctx(xdfile_t *xdf)\n> +{\n> +\txdl_free(xdf->rhash);\n> +\txdl_free(xdf->rindex);\n> +\txdl_free(xdf->rchg - 1);\n> +\txdl_free(xdf->ha);\n> +\txdl_free(xdf->recs);\n> +\txdl_cha_free(&xdf->rcha);\n> +}\n\nAnd I like the attention to the detail of where the opening brace is\nin the \"moved\" existing function ;-).\n\n>  abort:\n> -\txdl_free(ha);\n> -\txdl_free(rindex);\n> -\txdl_free(rchg);\n> -\txdl_free(rhash);\n> -\txdl_free(recs);\n> -\txdl_cha_free(&xdf->rcha);\n\nUpon an error, the original and the updated would behave a bit\ndifferently here, as the original would not have touched xdf, other\nthan its rcha member, so the caller _could_ make use of the original\ncontents in the structure after seeing an error return.  With the\nnew code, that is no longer possible.\n\nIts only caller is xdl_prepare_env(), and its caller is\nxdl_do_diff(), both of which passes the xdfenv_t *xe given by their\ncallers.  There are four callers of xdl_do_diff():\n\n xdl_fall_back_diff() in xdiff/xutils.c\n xdl_merge() and xdl_refine_conflicts() in xdiff/xmerge.c\n xdl_diff() in xdiff/xdiffi.c\n\nand all of them seem to pass an uninitialized piece of memory as\nxdfenv_t *xe down the callchain, so this behaviour change does not\nmake any difference.\n\n> +\txdl_free_ctx(xdf);\n\nAnd the code certainly is safer as we know we have one place to look\nat when we added a member that holds resources to xdfile_t.\n\n> -static void xdl_free_ctx(xdfile_t *xdf) {\n\nWe know clearing/freeing side is fine, but what about initializing\nside?\n\n>  static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n>  \t\t\t   xdlclassifier_t *cf, xdfile_t *xdf) {\n> +\tlong bsize;\n>  \tunsigned long hav;\n>  \tchar const *blk, *cur, *top, *prev;\n>  \txrecord_t *crec;\n>  \n> +\txdf->ha = NULL;\n> +\txdf->rindex = NULL;\n> +\txdf->rchg = NULL;\n> +\txdf->rhash = NULL;\n> +\txdf->recs = NULL;\n\nIt turns out that this is the only place that initializes xdfile_t\nin xdiff/ API, so we are covered on both ends.  xdiff/xprepare.c is\nthe only place we need to look at if we ever want to futz with\nxdfile_t members, and with this change, we know there aren't two\nways to free things in it (there weren't two ways to initialize,\neither, even before this patch).\n\nNice.\n"},{"id":"526859","messageId":"CAH=ZcbDzBvmzE75-7-BresWG0tYW05-1G-cnT+FLeuJe6JStcA@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqh5wxxcwq.fsf@gitster.g","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-20T17:41:43Z","receivedAt":"2025-09-20T17:41:58Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Sat, Sep 20, 2025 at 11:16 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > Best-viewed-with: --color-moved\n>\n> Two comments.\n>\n>  - This is a bit unusual to see in the trailer.\n\nI'm still not sure what the etiquette is for including those kinds of\nflags in a commit message. Could you show me my full commit message as\na response with your preferred way of adding those flags in a commit\nmessage? please.\n\n>  - It turned out that it was a very effective way to spot a typo for\n>    me.  You should try it yourself before you send out your patches\n>    ;-).\n\nHuh, I have viewed this patch with --color-moved dozens of times. I\nthink CLion (My IDE of choice for C) \"fixed\" that for me, and I didn't\nnotice until you pointed it out. Elijah missed it too. Maybe my\nterminal needs a more extreme contrast, so it's easier for me to spot\nthings like that.\n\n> If I do not spot any other issues in the series, I may just \"rebase\n> -i\" to correct this single line to reduce the risk of mistakes,\n> instead of asking you to send an update.  We'll see.\n\nIf that is the only problem then I would prefer that you fix that\nsingle mistake, so I don't flood the mailing list.\n"},{"id":"526860","messageId":"83F61517-06DB-402E-911A-E33EE07D54AE@gmail.com","threadId":"64108","inReplyTo":"xmqqh5wxxcwq.fsf@gitster.g","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-09-20T17:46:19Z","receivedAt":"2025-09-20T17:46:32Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"\n> Le 20 sept. 2025 à 13:16, Junio C Hamano <gitster@pobox.com> a écrit :\n> \n> ﻿\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n>> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>> \n>> Move xdl_prepare_env() later in the file to avoid the need\n>> for static forward declarations.\n>> \n>> Best-viewed-with: --color-moved\n> \n> Two comments.\n> \n> - This is a bit unusual to see in the trailer.\n\nThis was (loosely!) my suggestion, and I think Peff has once or twice done something similar.\n\nNo harm in working it into the prose instead, and I have no stake either way beyond a mild personal preference for the trailer. "},{"id":"526861","messageId":"xmqq8qi9xbf1.fsf@gitster.g","threadId":"64108","inReplyTo":"ddfee67e06bb56cfa8f11187dc27135bfce63647.1758294992.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 04/10] xdiff: delete xdl_get_rec() in xemit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-20T17:48:34Z","receivedAt":"2025-09-20T17:48:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> This function aliases the fields of xrecord_t, which makes it harder\n> to track the usages of those fields. Delete it.\n\nCan we phrase it in a way that is a bit more friendly to casual\nreaders?  It is hard to tell if the function is serving any useful\npurpose from the above.  If it is doing something useful, but is\nhard to read the surrounding code, that wouldn't be a good reason to\nremove it.\n\nIt seems that this patch does what people often call \"inlining a\nfunction call\".  I am not sure your comment about aliasing or if\nthat is why the code is harder than necessary to read.\n\n> -static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n> -\tlong size, psize = strlen(pre);\n> -\tchar const *rec;\n> -\n> -\tsize = xdl_get_rec(xdf, ri, &rec);\n> -\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n\nSo we used to get \"rec\" and \"size\" separately because xdl_get_rec()\nmade xrecord_t inaccessible to its callers.  To call a helper\nfunction that takes a <ptr, len> pair, xdl_get_rec() is the way to\ngrab that <ptr, len> pair out of the record index.\n\n> +static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n> +{\n> +\txrecord_t *rec = xdf->recs[ri];\n>  \n> +\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n>  \t\treturn -1;\n>  \t}\n\nBut by directly peeking into the xdf->recs[] array, we do not have\nto.  Each element of the array is the <ptr, len> pair we want.\n\nThe updated code is certainly easier to read.  That applies to all\nthe other callers touched by this patch.\n\nThanks.\n\n"},{"id":"526862","messageId":"CABPp-BFJPdVkA0U14_B_EzTj6rAi3jPFcQgW2uPqSz1ZBOdKAQ@mail.gmail.com","threadId":"64108","inReplyTo":"CAH=ZcbDzBvmzE75-7-BresWG0tYW05-1G-cnT+FLeuJe6JStcA@mail.gmail.com","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2025-09-20T18:31:23Z","receivedAt":"2025-09-20T18:31:36Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Sat, Sep 20, 2025 at 10:41 AM Ezekiel Newren <ezekielnewren@gmail.com> wrote:\n>\n> On Sat, Sep 20, 2025 at 11:16 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > > Best-viewed-with: --color-moved\n> >\n> > Two comments.\n> >\n> >  - This is a bit unusual to see in the trailer.\n>\n> I'm still not sure what the etiquette is for including those kinds of\n> flags in a commit message. Could you show me my full commit message as\n> a response with your preferred way of adding those flags in a commit\n> message? please.\n\nThis command run in git.git will give lots of examples from various authors:\n   git log --grep=\"est viewed with\"\n\nI didn't call out the trailer because I saw Ben suggest it in this\nthread, and he said he had seen Peff do this.  But, searching right\nnow, I can't actually find any such trailers from Peff or anyone else\n-- am I just searching wrong?\n\n> >  - It turned out that it was a very effective way to spot a typo for\n> >    me.  You should try it yourself before you send out your patches\n> >    ;-).\n>\n> Huh, I have viewed this patch with --color-moved dozens of times. I\n> think CLion (My IDE of choice for C) \"fixed\" that for me, and I didn't\n> notice until you pointed it out. Elijah missed it too. Maybe my\n> terminal needs a more extreme contrast, so it's easier for me to spot\n> things like that.\n\nYep, I did.  In fact, in v1 of the series Ezekiel didn't call out the\n--color-moved thing, but I used it and mentioned it in my review, and\nEzekiel decided to mention it in v2.  When I looked at the\n--color-moved output, I saw the sea of purple and blue and skimmed\nquickly to verify it was all purple and blue -- and apparently didn't\nsee the one red character amidst the purple and the one green\ncharacter amidst the blue.  Since the range-diff showed no differences\nto v1 and I had already thought this patch was fine from v1, I didn't\nlook at it any closer.\n\nSo, not only did I miss it, but I missed it despite being the one to\nsuggest that flag after using it myself.  Oops.\n"},{"id":"526867","messageId":"20250920184638.GA1010995@coredump.intra.peff.net","threadId":"64108","inReplyTo":"83F61517-06DB-402E-911A-E33EE07D54AE@gmail.com","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-20T18:46:38Z","receivedAt":"2025-09-20T18:46:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Sep 20, 2025 at 01:46:19PM -0400, Ben Knoble wrote:\n\n> >> Best-viewed-with: --color-moved\n> > \n> > Two comments.\n> > \n> > - This is a bit unusual to see in the trailer.\n> \n> This was (loosely!) my suggestion, and I think Peff has once or twice\n> done something similar.\n\nI don't think I've ever used a trailer like that, but I do sometimes\nmention it in prose. I'll sometimes put it in comments below the \"---\"\nline, though.\n\n-Peff\n\nPS I sometimes find:\n\n     git log --format='%(trailers:only,keyonly)' |\n     sort | uniq -c | sort -rn\n\n   amusing to look through for this sort of thing.\n"},{"id":"526872","messageId":"C6E7FA9B-E415-4841-9DE0-BA597E5E54D9@gmail.com","threadId":"64108","inReplyTo":"CABPp-BFJPdVkA0U14_B_EzTj6rAi3jPFcQgW2uPqSz1ZBOdKAQ@mail.gmail.com","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-09-20T22:25:00Z","receivedAt":"2025-09-20T22:25:12Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"\n> Le 20 sept. 2025 à 14:31, Elijah Newren <newren@gmail.com> a écrit :\n> \n> ﻿On Sat, Sep 20, 2025 at 10:41 AM Ezekiel Newren <ezekielnewren@gmail.com> wrote:\n>> \n>> On Sat, Sep 20, 2025 at 11:16 AM Junio C Hamano <gitster@pobox.com> wrote:\n>>>> Best-viewed-with: --color-moved\n>>> \n>>> Two comments.\n>>> \n>>> - This is a bit unusual to see in the trailer.\n>> \n>> I'm still not sure what the etiquette is for including those kinds of\n>> flags in a commit message. Could you show me my full commit message as\n>> a response with your preferred way of adding those flags in a commit\n>> message? please.\n> \n> This command run in git.git will give lots of examples from various authors:\n>   git log --grep=\"est viewed with\"\n> \n> I didn't call out the trailer because I saw Ben suggest it in this\n> thread, and he said he had seen Peff do this.  But, searching right\n> now, I can't actually find any such trailers from Peff or anyone else\n> -- am I just searching wrong?\n\nLooks like I misremembered. (Maybe I was remembering Peff’s notes, but I thought I had seen a differently-named trailer.) My apologies!"},{"id":"526873","messageId":"7323B02B-4273-4952-83EC-CD467A2365B5@gmail.com","threadId":"64108","inReplyTo":"20250920184638.GA1010995@coredump.intra.peff.net","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-09-20T22:25:30Z","receivedAt":"2025-09-20T22:25:43Z","isPatch":true,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"\n> Le 20 sept. 2025 à 14:46, Jeff King <peff@peff.net> a écrit :\n> \n> ﻿On Sat, Sep 20, 2025 at 01:46:19PM -0400, Ben Knoble wrote:\n> \n>>>> Best-viewed-with: --color-moved\n>>> \n>>> Two comments.\n>>> \n>>> - This is a bit unusual to see in the trailer.\n>> \n>> This was (loosely!) my suggestion, and I think Peff has once or twice\n>> done something similar.\n> \n> I don't think I've ever used a trailer like that, but I do sometimes\n> mention it in prose. I'll sometimes put it in comments below the \"---\"\n> line, though.\n> \n> -Peff\n\nSilly memory. Sorry :)\n\n\n> PS I sometimes find:\n> \n>     git log --format='%(trailers:only,keyonly)' |\n>     sort | uniq -c | sort -rn\n> \n>   amusing to look through for this sort of thing.\n"},{"id":"526874","messageId":"xmqq4iswyccd.fsf@gitster.g","threadId":"64108","inReplyTo":"CABPp-BFJPdVkA0U14_B_EzTj6rAi3jPFcQgW2uPqSz1ZBOdKAQ@mail.gmail.com","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-20T22:43:14Z","receivedAt":"2025-09-20T22:43:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n>> Huh, I have viewed this patch with --color-moved dozens of times. I\n>> think CLion (My IDE of choice for C) \"fixed\" that for me, and I didn't\n>> notice until you pointed it out. Elijah missed it too. Maybe my\n>> terminal needs a more extreme contrast, so it's easier for me to spot\n>> things like that.\n>\n> Yep, I did.  In fact, in v1 of the series Ezekiel didn't call out the\n> --color-moved thing, but I used it and mentioned it in my review, and\n> Ezekiel decided to mention it in v2.  When I looked at the\n> --color-moved output, I saw the sea of purple and blue and skimmed\n> quickly to verify it was all purple and blue -- and apparently didn't\n> see the one red character amidst the purple and the one green\n> character amidst the blue.  Since the range-diff showed no differences\n> to v1 and I had already thought this patch was fine from v1, I didn't\n> look at it any closer.\n>\n> So, not only did I miss it, but I missed it despite being the one to\n> suggest that flag after using it myself.  Oops.\n\nHeh, no harm done.  With enough eyeballs, these little things will\nbe found ;-)\n"},{"id":"526875","messageId":"xmqqy0q8wxc6.fsf@gitster.g","threadId":"64108","inReplyTo":"20250920184638.GA1010995@coredump.intra.peff.net","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-20T22:52:41Z","receivedAt":"2025-09-20T22:52:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I don't think I've ever used a trailer like that, but I do sometimes\n> mention it in prose. I'll sometimes put it in comments below the \"---\"\n> line, though.\n\nYeah, I am on the fence between in prose and below the three-dash\nline, as unlike other comments that often help only those during\nreview (e.g., what is different relative to the previous round),\nthis hint is helpful to those who read \"git log -p\".\n\n> PS I sometimes find:\n>\n>      git log --format='%(trailers:only,keyonly)' |\n>      sort | uniq -c | sort -rn\n>\n>    amusing to look through for this sort of thing.\n\nThe top entries are as expecte (this is with --since=5.years)\n\n  24336 Signed-off-by\n  17501 \n    740 Helped-by\n    703 Reviewed-by\n    495 Acked-by\n    485 Reported-by\n    420 Mentored-by\n    186 Co-authored-by\n    164 Suggested-by\n\nbut I have to wonder what the empty one is.\n"},{"id":"526877","messageId":"20250920231508.GA1018519@coredump.intra.peff.net","threadId":"64108","inReplyTo":"xmqqy0q8wxc6.fsf@gitster.g","subject":"Re: [PATCH v3 01/10] xdiff: delete static forward declarations in xprepare","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-20T23:15:08Z","receivedAt":"2025-09-20T23:15:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Sep 20, 2025 at 03:52:41PM -0700, Junio C Hamano wrote:\n\n> > PS I sometimes find:\n> >\n> >      git log --format='%(trailers:only,keyonly)' |\n> >      sort | uniq -c | sort -rn\n> >\n> >    amusing to look through for this sort of thing.\n> \n> The top entries are as expecte (this is with --since=5.years)\n> \n>   24336 Signed-off-by\n>   17501 \n>     740 Helped-by\n>     703 Reviewed-by\n>     495 Acked-by\n>     485 Reported-by\n>     420 Mentored-by\n>     186 Co-authored-by\n>     164 Suggested-by\n> \n> but I have to wonder what the empty one is.\n\nI think %(trailers) always ends each trailer line with its own newline,\nincluding the final one.  So then --format adds its own final newline,\nand you get a bunch of blank lines between commits. It's easier to see\nwith --format='%h %s%n(trailers:only,keyonly)'.\n\nI don't think you'd want to change %(trailers) to omit the final\nnewline, otherwise a format like:\n\n  git log --format='%h %s%n%(trailers)---'\n\nwould break.\n\nPossibly the formatter could be more clever about adding the final\nnewline only when the formatted text does not itself end with one. But I\nsuspect some people's custom formats do rely on the current behavior, so\nyou probably need some kind of new option or format placeholder.\n\n-Peff\n"},{"id":"526880","messageId":"xmqqms6owu7o.fsf@gitster.g","threadId":"64108","inReplyTo":"1e404c3290bd4ba8b4973a597b976c301138cf73.1758294992.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 10/10] xdiff: treat xdfile_t.rchg like an enum","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-21T00:00:11Z","receivedAt":"2025-09-21T00:00:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\n> index 2cecde5afe..7092879829 100644\n> --- a/xdiff/xdiff.h\n> +++ b/xdiff/xdiff.h\n> @@ -27,6 +27,10 @@\n>  extern \"C\" {\n>  #endif /* #ifdef __cplusplus */\n>  \n> +#define NO 0\n> +#define YES 1\n> +#define MAYBE 2\n\n<xdiff/xdiff.h> is included surprisingly widely.\n\nI am not comfortable with the idea of exposing a set of overly\ngenericly named macros like these, especially when they are *meant*\nonly to be used with xdfile_t.rchg, to those *.c files.  So far,\nwhen they include <xdiff-interface.h> (or <ll-merge.h>), they have\nbeen rest assured that their namespaces won't be contaminated and\nthey would not risk stepping on others' toes as long as they stay\naway from inventing their own xdsomething or s_xsomething (neither\nof which is quite similar to how we name our symbols and types).\n\nBut these are names that they may legitimately want to use for their\nown use.\n\n\n"},{"id":"526881","messageId":"CAH=ZcbD4oi1hdWDK8s2g6mQGrk+kwWffnSYE5gQZ_d3Z2Z3euA@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqms6owu7o.fsf@gitster.g","subject":"Re: [PATCH v3 10/10] xdiff: treat xdfile_t.rchg like an enum","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-21T00:38:13Z","receivedAt":"2025-09-21T00:38:26Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Sat, Sep 20, 2025 at 6:00 PM Junio C Hamano <gitster@pobox.com> wrote:\n> I am not comfortable with the idea of exposing a set of overly\n> genericly named macros like these, especially when they are *meant*\n> only to be used with xdfile_t.rchg, to those *.c files.  So far,\n> when they include <xdiff-interface.h> (or <ll-merge.h>), they have\n> been rest assured that their namespaces won't be contaminated and\n> they would not risk stepping on others' toes as long as they stay\n> away from inventing their own xdsomething or s_xsomething (neither\n> of which is quite similar to how we name our symbols and types).\n\nWhat if I move NO, YES, MAYBE into xprepare.c and refactor `char rchg`\nto `bool changed`? The problem with bool is that C needs to include\nstdbool.h to match how Rust defines bool. git-compat-util.h didn't\ninclude it, then it did, then it didn't because compat/posix.h\nincluded it instead.\n\nHow do you feel about xdiff.h including compat/posix.h too? If we\ndon't use bool on the C side then Rust is going to be littered with\nsome_condition != 0 or other_condition == 0 and won't be as clear that\nit's a boolean instead of a numeric type.\n"},{"id":"526886","messageId":"c8a35919-e1d1-4d85-b7d7-81923667af60@gmail.com","threadId":"64108","inReplyTo":"CAH=ZcbD4oi1hdWDK8s2g6mQGrk+kwWffnSYE5gQZ_d3Z2Z3euA@mail.gmail.com","subject":"Re: [PATCH v3 10/10] xdiff: treat xdfile_t.rchg like an enum","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-21T09:19:35Z","receivedAt":"2025-09-21T09:19:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 21/09/2025 01:38, Ezekiel Newren wrote:\n> On Sat, Sep 20, 2025 at 6:00 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> I am not comfortable with the idea of exposing a set of overly\n>> genericly named macros like these, especially when they are *meant*\n>> only to be used with xdfile_t.rchg, to those *.c files.  So far,\n>> when they include <xdiff-interface.h> (or <ll-merge.h>), they have\n>> been rest assured that their namespaces won't be contaminated and\n>> they would not risk stepping on others' toes as long as they stay\n>> away from inventing their own xdsomething or s_xsomething (neither\n>> of which is quite similar to how we name our symbols and types).\n> \n> What if I move NO, YES, MAYBE into xprepare.c and refactor `char rchg`\n> to `bool changed`? \n\nThat would be good as it avoids the possibility of using MAYBE outside \nof xprepare.c\n\n> The problem with bool is that C needs to include\n> stdbool.h to match how Rust defines bool. git-compat-util.h didn't\n> include it, then it did, then it didn't because compat/posix.h\n> included it instead.\n\n75a044f748f (git-compat-util.h: split out POSIX-emulating bits, \n2025-02-18) moved '#include <stdbool.h>' from \"git-compat-util.h\" into \n\"compat/posix.h\" but also added '#include \"compat/posix.h\"' to \n\"git-compat-util.h\" so there should be no problem.\n\nThanks\n\nPhillip\n\n> \n> How do you feel about xdiff.h including compat/posix.h too? If we\n> don't use bool on the C side then Rust is going to be littered with\n> some_condition != 0 or other_condition == 0 and won't be as clear that\n> it's a boolean instead of a numeric type.\n\n"},{"id":"526891","messageId":"cd39a321-a6c4-4126-a716-dc0b0cffa68d@gmail.com","threadId":"64108","inReplyTo":"ddfee67e06bb56cfa8f11187dc27135bfce63647.1758294992.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 04/10] xdiff: delete xdl_get_rec() in xemit","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-21T13:06:10Z","receivedAt":"2025-09-21T13:06:02Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ezekiel\n\nOn 19/09/2025 16:16, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> \n> This function aliases the fields of xrecord_t, which makes it harder\n> to track the usages of those fields. Delete it.\n\nPatch 6 goes the other way and introduces a getter function that hides \nthe field accesses so I'm not sure why this one is so bad that it needs \nto be removed.\n\nThanks\n\nPhillip\n\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>   xdiff/xemit.c | 38 +++++++++++++-------------------------\n>   1 file changed, 13 insertions(+), 25 deletions(-)\n> \n> diff --git a/xdiff/xemit.c b/xdiff/xemit.c\n> index 1d40c9cb40..b3793e81e2 100644\n> --- a/xdiff/xemit.c\n> +++ b/xdiff/xemit.c\n> @@ -22,21 +22,11 @@\n>   \n>   #include \"xinclude.h\"\n>   \n> -static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n> -\n> -\t*rec = xdf->recs[ri]->ptr;\n> -\n> -\treturn xdf->recs[ri]->size;\n> -}\n> -\n> -\n> -static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n> -\tlong size, psize = strlen(pre);\n> -\tchar const *rec;\n> -\n> -\tsize = xdl_get_rec(xdf, ri, &rec);\n> -\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n> +static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n> +{\n> +\txrecord_t *rec = xdf->recs[ri];\n>   \n> +\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n>   \t\treturn -1;\n>   \t}\n>   \n> @@ -120,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n>   static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n>   \t\t\t   char *buf, long sz)\n>   {\n> -\tconst char *rec;\n> -\tlong len = xdl_get_rec(xdf, ri, &rec);\n> +\txrecord_t *rec = xdf->recs[ri];\n> +\n>   \tif (!xecfg->find_func)\n> -\t\treturn def_ff(rec, len, buf, sz);\n> -\treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n> +\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n> +\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n>   }\n>   \n>   static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n> @@ -160,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n>   \n>   static int is_empty_rec(xdfile_t *xdf, long ri)\n>   {\n> -\tconst char *rec;\n> -\tlong len = xdl_get_rec(xdf, ri, &rec);\n> +\txrecord_t *rec = xdf->recs[ri];\n> +\tlong i = 0;\n>   \n> -\twhile (len > 0 && XDL_ISSPACE(*rec)) {\n> -\t\trec++;\n> -\t\tlen--;\n> -\t}\n> -\treturn !len;\n> +\tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n> +\n> +\treturn i == rec->size;\n>   }\n>   \n>   int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n\n"},{"id":"526892","messageId":"7979d8f5-1e97-4429-891a-678adb3ba54f@gmail.com","threadId":"64108","inReplyTo":"807ce3e5aae0253dd58c7a4d0bd04ca341b070c8.1758294992.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 05/10] xdiff: delete struct diffdata_t","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-21T13:06:26Z","receivedAt":"2025-09-21T13:06:15Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ezekiel\n\nOn 19/09/2025 16:16, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> \n> Every field in this struct is an alias for a certain field in xdfile_t.\n> \n> diffdata_t.nrec   -> xdfile_t.nreff\n> diffdata_t.ha     -> xdfile_t.ha\n> diffdata_t.rindex -> xdfile_t.rindex\n> diffdata_t.rchg   -> xdfile_t.rchg\n\nThat explains some of the changes here (so long as one assumes the \naliasing is a bad thing) but it does not explain why it is a good idea \nto remove the local variables rchg[12] and rindex[12] and instead \ndereference xdf[12] inside the loops\n\nThanks\n\nPhillip\n\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>   xdiff/xdiffi.c | 32 ++++++++------------------------\n>   xdiff/xdiffi.h | 11 ++---------\n>   2 files changed, 10 insertions(+), 33 deletions(-)\n> \n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index 5a96e36dfb..bbf0161f84 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -257,10 +257,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n>    * sub-boxes by calling the box splitting function. Note that the real job\n>    * (marking changed lines) is done in the two boundary reaching checks.\n>    */\n> -int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n> -\t\t diffdata_t *dd2, long off2, long lim2,\n> +int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n> +\t\t xdfile_t *xdf2, long off2, long lim2,\n>   \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n> -\tunsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;\n> +\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n>   \n>   \t/*\n>   \t * Shrink the box by walking through each diagonal snake (SW and NE).\n> @@ -273,17 +273,11 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n>   \t * be obviously changed.\n>   \t */\n>   \tif (off1 == lim1) {\n> -\t\tchar *rchg2 = dd2->rchg;\n> -\t\tlong *rindex2 = dd2->rindex;\n> -\n>   \t\tfor (; off2 < lim2; off2++)\n> -\t\t\trchg2[rindex2[off2]] = 1;\n> +\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n>   \t} else if (off2 == lim2) {\n> -\t\tchar *rchg1 = dd1->rchg;\n> -\t\tlong *rindex1 = dd1->rindex;\n> -\n>   \t\tfor (; off1 < lim1; off1++)\n> -\t\t\trchg1[rindex1[off1]] = 1;\n> +\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n>   \t} else {\n>   \t\txdpsplit_t spl;\n>   \t\tspl.i1 = spl.i2 = 0;\n> @@ -300,9 +294,9 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n>   \t\t/*\n>   \t\t * ... et Impera.\n>   \t\t */\n> -\t\tif (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,\n> +\t\tif (xdl_recs_cmp(xdf1, off1, spl.i1, xdf2, off2, spl.i2,\n>   \t\t\t\t kvdf, kvdb, spl.min_lo, xenv) < 0 ||\n> -\t\t    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,\n> +\t\t    xdl_recs_cmp(xdf1, spl.i1, lim1, xdf2, spl.i2, lim2,\n>   \t\t\t\t kvdf, kvdb, spl.min_hi, xenv) < 0) {\n>   \n>   \t\t\treturn -1;\n> @@ -318,7 +312,6 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n>   \tlong ndiags;\n>   \tlong *kvd, *kvdf, *kvdb;\n>   \txdalgoenv_t xenv;\n> -\tdiffdata_t dd1, dd2;\n>   \tint res;\n>   \n>   \tif (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)\n> @@ -357,16 +350,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n>   \txenv.snake_cnt = XDL_SNAKE_CNT;\n>   \txenv.heur_min = XDL_HEUR_MIN_COST;\n>   \n> -\tdd1.nrec = xe->xdf1.nreff;\n> -\tdd1.ha = xe->xdf1.ha;\n> -\tdd1.rchg = xe->xdf1.rchg;\n> -\tdd1.rindex = xe->xdf1.rindex;\n> -\tdd2.nrec = xe->xdf2.nreff;\n> -\tdd2.ha = xe->xdf2.ha;\n> -\tdd2.rchg = xe->xdf2.rchg;\n> -\tdd2.rindex = xe->xdf2.rindex;\n> -\n> -\tres = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,\n> +\tres = xdl_recs_cmp(&xe->xdf1, 0, xe->xdf1.nreff, &xe->xdf2, 0, xe->xdf2.nreff,\n>   \t\t\t   kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,\n>   \t\t\t   &xenv);\n>   \txdl_free(kvd);\n> diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\n> index 126c9d8ff4..49e52c67f9 100644\n> --- a/xdiff/xdiffi.h\n> +++ b/xdiff/xdiffi.h\n> @@ -24,13 +24,6 @@\n>   #define XDIFFI_H\n>   \n>   \n> -typedef struct s_diffdata {\n> -\tlong nrec;\n> -\tunsigned long const *ha;\n> -\tlong *rindex;\n> -\tchar *rchg;\n> -} diffdata_t;\n> -\n>   typedef struct s_xdalgoenv {\n>   \tlong mxcost;\n>   \tlong snake_cnt;\n> @@ -46,8 +39,8 @@ typedef struct s_xdchange {\n>   \n>   \n>   \n> -int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n> -\t\t diffdata_t *dd2, long off2, long lim2,\n> +int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n> +\t\t xdfile_t *xdf2, long off2, long lim2,\n>   \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);\n>   int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n>   \t\txdfenv_t *xe);\n\n\n"},{"id":"526893","messageId":"d22c61da-bd78-47cd-b3b6-adc0d3873fef@gmail.com","threadId":"64108","inReplyTo":"e1e94107c9722b751d6111460b17e02a7ffd96d1.1758294992.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 07/10] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-21T13:06:52Z","receivedAt":"2025-09-21T13:06:41Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ezekiol\n\nOn 19/09/2025 16:16, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> \n> The fields from xdlclass_t are aliases of xrecord_t:\n> xdlclass_t.line -> xrecord_t.ptr\n> xdlclass_t.size -> xrecord_t.size\n> xdlclass_t.ha   -> xrecord_t.ha\n> \n> Remove aliasing from xdlclass_t, to reduce future refactoring mistakes.\n\nThis is a rather nebulous reason. I assume this is about changing the \ntypes used in xrecord_t in which case it would be helpful to say \nsomething like\n\nxdlclass_t carries a copy of the data in xrecord_t, but instead of \nembedding xrecord_t it duplicates the individual fields. A future commit \nwill change the types used in xrecord_t so embed it in xdlclass_t first \nso we don't have to remember to change the types here as well.\n\nAs we're embedding the struct, instead of doing\n\n> -\t\trcrec->line = line;\n> -\t\trcrec->size = rec->size;\n> -\t\trcrec->ha = rec->ha;\n> +\t\trcrec->rec.ptr = rec->ptr;\n> +\t\trcrec->rec.size = rec->size;\n> +\t\trcrec->rec.ha = rec->ha;\n\nit would be simpler do do\n\n-\t\trcrec->line = line;\n-\t\trcrec->size = rec->size;\n-\t\trcrec->ha = rec->ha;\n+\t\trcrec->rec = rec;\n\nwhich would make it clear we're copying all the struct members.\n\nThanks\n\nPhillip\n\n\n"},{"id":"526894","messageId":"daad78da-752b-40f4-9c69-d6f6597ca258@gmail.com","threadId":"64108","inReplyTo":"fd541355609b715ebe94f9e025450165bd91d170.1758294992.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 09/10] xdiff: delete rchg aliasing","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-21T13:07:02Z","receivedAt":"2025-09-21T13:06:51Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ezekiel\n\nWhat is the purpose of this change. On the face of it it makes the code \nmore verbose and introduces an extra pointer dereference into the loop \ncondition. The compiler may lift the deference out of the loop but it \nwould be helpful to know why this change is useful.\n\nThanks\n\nPhillip\n\nOn 19/09/2025 16:16, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> \n> Best-viewed-with: --color-words\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>   xdiff/xdiffi.c | 7 +++----\n>   1 file changed, 3 insertions(+), 4 deletions(-)\n> \n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index a66125d44a..83c4cff6f7 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -932,16 +932,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n>   \n>   int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n>   \txdchange_t *cscr = NULL, *xch;\n> -\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n>   \tlong i1, i2, l1, l2;\n>   \n>   \t/*\n>   \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n>   \t */\n>   \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n> -\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n> -\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n> -\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n> +\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n> +\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n> +\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n>   \n>   \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n>   \t\t\t\txdl_free_script(cscr);\n\n"},{"id":"526898","messageId":"CAH=ZcbD6aw-=93URRjYKfYvt+0z2YrcwfyY9TmZHSsXXsXiB+g@mail.gmail.com","threadId":"64108","inReplyTo":"cd39a321-a6c4-4126-a716-dc0b0cffa68d@gmail.com","subject":"Re: [PATCH v3 04/10] xdiff: delete xdl_get_rec() in xemit","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-21T15:07:27Z","receivedAt":"2025-09-21T15:07:40Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Sun, Sep 21, 2025 at 7:06 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> Patch 6 goes the other way and introduces a getter function that hides\n> the field accesses so I'm not sure why this one is so bad that it needs\n> to be removed.\n\nI've added a copy below of the two functions for easy reference.\nTo quote myself[1]:\n```\nThe fields rindex and ha of xdfile_t are specific to the classic diff\n(myers and minimal). I plan on creating a struct for classic diff, but\nthere's a lot of cleanup that needs to be done before that can happen,\nand leaving ha in would make those cleanups harder to follow.\n```\nget_hash() is a scaffolding function that will reduce refactor churn.\nIt changes a few times in this patch series alone, and will change a\nfew more times before the code is cleaned up enough to delete it. By\ncontrast, xdl_get_rec() merely performs an array index, which is so\ntrivial that it doesn't justify having its own function.\n\nget_hash() reduces confusion because xdfile_t.ha is an array that is a\nsparse copy of xrecord_t.ha values from xdfile_t.recs. The field\nxrecord_t.ha is confusing on its own, as it is first used to store the\nhash of the line and later repurposed as a minimal perfect hash[2].\n\nstatic long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n\n     *rec = xdf->recs[ri]->ptr;\n\n     return xdf->recs[ri]->size;\n}\n\nstatic unsigned long get_hash(xdfile_t *xdf, long index)\n{\n    return xdf->recs[xdf->rindex[index]]->ha;\n}\n\n[1] https://lore.kernel.org/git/0bacb1191dad2748d2afa79665f1293b0381bde1.1758294992.git.gitgitgadget@gmail.com/\n[2] https://lore.kernel.org/git/af96763036e13480ed4e6dfedcade5b2c90e414c.1757274320.git.gitgitgadget@gmail.com/\n"},{"id":"526900","messageId":"CAH=ZcbC1+cTP0Gah8tuz9-kKxJKx+TghTMvLPQ=8E_2wRUK8FA@mail.gmail.com","threadId":"64108","inReplyTo":"7979d8f5-1e97-4429-891a-678adb3ba54f@gmail.com","subject":"Re: [PATCH v3 05/10] xdiff: delete struct diffdata_t","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-21T16:03:06Z","receivedAt":"2025-09-21T16:03:19Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Sun, Sep 21, 2025 at 7:06 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > Every field in this struct is an alias for a certain field in xdfile_t.\n> >\n> > diffdata_t.nrec   -> xdfile_t.nreff\n> > diffdata_t.ha     -> xdfile_t.ha\n> > diffdata_t.rindex -> xdfile_t.rindex\n> > diffdata_t.rchg   -> xdfile_t.rchg\n>\n> That explains some of the changes here (so long as one assumes the\n> aliasing is a bad thing) but it does not explain why it is a good idea\n> to remove the local variables rchg[12] and rindex[12] and instead\n> dereference xdf[12] inside the loops\n\nI removed the struct and local variable aliases to make it easier for\nusage-tracking tools to follow where fields are actually used. Whether\nsomeone relies on grep, ctags/etags, or more modern tooling, aliases\nhide the true field accesses and make it harder to track or refactor\nthose fields consistently. By using the fields directly, every\nreference is literal and unambiguous. Also the local variable names\ndon't add any meaning and only barely shorten the lines where they are\nused.\n"},{"id":"526901","messageId":"CAH=ZcbA95gYZggsqTw=hv0PZR0FkHByVib7N79XQ3c6-1OySBg@mail.gmail.com","threadId":"64108","inReplyTo":"d22c61da-bd78-47cd-b3b6-adc0d3873fef@gmail.com","subject":"Re: [PATCH v3 07/10] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-21T16:07:20Z","receivedAt":"2025-09-21T16:07:34Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Sun, Sep 21, 2025 at 7:06 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > The fields from xdlclass_t are aliases of xrecord_t:\n> > xdlclass_t.line -> xrecord_t.ptr\n> > xdlclass_t.size -> xrecord_t.size\n> > xdlclass_t.ha   -> xrecord_t.ha\n> >\n> > Remove aliasing from xdlclass_t, to reduce future refactoring mistakes.\n>\n> This is a rather nebulous reason. I assume this is about changing the\n> types used in xrecord_t.\n\nYes, this is a stepping stone to many more refactorings that I have planned.\n\n> in which case it would be helpful to say something like:\n> xdlclass_t carries a copy of the data in xrecord_t, but instead of\n> embedding xrecord_t it duplicates the individual fields. A future commit\n> will change the types used in xrecord_t so embed it in xdlclass_t first\n> so we don't have to remember to change the types here as well.\n\n I've incorporated your wording into my commit message.\n\n> As we're embedding the struct, instead of doing\n>\n> > -             rcrec->line = line;\n> > -             rcrec->size = rec->size;\n> > -             rcrec->ha = rec->ha;\n> > +             rcrec->rec.ptr = rec->ptr;\n> > +             rcrec->rec.size = rec->size;\n> > +             rcrec->rec.ha = rec->ha;\n>\n> it would be simpler do do\n>\n> -               rcrec->line = line;\n> -               rcrec->size = rec->size;\n> -               rcrec->ha = rec->ha;\n> +               rcrec->rec = rec;\n>\n> which would make it clear we're copying all the struct members.\n\nMakes sense. I'll incorporate that.\n"},{"id":"526902","messageId":"CAH=ZcbAEho12zWy4fTis0hjUa8+wXm=WwHNG0Dj-Fd0GBvoe7g@mail.gmail.com","threadId":"64108","inReplyTo":"c8a35919-e1d1-4d85-b7d7-81923667af60@gmail.com","subject":"Re: [PATCH v3 10/10] xdiff: treat xdfile_t.rchg like an enum","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-21T16:11:17Z","receivedAt":"2025-09-21T16:11:30Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Sun, Sep 21, 2025 at 3:19 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> > What if I move NO, YES, MAYBE into xprepare.c and refactor `char rchg`\n> > to `bool changed`?\n>\n> That would be good as it avoids the possibility of using MAYBE outside\n> of xprepare.c\n\nOk, I'll do that.\n\n> > The problem with bool is that C needs to include\n> > stdbool.h to match how Rust defines bool. git-compat-util.h didn't\n> > include it, then it did, then it didn't because compat/posix.h\n> > included it instead.\n>\n> 75a044f748f (git-compat-util.h: split out POSIX-emulating bits,\n> 2025-02-18) moved '#include <stdbool.h>' from \"git-compat-util.h\" into\n> \"compat/posix.h\" but also added '#include \"compat/posix.h\"' to\n> \"git-compat-util.h\" so there should be no problem.\n\nOh, I missed that when I was reading through git-compat-util.h. I just\nsearched for `stdbool.h` and saw that it was in compat/posix.h instead\nof git-compat-util.h.\n"},{"id":"526903","messageId":"CAH=ZcbCqnr_WteidXTETCFxO=NGuT84VRg-ZsU6Si6coP+Z2uQ@mail.gmail.com","threadId":"64108","inReplyTo":"daad78da-752b-40f4-9c69-d6f6597ca258@gmail.com","subject":"Re: [PATCH v3 09/10] xdiff: delete rchg aliasing","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-21T16:37:23Z","receivedAt":"2025-09-21T16:37:36Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Sun, Sep 21, 2025 at 7:06 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> What is the purpose of this change. On the face of it it makes the code\n> more verbose and introduces an extra pointer dereference into the loop\n> condition. The compiler may lift the dereference out of the loop but it\n> would be helpful to know why this change is useful.\n\nMost of Git directly accesses rchg, so changing this to also directly\naccess it makes the code more consistent. Also, usage tracking tools\nlike ctags or a modern IDE won't show all uses. This makes it harder\nto refactor or audit. Dropping the aliases means every access is\nvisible and discoverable through the actual field name.\n"},{"id":"526967","messageId":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v3.git.git.1758294992.gitgitgadget@gmail.com","subject":"[PATCH v4 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:29Z","receivedAt":"2025-09-22T19:51:45Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"Changes since v3.\n\n * Address review feedback.\n * Split the deletion of xdl_get_rec() into 2 commits.\n * Move NO, YES, MAYBE into xprepare.c, and use bool literals.\n * refactor 'char rchg' to 'bool changed'\n\nChanges since v2.\n\n * No patch changes, just resending to get patch 9 to show up on the mailing\n   list.\n * A few tweaks to the cover letter.\n\nChanges since v1, to address review feedback.\n\n * Only include the clean up patches; The remaining patches will be split\n   into a separate series.\n * Commit message clarifications.\n * Minor style cleanups.\n * Performance impacts included in commit message of patch 8.\n\n\nRelevant part of the original cover letter follows:\n===================================================\n\nBefore:\n\ntypedef struct s_xrecord {\n\tstruct s_xrecord *next;\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\tchastore_t rcha;\n\tlong nrec;\n\tunsigned int hbits;\n\txrecord_t **rhash;\n\tlong dstart, dend;\n\txrecord_t **recs;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n\tunsigned long *ha;\n} xdfile_t;\n\n\nAfter cleanup:\n\ntypedef struct s_xrecord {\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\txrecord_t *recs;\n\tlong nrec;\n\tlong dstart, dend;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n} xdfile_t;\n\n\n===\n\nEzekiel Newren (12):\n  xdiff: delete static forward declarations in xprepare\n  xdiff: delete local variables and initialize/free xdfile_t directly\n  xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  xdiff: delete superfluous function xdl_get_rec() in xemit\n  xdiff: delete superfluous local variables that alias fields in\n    xrecord_t\n  xdiff: delete struct diffdata_t\n  xdiff: delete redundant array xdfile_t.ha\n  xdiff: delete fields ha, line, size in xdlclass_t in favor of an\n    xrecord_t\n  xdiff: delete chastore from xdfile_t\n  xdiff: delete rchg aliasing\n  xdiff: use bool literals for xdfile_t.rchg\n  xdiff: refactor 'char *rchg' to 'bool *changed' in xdfile_t\n\n xdiff/xdiffi.c     | 101 ++++++++---------\n xdiff/xdiffi.h     |  11 +-\n xdiff/xemit.c      |  38 +++----\n xdiff/xhistogram.c |  10 +-\n xdiff/xmerge.c     |  56 +++++-----\n xdiff/xpatience.c  |  18 ++--\n xdiff/xprepare.c   | 263 +++++++++++++++++----------------------------\n xdiff/xtypes.h     |   9 +-\n xdiff/xutils.c     |  16 +--\n 9 files changed, 212 insertions(+), 310 deletions(-)\n\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2048%2Fezekielnewren%2Fuse_rust_types_in_xdiff-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2048/ezekielnewren/use_rust_types_in_xdiff-v4\nPull-Request: https://github.com/git/git/pull/2048\n\nRange-diff vs v3:\n\n  1:  784cffcef5 !  1:  79d1099656 xdiff: delete static forward declarations in xprepare\n     @@ xdiff/xprepare.c: static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xd\n      +\t\txdl_free_ctx(&xe->xdf1);\n      +\t\txdl_free_classifier(&cf);\n      +\t\treturn -1;\n     -+\t    }\n     ++\t}\n      +\n      +\txdl_free_classifier(&cf);\n      +\n  2:  b79157e64f =  2:  9142f28fcd xdiff: delete local variables and initialize/free xdfile_t directly\n  3:  2e8de5be03 =  3:  13f00f5683 xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  -:  ---------- >  4:  311279c123 xdiff: delete superfluous function xdl_get_rec() in xemit\n  4:  ddfee67e06 !  5:  d84658ac83 xdiff: delete xdl_get_rec() in xemit\n     @@ Metadata\n      Author: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## Commit message ##\n     -    xdiff: delete xdl_get_rec() in xemit\n     +    xdiff: delete superfluous local variables that alias fields in xrecord_t\n      \n     -    This function aliases the fields of xrecord_t, which makes it harder\n     -    to track the usages of those fields. Delete it.\n     +    Use the type xrecord_t as the local variable for the functions in the\n     +    file xdiff/xemit.c.\n      \n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xemit.c ##\n      @@\n     - \n       #include \"xinclude.h\"\n       \n     --static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n     --\n     --\t*rec = xdf->recs[ri]->ptr;\n     --\n     --\treturn xdf->recs[ri]->size;\n     --}\n     --\n     --\n     + \n      -static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n      -\tlong size, psize = strlen(pre);\n     --\tchar const *rec;\n     --\n     --\tsize = xdl_get_rec(xdf, ri, &rec);\n     --\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n     +-\tchar const *rec = xdf->recs[ri]->ptr;\n      +static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n      +{\n      +\txrecord_t *rec = xdf->recs[ri];\n       \n     -+\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n     +-\tsize = xdf->recs[ri]->size;\n     +-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0)\n     ++\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n       \t\treturn -1;\n     - \t}\n       \n     + \treturn 0;\n      @@ xdiff/xemit.c: static long def_ff(const char *rec, long len, char *buf, long sz)\n       static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n       \t\t\t   char *buf, long sz)\n       {\n     --\tconst char *rec;\n     --\tlong len = xdl_get_rec(xdf, ri, &rec);\n     +-\tconst char *rec = xdf->recs[ri]->ptr;\n     +-\tlong len = xdf->recs[ri]->size;\n      +\txrecord_t *rec = xdf->recs[ri];\n      +\n       \tif (!xecfg->find_func)\n     @@ xdiff/xemit.c: static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg\n       \n       static int is_empty_rec(xdfile_t *xdf, long ri)\n       {\n     --\tconst char *rec;\n     --\tlong len = xdl_get_rec(xdf, ri, &rec);\n     +-\tconst char *rec = xdf->recs[ri]->ptr;\n     +-\tlong len = xdf->recs[ri]->size;\n      +\txrecord_t *rec = xdf->recs[ri];\n      +\tlong i = 0;\n       \n  5:  807ce3e5aa =  6:  bf16453846 xdiff: delete struct diffdata_t\n  6:  0bacb1191d !  7:  4ef7f243e9 xdiff: delete redundant array xdfile_t.ha\n     @@ Commit message\n      \n          This makes the code about 5% slower. The fields rindex and ha are\n          specific to the classic diff (myers and minimal). I plan on creating a\n     -    struct for classic diff, but there'a alot of cleanup that needs to be\n     +    struct for classic diff, but there's a lot of cleanup that needs to be\n          done before that can happen and leaving ha in would make those cleanups\n          harder to follow.\n      \n  7:  e1e94107c9 !  8:  3b6c2127c4 xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t\n     @@ Commit message\n          xdlclass_t.size -> xrecord_t.size\n          xdlclass_t.ha   -> xrecord_t.ha\n      \n     -    Remove aliasing from xdlclass_t, to reduce future refactoring mistakes.\n     +    xdlclass_t carries a copy of the data in xrecord_t, but instead of\n     +    embedding xrecord_t it duplicates the individual fields. A future\n     +    commit will change the types used in xrecord_t so embed it in\n     +    xdlclass_t first, so we don't have to remember to change the types\n     +    here as well.\n      \n          Best-viewed-with: --color-words\n     +    Helped-by: Phillip Wood <phillip.wood123@gmail.com>\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xprepare.c ##\n     @@ xdiff/xprepare.c: static int xdl_classify_record(unsigned int pass, xdlclassifie\n      -\t\trcrec->line = line;\n      -\t\trcrec->size = rec->size;\n      -\t\trcrec->ha = rec->ha;\n     -+\t\trcrec->rec.ptr = rec->ptr;\n     -+\t\trcrec->rec.size = rec->size;\n     -+\t\trcrec->rec.ha = rec->ha;\n     ++\t\trcrec->rec = *rec;\n       \t\trcrec->len1 = rcrec->len2 = 0;\n       \t\trcrec->next = cf->rchash[hi];\n       \t\tcf->rchash[hi] = rcrec;\n  8:  fae26d2a04 !  9:  f7b5021e48 xdiff: delete chastore from xdfile_t\n     @@ xdiff/xemit.c\n      -\txrecord_t *rec = xdf->recs[ri];\n      +\txrecord_t *rec = &xdf->recs[ri];\n       \n     - \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) {\n     + \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n       \t\treturn -1;\n      @@ xdiff/xemit.c: static long def_ff(const char *rec, long len, char *buf, long sz)\n       static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n  9:  fd54135560 = 10:  97135495e2 xdiff: delete rchg aliasing\n 10:  1e404c3290 ! 11:  b544c15a67 xdiff: treat xdfile_t.rchg like an enum\n     @@ Metadata\n      Author: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## Commit message ##\n     -    xdiff: treat xdfile_t.rchg like an enum\n     +    xdiff: use bool literals for xdfile_t.rchg\n      \n     -    Define macros NO(0), YES(1), MAYBE(2) as the enum values for rchg to\n     -    make the code easier to follow. Perhaps 'rchg' should be renamed to\n     -    'changed'?\n     -\n     -    A few of the code changes might appear to change behavior, such as:\n     -        -    while (xdf->rchg[g->start - 1])\n     -        +    while (xdf->rchg[g->start - 1] == YES)\n     -    because it appears the value of MAYBE is being ignored.  However, MAYBE\n     -    is only ever assigned as a value to a temporary array (dis1 & dis2) and\n     -    then as a last step use that temporary array to decide if it wants to\n     -    change xdfile_t.rchg[i] to YES or leave it as NO.  As such, rchg will\n     -    never have a value of MAYBE and thus there is no behavioral change.\n     +    Define macros NO(0), YES(1), MAYBE(2) as the enum values for dis1 and\n     +    dis2 to make the code easier to follow.\n      \n          Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n     - ## xdiff/xdiff.h ##\n     -@@\n     - extern \"C\" {\n     - #endif /* #ifdef __cplusplus */\n     - \n     -+#define NO 0\n     -+#define YES 1\n     -+#define MAYBE 2\n     -+\n     - /* xpparm_t.flags */\n     - #define XDF_NEED_MINIMAL (1 << 0)\n     - \n     -\n       ## xdiff/xdiffi.c ##\n      @@ xdiff/xdiffi.c: int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n       \t */\n       \tif (off1 == lim1) {\n       \t\tfor (; off2 < lim2; off2++)\n      -\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n     -+\t\t\txdf2->rchg[xdf2->rindex[off2]] = YES;\n     ++\t\t\txdf2->rchg[xdf2->rindex[off2]] = true;\n       \t} else if (off2 == lim2) {\n       \t\tfor (; off1 < lim1; off1++)\n      -\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n     -+\t\t\txdf1->rchg[xdf1->rindex[off1]] = YES;\n     ++\t\t\txdf1->rchg[xdf1->rindex[off1]] = true;\n       \t} else {\n       \t\txdpsplit_t spl;\n       \t\tspl.i1 = spl.i2 = 0;\n     -@@ xdiff/xdiffi.c: struct xdlgroup {\n     - static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n     - {\n     - \tg->start = g->end = 0;\n     --\twhile (xdf->rchg[g->end])\n     -+\twhile (xdf->rchg[g->end] == YES)\n     - \t\tg->end++;\n     - }\n     - \n     -@@ xdiff/xdiffi.c: static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n     - \t\treturn -1;\n     - \n     - \tg->start = g->end + 1;\n     --\tfor (g->end = g->start; xdf->rchg[g->end]; g->end++)\n     -+\tfor (g->end = g->start; xdf->rchg[g->end] == YES; g->end++)\n     - \t\t;\n     - \n     - \treturn 0;\n     -@@ xdiff/xdiffi.c: static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n     - \t\treturn -1;\n     - \n     - \tg->end = g->start - 1;\n     --\tfor (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n     -+\tfor (g->start = g->end; xdf->rchg[g->start - 1] == YES; g->start--)\n     - \t\t;\n     - \n     - \treturn 0;\n      @@ xdiff/xdiffi.c: static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n       {\n       \tif (g->end < xdf->nrec &&\n       \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n      -\t\txdf->rchg[g->start++] = 0;\n      -\t\txdf->rchg[g->end++] = 1;\n     -+\t\txdf->rchg[g->start++] = NO;\n     -+\t\txdf->rchg[g->end++] = YES;\n     ++\t\txdf->rchg[g->start++] = false;\n     ++\t\txdf->rchg[g->end++] = true;\n       \n     --\t\twhile (xdf->rchg[g->end])\n     -+\t\twhile (xdf->rchg[g->end] == YES)\n     + \t\twhile (xdf->rchg[g->end])\n       \t\t\tg->end++;\n     - \n     - \t\treturn 0;\n      @@ xdiff/xdiffi.c: static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n       {\n       \tif (g->start > 0 &&\n       \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n      -\t\txdf->rchg[--g->start] = 1;\n      -\t\txdf->rchg[--g->end] = 0;\n     -+\t\txdf->rchg[--g->start] = YES;\n     -+\t\txdf->rchg[--g->end] = NO;\n     ++\t\txdf->rchg[--g->start] = true;\n     ++\t\txdf->rchg[--g->end] = false;\n       \n     --\t\twhile (xdf->rchg[g->start - 1])\n     -+\t\twhile (xdf->rchg[g->start - 1] == YES)\n     + \t\twhile (xdf->rchg[g->start - 1])\n       \t\t\tg->start--;\n     - \n     - \t\treturn 0;\n      \n       ## xdiff/xhistogram.c ##\n      @@ xdiff/xhistogram.c: redo:\n     @@ xdiff/xhistogram.c: redo:\n       \tif (!count1) {\n       \t\twhile(count2--)\n      -\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     -+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n     ++\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n       \t\treturn 0;\n       \t} else if (!count2) {\n       \t\twhile(count1--)\n      -\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     -+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n     ++\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n       \t\treturn 0;\n       \t}\n       \n     @@ xdiff/xhistogram.c: redo:\n       \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n       \t\t\twhile (count1--)\n      -\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     -+\t\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n     ++\t\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n       \t\t\twhile (count2--)\n      -\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     -+\t\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n     ++\t\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n       \t\t\tresult = 0;\n       \t\t} else {\n       \t\t\tresult = histogram_diff(xpp, env,\n     @@ xdiff/xpatience.c: static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n       \tif (!count1) {\n       \t\twhile(count2--)\n      -\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     -+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n     ++\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n       \t\treturn 0;\n       \t} else if (!count2) {\n       \t\twhile(count1--)\n      -\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     -+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n     ++\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n       \t\treturn 0;\n       \t}\n       \n     @@ xdiff/xpatience.c: static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n       \tif (!map.has_matches) {\n       \t\twhile(count1--)\n      -\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     -+\t\t\tenv->xdf1.rchg[line1++ - 1] = YES;\n     ++\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n       \t\twhile(count2--)\n      -\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     -+\t\t\tenv->xdf2.rchg[line2++ - 1] = YES;\n     ++\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n       \t\txdl_free(map.entries);\n       \t\treturn 0;\n       \t}\n      \n       ## xdiff/xprepare.c ##\n     +@@\n     + #define XDL_GUESS_NLINES1 256\n     + #define XDL_GUESS_NLINES2 20\n     + \n     ++#define NO 0\n     ++#define YES 1\n     ++#define MAYBE 2\n     + \n     + typedef struct s_xdlclass {\n     + \tstruct s_xdlclass *next;\n      @@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n       \t * current line (i) is already a multimatch line.\n       \t */\n     @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n       \t\t\txdf1->rindex[nreff++] = i;\n       \t\t} else\n      -\t\t\txdf1->rchg[i] = 1;\n     -+\t\t\txdf1->rchg[i] = YES;\n     ++\t\t\txdf1->rchg[i] = true;\n       \t}\n       \txdf1->nreff = nreff;\n       \n     @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n       \t\t\txdf2->rindex[nreff++] = i;\n       \t\t} else\n      -\t\t\txdf2->rchg[i] = 1;\n     -+\t\t\txdf2->rchg[i] = YES;\n     ++\t\t\txdf2->rchg[i] = true;\n       \t}\n       \txdf2->nreff = nreff;\n       \n  -:  ---------- > 12:  034a4a7b2a xdiff: refactor 'char *rchg' to 'bool *changed' in xdfile_t\n\n-- \ngitgitgadget\n"},{"id":"526968","messageId":"79d1099656f5b86831e8b4b784daa42368f5a2b1.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 01/12] xdiff: delete static forward declarations in xprepare","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:30Z","receivedAt":"2025-09-22T19:51:47Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nMove xdl_prepare_env() later in the file to avoid the need\nfor static forward declarations.\n\nBest-viewed-with: --color-moved\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 116 ++++++++++++++++++++---------------------------\n 1 file changed, 50 insertions(+), 66 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e1d4017b2d..249bfa678f 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -53,21 +53,6 @@ typedef struct s_xdlclassifier {\n \n \n \n-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);\n-static void xdl_free_classifier(xdlclassifier_t *cf);\n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec);\n-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n-\t\t\t   xdlclassifier_t *cf, xdfile_t *xdf);\n-static void xdl_free_ctx(xdfile_t *xdf);\n-static int xdl_clean_mmatch(char const *dis, long i, long s, long e);\n-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-\n-\n-\n-\n static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {\n \tcf->flags = flags;\n \n@@ -242,57 +227,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {\n }\n \n \n-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n-\t\t    xdfenv_t *xe) {\n-\tlong enl1, enl2, sample;\n-\txdlclassifier_t cf;\n-\n-\tmemset(&cf, 0, sizeof(cf));\n-\n-\t/*\n-\t * For histogram diff, we can afford a smaller sample size and\n-\t * thus a poorer estimate of the number of lines, as the hash\n-\t * table (rhash) won't be filled up/grown. The number of lines\n-\t * (nrecs) will be updated correctly anyway by\n-\t * xdl_prepare_ctx().\n-\t */\n-\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n-\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n-\n-\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n-\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n-\n-\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n-\t\treturn -1;\n-\n-\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n-\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n-\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n-\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf2);\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\txdl_free_classifier(&cf);\n-\n-\treturn 0;\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n@@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2\n \n \treturn 0;\n }\n+\n+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n+\t\t    xdfenv_t *xe) {\n+\tlong enl1, enl2, sample;\n+\txdlclassifier_t cf;\n+\n+\tmemset(&cf, 0, sizeof(cf));\n+\n+\t/*\n+\t * For histogram diff, we can afford a smaller sample size and\n+\t * thus a poorer estimate of the number of lines, as the hash\n+\t * table (rhash) won't be filled up/grown. The number of lines\n+\t * (nrecs) will be updated correctly anyway by\n+\t * xdl_prepare_ctx().\n+\t */\n+\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n+\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n+\n+\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n+\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n+\n+\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n+\t\treturn -1;\n+\n+\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n+\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n+\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n+\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf2);\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\txdl_free_classifier(&cf);\n+\n+\treturn 0;\n+}\n-- \ngitgitgadget\n\n"},{"id":"526969","messageId":"9142f28fcdee62b323088d6d34cfc15fd4c58010.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 02/12] xdiff: delete local variables and initialize/free xdfile_t directly","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:31Z","receivedAt":"2025-09-22T19:51:49Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThese local variables are essentially a hand-rolled additional\nimplementation of xdl_free_ctx() inlined into xdl_prepare_ctx(). Modify\nthe code to use the existing xdl_free_ctx() function so there aren't\ntwo ways to free such variables.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 78 +++++++++++++++++++-----------------------------\n 1 file changed, 30 insertions(+), 48 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 249bfa678f..96134c9fbf 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -134,99 +134,81 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n }\n \n \n+static void xdl_free_ctx(xdfile_t *xdf)\n+{\n+\txdl_free(xdf->rhash);\n+\txdl_free(xdf->rindex);\n+\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->ha);\n+\txdl_free(xdf->recs);\n+\txdl_cha_free(&xdf->rcha);\n+}\n+\n+\n static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n \t\t\t   xdlclassifier_t *cf, xdfile_t *xdf) {\n-\tunsigned int hbits;\n-\tlong nrec, hsize, bsize;\n+\tlong bsize;\n \tunsigned long hav;\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n-\txrecord_t **recs;\n-\txrecord_t **rhash;\n-\tunsigned long *ha;\n-\tchar *rchg;\n-\tlong *rindex;\n \n-\tha = NULL;\n-\trindex = NULL;\n-\trchg = NULL;\n-\trhash = NULL;\n-\trecs = NULL;\n+\txdf->ha = NULL;\n+\txdf->rindex = NULL;\n+\txdf->rchg = NULL;\n+\txdf->rhash = NULL;\n+\txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n \t\tgoto abort;\n-\tif (!XDL_ALLOC_ARRAY(recs, narec))\n+\tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\thbits = xdl_hashbits((unsigned int) narec);\n-\thsize = 1 << hbits;\n-\tif (!XDL_CALLOC_ARRAY(rhash, hsize))\n+\txdf->hbits = xdl_hashbits((unsigned int) narec);\n+\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n \t\tgoto abort;\n \n-\tnrec = 0;\n+\txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n \t\t\tprev = cur;\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n-\t\t\tif (XDL_ALLOC_GROW(recs, nrec + 1, narec))\n+\t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n \t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n \t\t\t\tgoto abort;\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\trecs[nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)\n+\t\t\txdf->recs[xdf->nrec++] = crec;\n+\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(rchg, nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n-\t\tif (!XDL_ALLOC_ARRAY(rindex, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(ha, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n \t\t\tgoto abort;\n \t}\n \n-\txdf->nrec = nrec;\n-\txdf->recs = recs;\n-\txdf->hbits = hbits;\n-\txdf->rhash = rhash;\n-\txdf->rchg = rchg + 1;\n-\txdf->rindex = rindex;\n+\txdf->rchg += 1;\n \txdf->nreff = 0;\n-\txdf->ha = ha;\n \txdf->dstart = 0;\n-\txdf->dend = nrec - 1;\n+\txdf->dend = xdf->nrec - 1;\n \n \treturn 0;\n \n abort:\n-\txdl_free(ha);\n-\txdl_free(rindex);\n-\txdl_free(rchg);\n-\txdl_free(rhash);\n-\txdl_free(recs);\n-\txdl_cha_free(&xdf->rcha);\n+\txdl_free_ctx(xdf);\n \treturn -1;\n }\n \n \n-static void xdl_free_ctx(xdfile_t *xdf) {\n-\n-\txdl_free(xdf->rhash);\n-\txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n-\txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n-- \ngitgitgadget\n\n"},{"id":"526970","messageId":"13f00f5683353a649a5d816474b2cc051cb8cbca.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 03/12] xdiff: delete unnecessary fields from xrecord_t and xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:32Z","receivedAt":"2025-09-22T19:51:50Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,\nbut never used for anything by the code. Remove them.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 15 ++-------------\n xdiff/xtypes.h   |  3 ---\n 2 files changed, 2 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 96134c9fbf..3576415c85 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -91,8 +91,7 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n }\n \n \n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec) {\n+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n \tchar const *line;\n \txdlclass_t *rcrec;\n@@ -126,17 +125,12 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \n \trec->ha = (unsigned long) rcrec->idx;\n \n-\thi = (long) XDL_HASHLONG(rec->ha, hbits);\n-\trec->next = rhash[hi];\n-\trhash[hi] = rec;\n-\n \treturn 0;\n }\n \n \n static void xdl_free_ctx(xdfile_t *xdf)\n {\n-\txdl_free(xdf->rhash);\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->ha);\n@@ -155,7 +149,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n-\txdf->rhash = NULL;\n \txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n@@ -163,10 +156,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\txdf->hbits = xdl_hashbits((unsigned int) narec);\n-\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n-\t\tgoto abort;\n-\n \txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n@@ -180,7 +169,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n \t\t\txdf->recs[xdf->nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n+\t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8442bd436e..8b8467360e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -39,7 +39,6 @@ typedef struct s_chastore {\n } chastore_t;\n \n typedef struct s_xrecord {\n-\tstruct s_xrecord *next;\n \tchar const *ptr;\n \tlong size;\n \tunsigned long ha;\n@@ -48,8 +47,6 @@ typedef struct s_xrecord {\n typedef struct s_xdfile {\n \tchastore_t rcha;\n \tlong nrec;\n-\tunsigned int hbits;\n-\txrecord_t **rhash;\n \tlong dstart, dend;\n \txrecord_t **recs;\n \tchar *rchg;\n-- \ngitgitgadget\n\n"},{"id":"526971","messageId":"311279c123da718a1462dc0a8695f0e06e7b0330.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 04/12] xdiff: delete superfluous function xdl_get_rec() in xemit","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:33Z","receivedAt":"2025-09-22T19:51:51Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen xrecord_t was a linked list, and recs didn't exist, I assume this\nfunction walked the list until it found the right record. Accessing\na contiguous array is so trival that this function is now superfluous.\nDelete it.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 23 +++++++----------------\n 1 file changed, 7 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 1d40c9cb40..40fc8154f3 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -22,23 +22,14 @@\n \n #include \"xinclude.h\"\n \n-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n-\n-\t*rec = xdf->recs[ri]->ptr;\n-\n-\treturn xdf->recs[ri]->size;\n-}\n-\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n \tlong size, psize = strlen(pre);\n-\tchar const *rec;\n-\n-\tsize = xdl_get_rec(xdf, ri, &rec);\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n+\tchar const *rec = xdf->recs[ri]->ptr;\n \n+\tsize = xdf->recs[ri]->size;\n+\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0)\n \t\treturn -1;\n-\t}\n \n \treturn 0;\n }\n@@ -120,8 +111,8 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\tconst char *rec = xdf->recs[ri]->ptr;\n+\tlong len = xdf->recs[ri]->size;\n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec, len, buf, sz);\n \treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n@@ -160,8 +151,8 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\tconst char *rec = xdf->recs[ri]->ptr;\n+\tlong len = xdf->recs[ri]->size;\n \n \twhile (len > 0 && XDL_ISSPACE(*rec)) {\n \t\trec++;\n-- \ngitgitgadget\n\n"},{"id":"526972","messageId":"d84658ac83e529ecc6e72a54bd3831dfe1b46f89.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 05/12] xdiff: delete superfluous local variables that alias fields in xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:34Z","receivedAt":"2025-09-22T19:51:53Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nUse the type xrecord_t as the local variable for the functions in the\nfile xdiff/xemit.c.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 29 +++++++++++++----------------\n 1 file changed, 13 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 40fc8154f3..2161ac3cd0 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -23,12 +23,11 @@\n #include \"xinclude.h\"\n \n \n-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n-\tlong size, psize = strlen(pre);\n-\tchar const *rec = xdf->recs[ri]->ptr;\n+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n+{\n+\txrecord_t *rec = xdf->recs[ri];\n \n-\tsize = xdf->recs[ri]->size;\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0)\n+\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n \n \treturn 0;\n@@ -111,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec = xdf->recs[ri]->ptr;\n-\tlong len = xdf->recs[ri]->size;\n+\txrecord_t *rec = xdf->recs[ri];\n+\n \tif (!xecfg->find_func)\n-\t\treturn def_ff(rec, len, buf, sz);\n-\treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n+\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n+\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n }\n \n static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n@@ -151,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec = xdf->recs[ri]->ptr;\n-\tlong len = xdf->recs[ri]->size;\n+\txrecord_t *rec = xdf->recs[ri];\n+\tlong i = 0;\n \n-\twhile (len > 0 && XDL_ISSPACE(*rec)) {\n-\t\trec++;\n-\t\tlen--;\n-\t}\n-\treturn !len;\n+\tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n+\n+\treturn i == rec->size;\n }\n \n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n-- \ngitgitgadget\n\n"},{"id":"526973","messageId":"bf16453846575c7a56e6543d741065f98cd04395.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 06/12] xdiff: delete struct diffdata_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:35Z","receivedAt":"2025-09-22T19:51:54Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nEvery field in this struct is an alias for a certain field in xdfile_t.\n\ndiffdata_t.nrec   -> xdfile_t.nreff\ndiffdata_t.ha     -> xdfile_t.ha\ndiffdata_t.rindex -> xdfile_t.rindex\ndiffdata_t.rchg   -> xdfile_t.rchg\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 32 ++++++++------------------------\n xdiff/xdiffi.h | 11 ++---------\n 2 files changed, 10 insertions(+), 33 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 5a96e36dfb..bbf0161f84 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -257,10 +257,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n  * sub-boxes by calling the box splitting function. Note that the real job\n  * (marking changed lines) is done in the two boundary reaching checks.\n  */\n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;\n+\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n@@ -273,17 +273,11 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t * be obviously changed.\n \t */\n \tif (off1 == lim1) {\n-\t\tchar *rchg2 = dd2->rchg;\n-\t\tlong *rindex2 = dd2->rindex;\n-\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\trchg2[rindex2[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n \t} else if (off2 == lim2) {\n-\t\tchar *rchg1 = dd1->rchg;\n-\t\tlong *rindex1 = dd1->rindex;\n-\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\trchg1[rindex1[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -300,9 +294,9 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t\t/*\n \t\t * ... et Impera.\n \t\t */\n-\t\tif (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,\n+\t\tif (xdl_recs_cmp(xdf1, off1, spl.i1, xdf2, off2, spl.i2,\n \t\t\t\t kvdf, kvdb, spl.min_lo, xenv) < 0 ||\n-\t\t    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,\n+\t\t    xdl_recs_cmp(xdf1, spl.i1, lim1, xdf2, spl.i2, lim2,\n \t\t\t\t kvdf, kvdb, spl.min_hi, xenv) < 0) {\n \n \t\t\treturn -1;\n@@ -318,7 +312,6 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \tlong ndiags;\n \tlong *kvd, *kvdf, *kvdb;\n \txdalgoenv_t xenv;\n-\tdiffdata_t dd1, dd2;\n \tint res;\n \n \tif (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)\n@@ -357,16 +350,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \txenv.snake_cnt = XDL_SNAKE_CNT;\n \txenv.heur_min = XDL_HEUR_MIN_COST;\n \n-\tdd1.nrec = xe->xdf1.nreff;\n-\tdd1.ha = xe->xdf1.ha;\n-\tdd1.rchg = xe->xdf1.rchg;\n-\tdd1.rindex = xe->xdf1.rindex;\n-\tdd2.nrec = xe->xdf2.nreff;\n-\tdd2.ha = xe->xdf2.ha;\n-\tdd2.rchg = xe->xdf2.rchg;\n-\tdd2.rindex = xe->xdf2.rindex;\n-\n-\tres = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,\n+\tres = xdl_recs_cmp(&xe->xdf1, 0, xe->xdf1.nreff, &xe->xdf2, 0, xe->xdf2.nreff,\n \t\t\t   kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,\n \t\t\t   &xenv);\n \txdl_free(kvd);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 126c9d8ff4..49e52c67f9 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -24,13 +24,6 @@\n #define XDIFFI_H\n \n \n-typedef struct s_diffdata {\n-\tlong nrec;\n-\tunsigned long const *ha;\n-\tlong *rindex;\n-\tchar *rchg;\n-} diffdata_t;\n-\n typedef struct s_xdalgoenv {\n \tlong mxcost;\n \tlong snake_cnt;\n@@ -46,8 +39,8 @@ typedef struct s_xdchange {\n \n \n \n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);\n int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\txdfenv_t *xe);\n-- \ngitgitgadget\n\n"},{"id":"526974","messageId":"4ef7f243e9aca64f2deef0b5d578dc6c2788e4a8.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 07/12] xdiff: delete redundant array xdfile_t.ha","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:36Z","receivedAt":"2025-09-22T19:51:56Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen 0 <= i < xdfile_t.nreff the following is true:\nxdfile_t.ha[i] == xdfile_t.recs[xdfile_t.rindex[i]]\n\nThis makes the code about 5% slower. The fields rindex and ha are\nspecific to the classic diff (myers and minimal). I plan on creating a\nstruct for classic diff, but there's a lot of cleanup that needs to be\ndone before that can happen and leaving ha in would make those cleanups\nharder to follow.\n\nA subsequent commit will delete the chastore cha from xdfile_t. That\nlater commit will investigate deleting ha and cha independently and\ntogether.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c   | 24 ++++++++++++++----------\n xdiff/xprepare.c | 12 ++----------\n xdiff/xtypes.h   |  1 -\n 3 files changed, 16 insertions(+), 21 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex bbf0161f84..11cd090b53 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -22,6 +22,11 @@\n \n #include \"xinclude.h\"\n \n+static unsigned long get_hash(xdfile_t *xdf, long index)\n+{\n+\treturn xdf->recs[xdf->rindex[index]]->ha;\n+}\n+\n #define XDL_MAX_COST_MIN 256\n #define XDL_HEUR_MIN_COST 256\n #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)\n@@ -42,8 +47,8 @@ typedef struct s_xdpsplit {\n  * using this algorithm, so a little bit of heuristic is needed to cut the\n  * search and to return a suboptimal point.\n  */\n-static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n-\t\t      unsigned long const *ha2, long off2, long lim2,\n+static long xdl_split(xdfile_t *xdf1, long off1, long lim1,\n+\t\t      xdfile_t *xdf2, long off2, long lim2,\n \t\t      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,\n \t\t      xdalgoenv_t *xenv) {\n \tlong dmin = off1 - lim2, dmax = lim1 - off2;\n@@ -87,7 +92,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdf[d + 1];\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);\n+\t\t\tfor (; i1 < lim1 && i2 < lim2 && get_hash(xdf1, i1) == get_hash(xdf2, i2); i1++, i2++);\n \t\t\tif (i1 - prev1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdf[d] = i1;\n@@ -124,7 +129,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdb[d + 1] - 1;\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);\n+\t\t\tfor (; i1 > off1 && i2 > off2 && get_hash(xdf1, i1 - 1) == get_hash(xdf2, i2 - 1); i1--, i2--);\n \t\t\tif (prev1 - i1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdb[d] = i1;\n@@ -159,7 +164,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&\n \t\t\t\t    off2 + xenv->snake_cnt <= i2 && i2 < lim2) {\n-\t\t\t\t\tfor (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)\n+\t\t\t\t\tfor (k = 1; get_hash(xdf1, i1 - k) == get_hash(xdf2, i2 - k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -183,7 +188,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&\n \t\t\t\t    off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {\n-\t\t\t\t\tfor (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)\n+\t\t\t\t\tfor (k = 0; get_hash(xdf1, i1 + k) == get_hash(xdf2, i2 + k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt - 1) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -260,13 +265,12 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n \t */\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, off1) == get_hash(xdf2, off2); off1++, off2++);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, lim1 - 1) == get_hash(xdf2, lim2 - 1); lim1--, lim2--);\n \n \t/*\n \t * If one dimension is empty, then all records on the other one must\n@@ -285,7 +289,7 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t/*\n \t\t * Divide ...\n \t\t */\n-\t\tif (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,\n+\t\tif (xdl_split(xdf1, off1, lim1, xdf2, off2, lim2, kvdf, kvdb,\n \t\t\t      need_min, &spl, xenv) < 0) {\n \n \t\t\treturn -1;\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 3576415c85..22c44f0683 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -133,7 +133,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n {\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n \txdl_free(xdf->recs);\n \txdl_cha_free(&xdf->rcha);\n }\n@@ -146,7 +145,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n \n-\txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n@@ -181,8 +179,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n \t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n-\t\t\tgoto abort;\n \t}\n \n \txdf->rchg += 1;\n@@ -300,9 +296,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (dis1[i] == 1 ||\n \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n-\t\t\txdf1->rindex[nreff] = i;\n-\t\t\txdf1->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf1->rchg[i] = 1;\n \t}\n@@ -312,9 +306,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf2->dend; i++, recs++) {\n \t\tif (dis2[i] == 1 ||\n \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n-\t\t\txdf2->rindex[nreff] = i;\n-\t\t\txdf2->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf2->rchg[i] = 1;\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8b8467360e..85848f1685 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -52,7 +52,6 @@ typedef struct s_xdfile {\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\n-\tunsigned long *ha;\n } xdfile_t;\n \n typedef struct s_xdfenv {\n-- \ngitgitgadget\n\n"},{"id":"526975","messageId":"3b6c2127c47f583d3c3b16e961043211349edf3c.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 08/12] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:37Z","receivedAt":"2025-09-22T19:51:57Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe fields from xdlclass_t are aliases of xrecord_t:\nxdlclass_t.line -> xrecord_t.ptr\nxdlclass_t.size -> xrecord_t.size\nxdlclass_t.ha   -> xrecord_t.ha\n\nxdlclass_t carries a copy of the data in xrecord_t, but instead of\nembedding xrecord_t it duplicates the individual fields. A future\ncommit will change the types used in xrecord_t so embed it in\nxdlclass_t first, so we don't have to remember to change the types\nhere as well.\n\nBest-viewed-with: --color-words\nHelped-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 14 ++++----------\n 1 file changed, 4 insertions(+), 10 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 22c44f0683..e6e2c0e1c0 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -32,9 +32,7 @@\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n-\tunsigned long ha;\n-\tchar const *line;\n-\tlong size;\n+\txrecord_t rec;\n \tlong idx;\n \tlong len1, len2;\n } xdlclass_t;\n@@ -93,14 +91,12 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n \n static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n-\tchar const *line;\n \txdlclass_t *rcrec;\n \n-\tline = rec->ptr;\n \thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n-\t\tif (rcrec->ha == rec->ha &&\n-\t\t\t\txdl_recmatch(rcrec->line, rcrec->size,\n+\t\tif (rcrec->rec.ha == rec->ha &&\n+\t\t\t\txdl_recmatch(rcrec->rec.ptr, rcrec->rec.size,\n \t\t\t\t\trec->ptr, rec->size, cf->flags))\n \t\t\tbreak;\n \n@@ -113,9 +109,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \t\tif (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))\n \t\t\t\treturn -1;\n \t\tcf->rcrecs[rcrec->idx] = rcrec;\n-\t\trcrec->line = line;\n-\t\trcrec->size = rec->size;\n-\t\trcrec->ha = rec->ha;\n+\t\trcrec->rec = *rec;\n \t\trcrec->len1 = rcrec->len2 = 0;\n \t\trcrec->next = cf->rchash[hi];\n \t\tcf->rchash[hi] = rcrec;\n-- \ngitgitgadget\n\n"},{"id":"526976","messageId":"f7b5021e48855ddb663cd444860d88c83b41d790.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 09/12] xdiff: delete chastore from xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:38Z","receivedAt":"2025-09-22T19:52:01Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxdfile_t currently uses chastore_t which is an arena allocator. I\nthink that xrecord_t used to be a linked list and recs didn't exist\noriginally. When recs was added I think they forgot to remove\nxdfile_t.next, but was overlooked. This dual data structure setup\nmakes the code somewhat confusing.\n\nAdditionally the C type chastore_t isn't FFI friendly, and provides\nlittle to no performance benefit over using realloc to grow an array.\n\nPerformance impact of deleting fields from xdfile_t:\nDeleting ha is about 5% slower.\nDeleting cha is about 5% faster.\n\nDelete ha, but keep cha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.269 s ±  0.017 s    [User: 1.135 s, System: 0.128 s]\n    Range (min … max):    1.249 s …  1.286 s    10 runs\n\n  Benchmark 2: build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.339 s ±  0.017 s    [User: 1.234 s, System: 0.099 s]\n    Range (min … max):    1.320 s …  1.358 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.06 ± 0.02 times faster than build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete cha, but keep ha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.290 s ±  0.001 s    [User: 1.154 s, System: 0.130 s]\n    Range (min … max):    1.288 s …  1.292 s    10 runs\n\n  Benchmark 2: build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.232 s ±  0.017 s    [User: 1.105 s, System: 0.121 s]\n    Range (min … max):    1.205 s …  1.249 s    10 runs\n\n  Summary\n    build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.05 ± 0.01 times faster than build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete ha AND chastore\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha_and_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.291 s ±  0.002 s    [User: 1.156 s, System: 0.129 s]\n    Range (min … max):    1.287 s …  1.295 s    10 runs\n\n  Benchmark 2: build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.306 s ±  0.001 s    [User: 1.195 s, System: 0.105 s]\n    Range (min … max):    1.305 s …  1.308 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.01 ± 0.00 times faster than build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 24 ++++++++++----------\n xdiff/xemit.c      |  6 ++---\n xdiff/xhistogram.c |  2 +-\n xdiff/xmerge.c     | 56 +++++++++++++++++++++++-----------------------\n xdiff/xpatience.c  | 10 ++++-----\n xdiff/xprepare.c   | 19 ++++++----------\n xdiff/xtypes.h     |  3 +--\n xdiff/xutils.c     | 12 +++++-----\n 8 files changed, 63 insertions(+), 69 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 11cd090b53..a66125d44a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -24,7 +24,7 @@\n \n static unsigned long get_hash(xdfile_t *xdf, long index)\n {\n-\treturn xdf->recs[xdf->rindex[index]]->ha;\n+\treturn xdf->recs[xdf->rindex[index]].ha;\n }\n \n #define XDL_MAX_COST_MIN 256\n@@ -489,13 +489,13 @@ static void measure_split(const xdfile_t *xdf, long split,\n \t\tm->indent = -1;\n \t} else {\n \t\tm->end_of_file = 0;\n-\t\tm->indent = get_indent(xdf->recs[split]);\n+\t\tm->indent = get_indent(&xdf->recs[split]);\n \t}\n \n \tm->pre_blank = 0;\n \tm->pre_indent = -1;\n \tfor (i = split - 1; i >= 0; i--) {\n-\t\tm->pre_indent = get_indent(xdf->recs[i]);\n+\t\tm->pre_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->pre_indent != -1)\n \t\t\tbreak;\n \t\tm->pre_blank += 1;\n@@ -508,7 +508,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n \tm->post_blank = 0;\n \tm->post_indent = -1;\n \tfor (i = split + 1; i < xdf->nrec; i++) {\n-\t\tm->post_indent = get_indent(xdf->recs[i]);\n+\t\tm->post_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->post_indent != -1)\n \t\t\tbreak;\n \t\tm->post_blank += 1;\n@@ -752,7 +752,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n-\t    recs_match(xdf->recs[g->start], xdf->recs[g->end])) {\n+\t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n \t\txdf->rchg[g->start++] = 0;\n \t\txdf->rchg[g->end++] = 1;\n \n@@ -773,7 +773,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n-\t    recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {\n+\t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n \t\txdf->rchg[--g->start] = 1;\n \t\txdf->rchg[--g->end] = 0;\n \n@@ -988,16 +988,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n \t\tint ignore = 1;\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tlong i;\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\txch->ignore = ignore;\n \t}\n@@ -1021,7 +1021,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \txdchange_t *xch;\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tint ignore = 1;\n \t\tlong i;\n \n@@ -1033,11 +1033,11 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\txch->ignore = ignore;\n \t}\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 2161ac3cd0..b2f1f30cd3 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -25,7 +25,7 @@\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n@@ -110,7 +110,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n@@ -150,7 +150,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \tlong i = 0;\n \n \tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 040d81e0bc..4d857e8ae2 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -86,7 +86,7 @@ struct region {\n \t((LINE_MAP(index, ptr))->cnt)\n \n #define REC(env, s, l) \\\n-\t(env->xdf##s.recs[l - 1])\n+\t(&env->xdf##s.recs[l - 1])\n \n static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n {\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex af40c88a5b..fd600cbb5d 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \t\tint line_count, long flags)\n {\n \tint i;\n-\txrecord_t **rec1 = xe1->xdf2.recs + i1;\n-\txrecord_t **rec2 = xe2->xdf2.recs + i2;\n+\txrecord_t *rec1 = xe1->xdf2.recs + i1;\n+\txrecord_t *rec2 = xe2->xdf2.recs + i2;\n \n \tfor (i = 0; i < line_count; i++) {\n-\t\tint result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,\n-\t\t\trec2[i]->ptr, rec2[i]->size, flags);\n+\t\tint result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n+\t\t\trec2[i].ptr, rec2[i].size, flags);\n \t\tif (!result)\n \t\t\treturn -1;\n \t}\n@@ -111,7 +111,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \n static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)\n {\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \tint size = 0;\n \n \trecs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n@@ -119,12 +119,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n \tif (count < 1)\n \t\treturn 0;\n \n-\tfor (i = 0; i < count; size += recs[i++]->size)\n+\tfor (i = 0; i < count; size += recs[i++].size)\n \t\tif (dest)\n-\t\t\tmemcpy(dest + size, recs[i]->ptr, recs[i]->size);\n+\t\t\tmemcpy(dest + size, recs[i].ptr, recs[i].size);\n \tif (add_nl) {\n-\t\ti = recs[count - 1]->size;\n-\t\tif (i == 0 || recs[count - 1]->ptr[i - 1] != '\\n') {\n+\t\ti = recs[count - 1].size;\n+\t\tif (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n \t\t\tif (needs_cr) {\n \t\t\t\tif (dest)\n \t\t\t\t\tdest[size] = '\\r';\n@@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)\n \n \tif (i < file->nrec - 1)\n \t\t/* All lines before the last *must* end in LF */\n-\t\treturn (size = file->recs[i]->size) > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\treturn (size = file->recs[i].size) > 1 &&\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!file->nrec)\n \t\t/* Cannot determine eol style from empty file */\n \t\treturn -1;\n-\tif ((size = file->recs[i]->size) &&\n-\t\t\tfile->recs[i]->ptr[size - 1] == '\\n')\n+\tif ((size = file->recs[i].size) &&\n+\t\t\tfile->recs[i].ptr[size - 1] == '\\n')\n \t\t/* Last line; ends in LF; Is it CR/LF? */\n \t\treturn size > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!i)\n \t\t/* The only line has no eol */\n \t\treturn -1;\n \t/* Determine eol from second-to-last line */\n-\treturn (size = file->recs[i - 1]->size) > 1 &&\n-\t\tfile->recs[i - 1]->ptr[size - 2] == '\\r';\n+\treturn (size = file->recs[i - 1].size) > 1 &&\n+\t\tfile->recs[i - 1].ptr[size - 2] == '\\r';\n }\n \n static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)\n@@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\txpparam_t const *xpp)\n {\n-\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n+\txrecord_t *rec1 = xe1->xdf2.recs, *rec2 = xe2->xdf2.recs;\n \tfor (; m; m = m->next) {\n \t\t/* let's handle just the conflicts */\n \t\tif (m->mode)\n \t\t\tcontinue;\n \n \t\twhile(m->chg1 && m->chg2 &&\n-\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n+\t\t      recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t\tm->i1++;\n \t\t\tm->i2++;\n \t\t}\n \t\twhile (m->chg1 && m->chg2 &&\n-\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n-\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n+\t\t       recmatch(&rec1[m->i1 + m->chg1 - 1],\n+\t\t\t\t&rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t}\n@@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t * This probably does not work outside git, since\n \t\t * we have a very simple mmfile structure.\n \t\t */\n-\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;\n-\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr\n-\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;\n-\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;\n-\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr\n-\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;\n+\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n+\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n+\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n+\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n+\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n+\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n \t\tif (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n \t\t\treturn -1;\n \t\tif (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||\n@@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)\n static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n {\n \tfor (; chg; chg--, i++)\n-\t\tif (line_contains_alnum(xe->xdf2.recs[i]->ptr,\n-\t\t\t\txe->xdf2.recs[i]->size))\n+\t\tif (line_contains_alnum(xe->xdf2.recs[i].ptr,\n+\t\t\t\txe->xdf2.recs[i].size))\n \t\t\treturn 1;\n \treturn 0;\n }\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 77dc411d19..bf69a58527 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)\n static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\t\t  int pass)\n {\n-\txrecord_t **records = pass == 1 ?\n+\txrecord_t *records = pass == 1 ?\n \t\tmap->env->xdf1.recs : map->env->xdf2.recs;\n-\txrecord_t *record = records[line - 1];\n+\txrecord_t *record = &records[line - 1];\n \t/*\n \t * After xdl_prepare_env() (or more precisely, due to\n \t * xdl_classify_record()), the \"ha\" member of the records (AKA lines)\n@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\treturn;\n \tmap->entries[index].line1 = line;\n \tmap->entries[index].hash = record->ha;\n-\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);\n+\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n \tif (!map->first)\n \t\tmap->first = map->entries + index;\n \tif (map->last) {\n@@ -246,8 +246,8 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)\n \n static int match(struct hashmap *map, int line1, int line2)\n {\n-\txrecord_t *record1 = map->env->xdf1.recs[line1 - 1];\n-\txrecord_t *record2 = map->env->xdf2.recs[line2 - 1];\n+\txrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n+\txrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n \treturn record1->ha == record2->ha;\n }\n \ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e6e2c0e1c0..27c5a4d636 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -128,7 +128,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n }\n \n \n@@ -143,8 +142,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n \n-\tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n-\t\tgoto abort;\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n@@ -155,12 +152,10 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n \t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n-\t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n-\t\t\t\tgoto abort;\n+\t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\txdf->recs[xdf->nrec++] = crec;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n@@ -260,7 +255,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, nm, nreff, mlim;\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \txdlclass_t *rcrec;\n \tchar *dis, *dis1, *dis2;\n \tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n@@ -273,7 +268,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n \t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -281,7 +276,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n \t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -317,13 +312,13 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n  */\n static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, lim;\n-\txrecord_t **recs1, **recs2;\n+\txrecord_t *recs1, *recs2;\n \n \trecs1 = xdf1->recs;\n \trecs2 = xdf2->recs;\n \tfor (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n \t     i++, recs1++, recs2++)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dstart = xdf2->dstart = i;\n@@ -331,7 +326,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \trecs1 = xdf1->recs + xdf1->nrec - 1;\n \trecs2 = xdf2->recs + xdf2->nrec - 1;\n \tfor (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dend = xdf1->nrec - i - 1;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 85848f1685..3d26cbf1ec 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -45,10 +45,9 @@ typedef struct s_xrecord {\n } xrecord_t;\n \n typedef struct s_xdfile {\n-\tchastore_t rcha;\n+\txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\txrecord_t **recs;\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 444a108f87..332982b509 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tmmfile_t subfile1, subfile2;\n \txdfenv_t env;\n \n-\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;\n-\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +\n-\t\tdiff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;\n-\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;\n-\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +\n-\t\tdiff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;\n+\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n+\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n+\t\tdiff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n+\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n+\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n+\t\tdiff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-- \ngitgitgadget\n\n"},{"id":"526977","messageId":"97135495e213d274e145eb96a5e1d60ec694ffc8.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 10/12] xdiff: delete rchg aliasing","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:39Z","receivedAt":"2025-09-22T19:52:03Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex a66125d44a..83c4cff6f7 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -932,16 +932,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n \n int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n \txdchange_t *cscr = NULL, *xch;\n-\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n \tlong i1, i2, l1, l2;\n \n \t/*\n \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n \t */\n \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n-\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n-\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n-\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n+\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n+\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n+\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n \n \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n \t\t\t\txdl_free_script(cscr);\n-- \ngitgitgadget\n\n"},{"id":"526978","messageId":"b544c15a6762ed5675a632b3ae75eb58db0d888f.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 11/12] xdiff: use bool literals for xdfile_t.rchg","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:40Z","receivedAt":"2025-09-22T19:52:08Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nDefine macros NO(0), YES(1), MAYBE(2) as the enum values for dis1 and\ndis2 to make the code easier to follow.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 12 ++++++------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 27 +++++++++++++++------------\n 4 files changed, 29 insertions(+), 26 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 83c4cff6f7..6213ce7a03 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = true;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = true;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -753,8 +753,8 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->rchg[g->start++] = 0;\n-\t\txdf->rchg[g->end++] = 1;\n+\t\txdf->rchg[g->start++] = false;\n+\t\txdf->rchg[g->end++] = true;\n \n \t\twhile (xdf->rchg[g->end])\n \t\t\tg->end++;\n@@ -774,8 +774,8 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->rchg[--g->start] = 1;\n-\t\txdf->rchg[--g->end] = 0;\n+\t\txdf->rchg[--g->start] = true;\n+\t\txdf->rchg[--g->end] = false;\n \n \t\twhile (xdf->rchg[g->start - 1])\n \t\t\tg->start--;\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 4d857e8ae2..ad88406656 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex bf69a58527..042e889348 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 27c5a4d636..f152e3acd8 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -29,6 +29,9 @@\n #define XDL_GUESS_NLINES1 256\n #define XDL_GUESS_NLINES2 20\n \n+#define NO 0\n+#define YES 1\n+#define MAYBE 2\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n@@ -212,9 +215,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \t * current line (i) is already a multimatch line.\n \t */\n \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n-\t\tif (!dis[i - r])\n+\t\tif (dis[i - r] == NO)\n \t\t\trdis0++;\n-\t\telse if (dis[i - r] == 2)\n+\t\telse if (dis[i - r] == MAYBE)\n \t\t\trpdis0++;\n \t\telse\n \t\t\tbreak;\n@@ -228,9 +231,9 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \tif (rdis0 == 0)\n \t\treturn 0;\n \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n-\t\tif (!dis[i + r])\n+\t\tif (dis[i + r] == NO)\n \t\t\trdis1++;\n-\t\telse if (dis[i + r] == 2)\n+\t\telse if (dis[i + r] == MAYBE)\n \t\t\trpdis1++;\n \t\telse\n \t\t\tbreak;\n@@ -270,7 +273,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n-\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tdis1[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n@@ -278,26 +281,26 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n-\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tdis2[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n \t}\n \n \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n-\t\tif (dis1[i] == 1 ||\n-\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n+\t\tif (dis1[i] == YES ||\n+\t\t    (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf1->rchg[i] = 1;\n+\t\t\txdf1->rchg[i] = true;\n \t}\n \txdf1->nreff = nreff;\n \n \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n \t     i <= xdf2->dend; i++, recs++) {\n-\t\tif (dis2[i] == 1 ||\n-\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n+\t\tif (dis2[i] == YES ||\n+\t\t    (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf2->rchg[i] = 1;\n+\t\t\txdf2->rchg[i] = true;\n \t}\n \txdf2->nreff = nreff;\n \n-- \ngitgitgadget\n\n"},{"id":"526979","messageId":"034a4a7b2ad18812f68b5fcb8a61fb24f0a84d66.1758570701.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v4 12/12] xdiff: refactor 'char *rchg' to 'bool *changed' in xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-22T19:51:41Z","receivedAt":"2025-09-22T19:52:08Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 28 ++++++++++++++--------------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 12 ++++++------\n xdiff/xtypes.h     |  2 +-\n xdiff/xutils.c     |  4 ++--\n 6 files changed, 31 insertions(+), 31 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 6213ce7a03..b902be9d0e 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->rchg[xdf2->rindex[off2]] = true;\n+\t\t\txdf2->changed[xdf2->rindex[off2]] = true;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->rchg[xdf1->rindex[off1]] = true;\n+\t\t\txdf1->changed[xdf1->rindex[off1]] = true;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -708,7 +708,7 @@ struct xdlgroup {\n static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tg->start = g->end = 0;\n-\twhile (xdf->rchg[g->end])\n+\twhile (xdf->changed[g->end])\n \t\tg->end++;\n }\n \n@@ -722,7 +722,7 @@ static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->start = g->end + 1;\n-\tfor (g->end = g->start; xdf->rchg[g->end]; g->end++)\n+\tfor (g->end = g->start; xdf->changed[g->end]; g->end++)\n \t\t;\n \n \treturn 0;\n@@ -738,7 +738,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->end = g->start - 1;\n-\tfor (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n+\tfor (g->start = g->end; xdf->changed[g->start - 1]; g->start--)\n \t\t;\n \n \treturn 0;\n@@ -753,10 +753,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->rchg[g->start++] = false;\n-\t\txdf->rchg[g->end++] = true;\n+\t\txdf->changed[g->start++] = false;\n+\t\txdf->changed[g->end++] = true;\n \n-\t\twhile (xdf->rchg[g->end])\n+\t\twhile (xdf->changed[g->end])\n \t\t\tg->end++;\n \n \t\treturn 0;\n@@ -774,10 +774,10 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->rchg[--g->start] = true;\n-\t\txdf->rchg[--g->end] = false;\n+\t\txdf->changed[--g->start] = true;\n+\t\txdf->changed[--g->end] = false;\n \n-\t\twhile (xdf->rchg[g->start - 1])\n+\t\twhile (xdf->changed[g->start - 1])\n \t\t\tg->start--;\n \n \t\treturn 0;\n@@ -938,9 +938,9 @@ int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n \t */\n \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n-\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n-\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n-\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n+\t\tif (xe->xdf1.changed[i1 - 1] || xe->xdf2.changed[i2 - 1]) {\n+\t\t\tfor (l1 = i1; xe->xdf1.changed[i1 - 1]; i1--);\n+\t\t\tfor (l2 = i2; xe->xdf2.changed[i2 - 1]; i2--);\n \n \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n \t\t\t\txdl_free_script(cscr);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex ad88406656..6dc450b1fe 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n+\t\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n+\t\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 042e889348..669b653580 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex f152e3acd8..009556f7c2 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -129,7 +129,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n static void xdl_free_ctx(xdfile_t *xdf)\n {\n \txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->changed - 1);\n \txdl_free(xdf->recs);\n }\n \n@@ -142,7 +142,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txrecord_t *crec;\n \n \txdf->rindex = NULL;\n-\txdf->rchg = NULL;\n+\txdf->changed = NULL;\n \txdf->recs = NULL;\n \n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n@@ -164,7 +164,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->changed, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n@@ -173,7 +173,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tgoto abort;\n \t}\n \n-\txdf->rchg += 1;\n+\txdf->changed += 1;\n \txdf->nreff = 0;\n \txdf->dstart = 0;\n \txdf->dend = xdf->nrec - 1;\n@@ -290,7 +290,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\t    (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf1->rchg[i] = true;\n+\t\t\txdf1->changed[i] = true;\n \t}\n \txdf1->nreff = nreff;\n \n@@ -300,7 +300,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\t    (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf2->rchg[i] = true;\n+\t\t\txdf2->changed[i] = true;\n \t}\n \txdf2->nreff = nreff;\n \ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 3d26cbf1ec..f145abba3e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -48,7 +48,7 @@ typedef struct s_xdfile {\n \txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\tchar *rchg;\n+\tbool *changed;\n \tlong *rindex;\n \tlong nreff;\n } xdfile_t;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 332982b509..ed65c222e6 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -425,8 +425,8 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-\tmemcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);\n-\tmemcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);\n+\tmemcpy(diff_env->xdf1.changed + line1 - 1, env.xdf1.changed, count1);\n+\tmemcpy(diff_env->xdf2.changed + line2 - 1, env.xdf2.changed, count2);\n \n \txdl_free_env(&env);\n \n-- \ngitgitgadget\n"},{"id":"527003","messageId":"xmqq1pnyru1f.fsf@gitster.g","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-22T22:39:40Z","receivedAt":"2025-09-22T22:39:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> Changes since v3.\n>\n>  * Address review feedback.\n>  * Split the deletion of xdl_get_rec() into 2 commits.\n\n\n>  * Move NO, YES, MAYBE into xprepare.c, and use bool literals.\n\nThe elements of this array is of type \"char\"; it makes me feel a bit\nawkward that the code\n\n * assigns bool \"true\" or \"false\" to \"char\"\n\n * expects that reading it in another compilation unit yields 0 or 1\n\nInstead of abusing boolean true/false, I'd rather see the code that\nassigns 0 or 1 to use 0 or 1 as literals.  As the array got a much\nbetter name .changed[], anybody would understand that\n\n    env->xdf.changed[line] = 0;\n    env->xdf.changed[line] = 1;\n\nmean what they mean.\n\n>  * refactor 'char rchg' to 'bool changed'\n\nHmph, I am not sure if it is a good idea to pretend that this\nchanged[] array that is more than bool to be a mere bool.  An object\ndeclared as type _Bool is guaranteed to be only large enough to\nstore the values 0 and 1.  Granted that you cannot allocate less\nthan a single bite or make an array of bits in modern architectures,\nan array of _Bool would likely be byte addressed and if you assign 2\nand read it back, you may get 2 back in practice, but I'd rather not\nto see such a strange code to live in this codebase.\n\nHow about\n\n - rename rchg[] to changed[], which is a very good move;\n\n - optionally make it unsigned char, not char;\n\n - the user of changed[] that uses only 0 or 1 and is not even aware\n   of that MAYBE thing use 0 or 1;\n\n - the user of changed[] that has to be aware of that MAYBE state\n   use its own NO/YES/MAYBE for readability.\n\nHmm?\n"},{"id":"527007","messageId":"CAH=ZcbC5Y04D4bGjfH3rZ8GKabDttFez5qb9i8mXVsfE3LF26w@mail.gmail.com","threadId":"64108","inReplyTo":"xmqq1pnyru1f.fsf@gitster.g","subject":"Re: [PATCH v4 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-23T00:13:58Z","receivedAt":"2025-09-23T00:14:11Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Mon, Sep 22, 2025 at 4:39 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Ezekiel Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > Changes since v3.\n> >\n> >  * Address review feedback.\n> >  * Split the deletion of xdl_get_rec() into 2 commits.\n>\n>\n> >  * Move NO, YES, MAYBE into xprepare.c, and use bool literals.\n>\n> The elements of this array is of type \"char\"; it makes me feel a bit\n> awkward that the code\n>\n>  * assigns bool \"true\" or \"false\" to \"char\"\n>\n>  * expects that reading it in another compilation unit yields 0 or 1\n>\n> Instead of abusing boolean true/false, I'd rather see the code that\n> assigns 0 or 1 to use 0 or 1 as literals.  As the array got a much\n> better name .changed[], anybody would understand that\n>\n>     env->xdf.changed[line] = 0;\n>     env->xdf.changed[line] = 1;\n>\n> mean what they mean.\n>\n> >  * refactor 'char rchg' to 'bool changed'\n>\n> Hmph, I am not sure if it is a good idea to pretend that this\n> changed[] array that is more than bool to be a mere bool.  An object\n> declared as type _Bool is guaranteed to be only large enough to\n> store the values 0 and 1.  Granted that you cannot allocate less\n> than a single bite or make an array of bits in modern architectures,\n> an array of _Bool would likely be byte addressed and if you assign 2\n> and read it back, you may get 2 back in practice, but I'd rather not\n> to see such a strange code to live in this codebase.\n>\n> How about\n>\n>  - rename rchg[] to changed[], which is a very good move;\n>\n>  - optionally make it unsigned char, not char;\n>\n>  - the user of changed[] that uses only 0 or 1 and is not even aware\n>    of that MAYBE thing use 0 or 1;\n>\n>  - the user of changed[] that has to be aware of that MAYBE state\n>    use its own NO/YES/MAYBE for readability.\n>\n> Hmm?\n\n'changed' is NEVER EVER!!! assigned anything other than 0 or 1 which\nstrictly makes it a bool. It's easy to mistake that because the\nfunctions in xprepare.c that deal with NO, YES, and MAYBE are within a\nfew lines of 'changed'. Please re-read xdl_cleanup_records() and\nxdl_clean_mmatch() very carefully. I will update my commit message to\nmake this more clear.\n"},{"id":"527012","messageId":"xmqqecryq8o9.fsf@gitster.g","threadId":"64108","inReplyTo":"CAH=ZcbC5Y04D4bGjfH3rZ8GKabDttFez5qb9i8mXVsfE3LF26w@mail.gmail.com","subject":"Re: [PATCH v4 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-23T01:06:30Z","receivedAt":"2025-09-23T01:06:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ezekiel Newren <ezekielnewren@gmail.com> writes:\n\n>> How about\n>>\n>>  - rename rchg[] to changed[], which is a very good move;\n>>\n>>  - optionally make it unsigned char, not char;\n>>\n>>  - the user of changed[] that uses only 0 or 1 and is not even aware\n>>    of that MAYBE thing use 0 or 1;\n>>\n>>  - the user of changed[] that has to be aware of that MAYBE state\n>>    use its own NO/YES/MAYBE for readability.\n>>\n>> Hmm?\n>\n> 'changed' is NEVER EVER!!! assigned anything other than 0 or 1 which\n> strictly makes it a bool. It's easy to mistake that because the\n> functions in xprepare.c that deal with NO, YES, and MAYBE are within a\n> few lines of 'changed'. Please re-read xdl_cleanup_records() and\n> xdl_clean_mmatch() very carefully. I will update my commit message to\n> make this more clear.\n\nOK, then there is a variable with some type that is _not_ bool that\nis used in xprepare.c and the code that deal with MAYBE does\nsomething like\n\n\tu8 current_state = MAYBE;\n\n\tif (the .changed[line] is NOT valid)\n\t\tcurrent_state = MAYBE;\n\telse if (env->xdf.changed[line])\n\t\tcurrent_state = YES;\n\telse /* false */\n\t\tcurrent_state = NO;\n\nand then use current_state as a three-way variable, perhaps like\n\n\tswitch (current_state) {\n\tcase YES:\n\t\tdo the yes thing;\n\t\tbreak;\n\tcase NO:\n\t\tdo the no thing;\n\t\tbreak;\n\tcase MAYBE:\n\t\tdo the maybe thing;\n\t\tbreak;\n\t}\n\n?\n"},{"id":"527013","messageId":"CAH=ZcbAHgCLjpLMzditOg8CW-L1RPohGuQjst=h-rATTSoio3Q@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqecryq8o9.fsf@gitster.g","subject":"Re: [PATCH v4 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-23T01:30:54Z","receivedAt":"2025-09-23T01:31:08Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Mon, Sep 22, 2025 at 7:06 PM Junio C Hamano <gitster@pobox.com> wrote:\n> > 'changed' is NEVER EVER!!! assigned anything other than 0 or 1 which\n> > strictly makes it a bool. It's easy to mistake that because the\n> > functions in xprepare.c that deal with NO, YES, and MAYBE are within a\n> > few lines of 'changed'. Please re-read xdl_cleanup_records() and\n> > xdl_clean_mmatch() very carefully. I will update my commit message to\n> > make this more clear.\n>\n> OK, then there is a variable with some type that is _not_ bool that\n> is used in xprepare.c and the code that deal with MAYBE does\n> something like\n>\n>         u8 current_state = MAYBE;\n>\n>         if (the .changed[line] is NOT valid)\n>                 current_state = MAYBE;\n>         else if (env->xdf.changed[line])\n>                 current_state = YES;\n>         else /* false */\n>                 current_state = NO;\n>\n> and then use current_state as a three-way variable, perhaps like\n>\n>         switch (current_state) {\n>         case YES:\n>                 do the yes thing;\n>                 break;\n>         case NO:\n>                 do the no thing;\n>                 break;\n>         case MAYBE:\n>                 do the maybe thing;\n>                 break;\n>         }\n\nI apologize for my previous phrasing. I was not very tactful. Yes, I\nthink your suggestion is a good idea. I'll incorporate that into my\npatches.\n"},{"id":"527076","messageId":"xmqqa52lqmup.fsf@gitster.g","threadId":"64108","inReplyTo":"CAH=ZcbAHgCLjpLMzditOg8CW-L1RPohGuQjst=h-rATTSoio3Q@mail.gmail.com","subject":"Re: [PATCH v4 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-23T14:12:30Z","receivedAt":"2025-09-23T14:12:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ezekiel Newren <ezekielnewren@gmail.com> writes:\n\n> I apologize for my previous phrasing. I was not very tactful. Yes, I\n> think your suggestion is a good idea. I'll incorporate that into my\n> patches.\n\nI didn't get an impression that you were _not_ tactful at all.  If\nthe arrangement is like what I outlined in the message you are\nresponding to, I am perfectly fine if the type of changed[] is an\narray of bool.  The only thing I found was disturbing was the idea\nto assign 2 into a _Bool.  Comparing a _Bool, which can be either 0\noor 1, and find it is different from litral 2 (or MAYBE that is\ndefined to be 2) is perfectly fine.\n\nThanks.\n"},{"id":"527104","messageId":"CAH=ZcbBWjzHcQMX3_fjp4a0gSgOv-yHqZEK4T2+jaWp1nMmoHw@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqa52lqmup.fsf@gitster.g","subject":"Re: [PATCH v4 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-23T16:50:49Z","receivedAt":"2025-09-23T16:51:02Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Tue, Sep 23, 2025 at 8:12 AM Junio C Hamano <gitster@pobox.com> wrote:\n> Ezekiel Newren <ezekielnewren@gmail.com> writes:\n>\n> > I apologize for my previous phrasing. I was not very tactful. Yes, I\n> > think your suggestion is a good idea. I'll incorporate that into my\n> > patches.\n>\n> I didn't get an impression that you were _not_ tactful at all.  If\n> the arrangement is like what I outlined in the message you are\n> responding to, I am perfectly fine if the type of changed[] is an\n> array of bool.  The only thing I found was disturbing was the idea\n> to assign 2 into a _Bool.  Comparing a _Bool, which can be either 0\n> oor 1, and find it is different from litral 2 (or MAYBE that is\n> defined to be 2) is perfectly fine.\n\nI'm going to reroll this to make it much easier to see that the enum\nmacros are separate from rchg/changed. After carefully re-reading\nthose 2 functions, I think the macros NONE(0), SOME(1), TOO_MANY(2)\nmake more sense. I think what dis1 and dis2 (which I'll rename to\nmatches1, matches2) was doing is setting a state value based on the\nquestion \"How many times does this line in file 1 show up in file 2,\nand vice versa\". My guess is that if the line in file 1 doesn't show\nup in file 2 then it's obviously different. But if the number of\nmatches is greater than some threshold and isn't minimal then TOO_MANY\notherwise set it to SOME. So I think dis1, dis2 are meant as \"Here is\nhow we deal with the number of matches found in the other file.\"\n\nIs this explanation congruent with the classic diff (myers/minimal)?\n"},{"id":"527143","messageId":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v4.git.git.1758570701.gitgitgadget@gmail.com","subject":"[PATCH v5 00/13] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:17Z","receivedAt":"2025-09-23T21:24:33Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"Changes since v4.\n\n * Make it clear that the field xdfile_t.rchg (now 'xdfile_t.changed') is\n   distinct from the local variables dis1, dis2 (now 'matches1',\n   'matches2').\n * Use NONE, SOME, TOO_MANY instead of NO, YES, MAYBE.\n * Use bool literals for xdfile_t.changed.\n\nChanges since v3.\n\n * Address review feedback.\n * Split the deletion of xdl_get_rec() into 2 commits.\n * Move NO, YES, MAYBE into xprepare.c, and use bool literals.\n * refactor 'char rchg' to 'bool changed'\n\nChanges since v2.\n\n * No patch changes, just resending to get patch 9 to show up on the mailing\n   list.\n * A few tweaks to the cover letter.\n\nChanges since v1, to address review feedback.\n\n * Only include the clean up patches; The remaining patches will be split\n   into a separate series.\n * Commit message clarifications.\n * Minor style cleanups.\n * Performance impacts included in commit message of patch 8.\n\n\nRelevant part of the original cover letter follows:\n===================================================\n\nBefore:\n\ntypedef struct s_xrecord {\n\tstruct s_xrecord *next;\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\tchastore_t rcha;\n\tlong nrec;\n\tunsigned int hbits;\n\txrecord_t **rhash;\n\tlong dstart, dend;\n\txrecord_t **recs;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n\tunsigned long *ha;\n} xdfile_t;\n\n\nAfter cleanup:\n\ntypedef struct s_xrecord {\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\txrecord_t *recs;\n\tlong nrec;\n\tlong dstart, dend;\n\tbool *changed;\n\tlong *rindex;\n\tlong nreff;\n} xdfile_t;\n\n\n===\n\nEzekiel Newren (13):\n  xdiff: delete static forward declarations in xprepare\n  xdiff: delete local variables and initialize/free xdfile_t directly\n  xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  xdiff: delete superfluous function xdl_get_rec() in xemit\n  xdiff: delete superfluous local variables that alias fields in\n    xrecord_t\n  xdiff: delete struct diffdata_t\n  xdiff: delete redundant array xdfile_t.ha\n  xdiff: delete fields ha, line, size in xdlclass_t in favor of an\n    xrecord_t\n  xdiff: delete chastore from xdfile_t\n  xdiff: delete rchg aliasing\n  xdiff: rename rchg -> changed in xdfile_t\n  xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c\n  xdiff: change type of xdfile_t.changed from char to bool\n\n xdiff/xdiffi.c     | 101 ++++++--------\n xdiff/xdiffi.h     |  11 +-\n xdiff/xemit.c      |  38 ++----\n xdiff/xhistogram.c |  10 +-\n xdiff/xmerge.c     |  56 ++++----\n xdiff/xpatience.c  |  18 +--\n xdiff/xprepare.c   | 330 ++++++++++++++++++++-------------------------\n xdiff/xtypes.h     |   9 +-\n xdiff/xutils.c     |  16 +--\n 9 files changed, 259 insertions(+), 330 deletions(-)\n\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2048%2Fezekielnewren%2Fuse_rust_types_in_xdiff-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2048/ezekielnewren/use_rust_types_in_xdiff-v5\nPull-Request: https://github.com/git/git/pull/2048\n\nRange-diff vs v4:\n\n  1:  79d1099656 =  1:  890e508000 xdiff: delete static forward declarations in xprepare\n  2:  9142f28fcd =  2:  0cfd75b1ff xdiff: delete local variables and initialize/free xdfile_t directly\n  3:  13f00f5683 =  3:  92c81d2ff6 xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  4:  311279c123 =  4:  7d3a7e617c xdiff: delete superfluous function xdl_get_rec() in xemit\n  5:  d84658ac83 =  5:  1d550cf308 xdiff: delete superfluous local variables that alias fields in xrecord_t\n  6:  bf16453846 =  6:  2a3a1b657e xdiff: delete struct diffdata_t\n  7:  4ef7f243e9 =  7:  4c6543cbe3 xdiff: delete redundant array xdfile_t.ha\n  8:  3b6c2127c4 =  8:  21bf4b5a20 xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t\n  9:  f7b5021e48 =  9:  ef6ae7d29c xdiff: delete chastore from xdfile_t\n 10:  97135495e2 = 10:  7b0856108a xdiff: delete rchg aliasing\n 12:  034a4a7b2a ! 11:  570ab9f898 xdiff: refactor 'char *rchg' to 'bool *changed' in xdfile_t\n     @@ Metadata\n      Author: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## Commit message ##\n     -    xdiff: refactor 'char *rchg' to 'bool *changed' in xdfile_t\n     +    xdiff: rename rchg -> changed in xdfile_t\n      \n     +    Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xdiffi.c ##\n     @@ xdiff/xdiffi.c: int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n       \t */\n       \tif (off1 == lim1) {\n       \t\tfor (; off2 < lim2; off2++)\n     --\t\t\txdf2->rchg[xdf2->rindex[off2]] = true;\n     -+\t\t\txdf2->changed[xdf2->rindex[off2]] = true;\n     +-\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n     ++\t\t\txdf2->changed[xdf2->rindex[off2]] = 1;\n       \t} else if (off2 == lim2) {\n       \t\tfor (; off1 < lim1; off1++)\n     --\t\t\txdf1->rchg[xdf1->rindex[off1]] = true;\n     -+\t\t\txdf1->changed[xdf1->rindex[off1]] = true;\n     +-\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n     ++\t\t\txdf1->changed[xdf1->rindex[off1]] = 1;\n       \t} else {\n       \t\txdpsplit_t spl;\n       \t\tspl.i1 = spl.i2 = 0;\n     @@ xdiff/xdiffi.c: static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n       {\n       \tif (g->end < xdf->nrec &&\n       \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n     --\t\txdf->rchg[g->start++] = false;\n     --\t\txdf->rchg[g->end++] = true;\n     -+\t\txdf->changed[g->start++] = false;\n     -+\t\txdf->changed[g->end++] = true;\n     +-\t\txdf->rchg[g->start++] = 0;\n     +-\t\txdf->rchg[g->end++] = 1;\n     ++\t\txdf->changed[g->start++] = 0;\n     ++\t\txdf->changed[g->end++] = 1;\n       \n      -\t\twhile (xdf->rchg[g->end])\n      +\t\twhile (xdf->changed[g->end])\n     @@ xdiff/xdiffi.c: static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n       {\n       \tif (g->start > 0 &&\n       \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n     --\t\txdf->rchg[--g->start] = true;\n     --\t\txdf->rchg[--g->end] = false;\n     -+\t\txdf->changed[--g->start] = true;\n     -+\t\txdf->changed[--g->end] = false;\n     +-\t\txdf->rchg[--g->start] = 1;\n     +-\t\txdf->rchg[--g->end] = 0;\n     ++\t\txdf->changed[--g->start] = 1;\n     ++\t\txdf->changed[--g->end] = 0;\n       \n      -\t\twhile (xdf->rchg[g->start - 1])\n      +\t\twhile (xdf->changed[g->start - 1])\n     @@ xdiff/xhistogram.c: redo:\n       \n       \tif (!count1) {\n       \t\twhile(count2--)\n     --\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n     -+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n     +-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     ++\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n       \t\treturn 0;\n       \t} else if (!count2) {\n       \t\twhile(count1--)\n     --\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n     -+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n     +-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     ++\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n       \t\treturn 0;\n       \t}\n       \n     @@ xdiff/xhistogram.c: redo:\n       \telse {\n       \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n       \t\t\twhile (count1--)\n     --\t\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n     -+\t\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n     +-\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     ++\t\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n       \t\t\twhile (count2--)\n     --\t\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n     -+\t\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n     +-\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     ++\t\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n       \t\t\tresult = 0;\n       \t\t} else {\n       \t\t\tresult = histogram_diff(xpp, env,\n     @@ xdiff/xpatience.c: static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n       \t/* trivial case: one side is empty */\n       \tif (!count1) {\n       \t\twhile(count2--)\n     --\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n     -+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n     +-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     ++\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n       \t\treturn 0;\n       \t} else if (!count2) {\n       \t\twhile(count1--)\n     --\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n     -+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n     +-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     ++\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n       \t\treturn 0;\n       \t}\n       \n     @@ xdiff/xpatience.c: static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n       \t/* are there any matching lines at all? */\n       \tif (!map.has_matches) {\n       \t\twhile(count1--)\n     --\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n     -+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n     +-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     ++\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n       \t\twhile(count2--)\n     --\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n     -+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n     +-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     ++\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n       \t\txdl_free(map.entries);\n       \t\treturn 0;\n       \t}\n     @@ xdiff/xprepare.c: static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, lo\n       \txdf->dstart = 0;\n       \txdf->dend = xdf->nrec - 1;\n      @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n     - \t\t    (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n     + \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n       \t\t\txdf1->rindex[nreff++] = i;\n       \t\t} else\n     --\t\t\txdf1->rchg[i] = true;\n     -+\t\t\txdf1->changed[i] = true;\n     +-\t\t\txdf1->rchg[i] = 1;\n     ++\t\t\txdf1->changed[i] = 1;\n       \t}\n       \txdf1->nreff = nreff;\n       \n      @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n     - \t\t    (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n     + \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n       \t\t\txdf2->rindex[nreff++] = i;\n       \t\t} else\n     --\t\t\txdf2->rchg[i] = true;\n     -+\t\t\txdf2->changed[i] = true;\n     +-\t\t\txdf2->rchg[i] = 1;\n     ++\t\t\txdf2->changed[i] = 1;\n       \t}\n       \txdf2->nreff = nreff;\n       \n     @@ xdiff/xtypes.h: typedef struct s_xdfile {\n       \tlong nrec;\n       \tlong dstart, dend;\n      -\tchar *rchg;\n     -+\tbool *changed;\n     ++\tchar *changed;\n       \tlong *rindex;\n       \tlong nreff;\n       } xdfile_t;\n  -:  ---------- > 12:  08a0fceb72 xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c\n 11:  b544c15a67 ! 13:  975e845bfa xdiff: use bool literals for xdfile_t.rchg\n     @@ Metadata\n      Author: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## Commit message ##\n     -    xdiff: use bool literals for xdfile_t.rchg\n     +    xdiff: change type of xdfile_t.changed from char to bool\n      \n     -    Define macros NO(0), YES(1), MAYBE(2) as the enum values for dis1 and\n     -    dis2 to make the code easier to follow.\n     +    The only values possible for 'changed' is 1 and 0, which exactly maps\n     +    to a bool type. It might not look like this is the case because\n     +    matches1 and matches2 (which use to be dis1, and dis2) were also char\n     +    and were assigned numerical values within a few lines of 'changed'\n     +    (what used to be rchg).\n     +\n     +    Using NONE, SOME, TOO_MANY for matches1[i]/matches2[j], and true/false\n     +    for changed[k] makes it clear to future readers that these are\n     +    logically separate concepts.\n      \n          Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n     @@ xdiff/xdiffi.c: int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n       \t */\n       \tif (off1 == lim1) {\n       \t\tfor (; off2 < lim2; off2++)\n     --\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n     -+\t\t\txdf2->rchg[xdf2->rindex[off2]] = true;\n     +-\t\t\txdf2->changed[xdf2->rindex[off2]] = 1;\n     ++\t\t\txdf2->changed[xdf2->rindex[off2]] = true;\n       \t} else if (off2 == lim2) {\n       \t\tfor (; off1 < lim1; off1++)\n     --\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n     -+\t\t\txdf1->rchg[xdf1->rindex[off1]] = true;\n     +-\t\t\txdf1->changed[xdf1->rindex[off1]] = 1;\n     ++\t\t\txdf1->changed[xdf1->rindex[off1]] = true;\n       \t} else {\n       \t\txdpsplit_t spl;\n       \t\tspl.i1 = spl.i2 = 0;\n     @@ xdiff/xdiffi.c: static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n       {\n       \tif (g->end < xdf->nrec &&\n       \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n     --\t\txdf->rchg[g->start++] = 0;\n     --\t\txdf->rchg[g->end++] = 1;\n     -+\t\txdf->rchg[g->start++] = false;\n     -+\t\txdf->rchg[g->end++] = true;\n     +-\t\txdf->changed[g->start++] = 0;\n     +-\t\txdf->changed[g->end++] = 1;\n     ++\t\txdf->changed[g->start++] = false;\n     ++\t\txdf->changed[g->end++] = true;\n       \n     - \t\twhile (xdf->rchg[g->end])\n     + \t\twhile (xdf->changed[g->end])\n       \t\t\tg->end++;\n      @@ xdiff/xdiffi.c: static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n       {\n       \tif (g->start > 0 &&\n       \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n     --\t\txdf->rchg[--g->start] = 1;\n     --\t\txdf->rchg[--g->end] = 0;\n     -+\t\txdf->rchg[--g->start] = true;\n     -+\t\txdf->rchg[--g->end] = false;\n     +-\t\txdf->changed[--g->start] = 1;\n     +-\t\txdf->changed[--g->end] = 0;\n     ++\t\txdf->changed[--g->start] = true;\n     ++\t\txdf->changed[--g->end] = false;\n       \n     - \t\twhile (xdf->rchg[g->start - 1])\n     + \t\twhile (xdf->changed[g->start - 1])\n       \t\t\tg->start--;\n      \n       ## xdiff/xhistogram.c ##\n     @@ xdiff/xhistogram.c: redo:\n       \n       \tif (!count1) {\n       \t\twhile(count2--)\n     --\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     -+\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n     +-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n     ++\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n       \t\treturn 0;\n       \t} else if (!count2) {\n       \t\twhile(count1--)\n     --\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     -+\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n     +-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n     ++\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n       \t\treturn 0;\n       \t}\n       \n     @@ xdiff/xhistogram.c: redo:\n       \telse {\n       \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n       \t\t\twhile (count1--)\n     --\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     -+\t\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n     +-\t\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n     ++\t\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n       \t\t\twhile (count2--)\n     --\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     -+\t\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n     +-\t\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n     ++\t\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n       \t\t\tresult = 0;\n       \t\t} else {\n       \t\t\tresult = histogram_diff(xpp, env,\n     @@ xdiff/xpatience.c: static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n       \t/* trivial case: one side is empty */\n       \tif (!count1) {\n       \t\twhile(count2--)\n     --\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     -+\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n     +-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n     ++\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n       \t\treturn 0;\n       \t} else if (!count2) {\n       \t\twhile(count1--)\n     --\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     -+\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n     +-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n     ++\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n       \t\treturn 0;\n       \t}\n       \n     @@ xdiff/xpatience.c: static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n       \t/* are there any matching lines at all? */\n       \tif (!map.has_matches) {\n       \t\twhile(count1--)\n     --\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n     -+\t\t\tenv->xdf1.rchg[line1++ - 1] = true;\n     +-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n     ++\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n       \t\twhile(count2--)\n     --\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n     -+\t\t\tenv->xdf2.rchg[line2++ - 1] = true;\n     +-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n     ++\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n       \t\txdl_free(map.entries);\n       \t\treturn 0;\n       \t}\n      \n       ## xdiff/xprepare.c ##\n     -@@\n     - #define XDL_GUESS_NLINES1 256\n     - #define XDL_GUESS_NLINES2 20\n     - \n     -+#define NO 0\n     -+#define YES 1\n     -+#define MAYBE 2\n     - \n     - typedef struct s_xdlclass {\n     - \tstruct s_xdlclass *next;\n     -@@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n     - \t * current line (i) is already a multimatch line.\n     - \t */\n     - \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n     --\t\tif (!dis[i - r])\n     -+\t\tif (dis[i - r] == NO)\n     - \t\t\trdis0++;\n     --\t\telse if (dis[i - r] == 2)\n     -+\t\telse if (dis[i - r] == MAYBE)\n     - \t\t\trpdis0++;\n     - \t\telse\n     - \t\t\tbreak;\n     -@@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n     - \tif (rdis0 == 0)\n     - \t\treturn 0;\n     - \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n     --\t\tif (!dis[i + r])\n     -+\t\tif (dis[i + r] == NO)\n     - \t\t\trdis1++;\n     --\t\telse if (dis[i + r] == 2)\n     -+\t\telse if (dis[i + r] == MAYBE)\n     - \t\t\trpdis1++;\n     - \t\telse\n     - \t\t\tbreak;\n      @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n     - \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n     - \t\trcrec = cf->rcrecs[recs->ha];\n     - \t\tnm = rcrec ? rcrec->len2 : 0;\n     --\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n     -+\t\tdis1[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n     - \t}\n       \n     - \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n     + \t/*\n     + \t * Create temporary arrays that will help us decide if\n     +-\t * changed[i] should remain 0 or become 1.\n     ++\t * changed[i] should remain false, or become true.\n     + \t */\n     + \tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n     + \t\tstatus = -1;\n      @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n     - \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n     - \t\trcrec = cf->rcrecs[recs->ha];\n     - \t\tnm = rcrec ? rcrec->len1 : 0;\n     --\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n     -+\t\tdis2[i] = (nm == 0) ? NO: (nm >= mlim && !need_min) ? MAYBE: YES;\n     - \t}\n       \n     + \t/*\n     + \t * Use temporary arrays to decide if changed[i] should remain\n     +-\t * 0 or become 1.\n     ++\t * false, or become true.\n     + \t */\n       \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n       \t     i <= xdf1->dend; i++, recs++) {\n     --\t\tif (dis1[i] == 1 ||\n     --\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n     -+\t\tif (dis1[i] == YES ||\n     -+\t\t    (dis1[i] == MAYBE && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n     + \t\tif (matches1[i] == SOME ||\n     + \t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n       \t\t\txdf1->rindex[nreff++] = i;\n     +-\t\t\t/* changed[i] remains 0 */\n     ++\t\t\t/* changed[i] remains false */\n       \t\t} else\n     --\t\t\txdf1->rchg[i] = 1;\n     -+\t\t\txdf1->rchg[i] = true;\n     +-\t\t\txdf1->changed[i] = 1;\n     ++\t\t\txdf1->changed[i] = true;\n       \t}\n       \txdf1->nreff = nreff;\n       \n     - \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n     - \t     i <= xdf2->dend; i++, recs++) {\n     --\t\tif (dis2[i] == 1 ||\n     --\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n     -+\t\tif (dis2[i] == YES ||\n     -+\t\t    (dis2[i] == MAYBE && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n     +@@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n     + \t\tif (matches2[i] == SOME ||\n     + \t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n       \t\t\txdf2->rindex[nreff++] = i;\n     +-\t\t\t/* changed[i] remains 0 */\n     ++\t\t\t/* changed[i] remains false */\n       \t\t} else\n     --\t\t\txdf2->rchg[i] = 1;\n     -+\t\t\txdf2->rchg[i] = true;\n     +-\t\t\txdf2->changed[i] = 1;\n     ++\t\t\txdf2->changed[i] = true;\n       \t}\n       \txdf2->nreff = nreff;\n       \n     +\n     + ## xdiff/xtypes.h ##\n     +@@ xdiff/xtypes.h: typedef struct s_xdfile {\n     + \txrecord_t *recs;\n     + \tlong nrec;\n     + \tlong dstart, dend;\n     +-\tchar *changed;\n     ++\tbool *changed;\n     + \tlong *rindex;\n     + \tlong nreff;\n     + } xdfile_t;\n\n-- \ngitgitgadget\n"},{"id":"527144","messageId":"890e5080008a6ccf8f8157a9a11fbffa401953a6.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 01/13] xdiff: delete static forward declarations in xprepare","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:18Z","receivedAt":"2025-09-23T21:24:34Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nMove xdl_prepare_env() later in the file to avoid the need\nfor static forward declarations.\n\nBest-viewed-with: --color-moved\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 116 ++++++++++++++++++++---------------------------\n 1 file changed, 50 insertions(+), 66 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e1d4017b2d..249bfa678f 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -53,21 +53,6 @@ typedef struct s_xdlclassifier {\n \n \n \n-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);\n-static void xdl_free_classifier(xdlclassifier_t *cf);\n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec);\n-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n-\t\t\t   xdlclassifier_t *cf, xdfile_t *xdf);\n-static void xdl_free_ctx(xdfile_t *xdf);\n-static int xdl_clean_mmatch(char const *dis, long i, long s, long e);\n-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-\n-\n-\n-\n static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {\n \tcf->flags = flags;\n \n@@ -242,57 +227,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {\n }\n \n \n-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n-\t\t    xdfenv_t *xe) {\n-\tlong enl1, enl2, sample;\n-\txdlclassifier_t cf;\n-\n-\tmemset(&cf, 0, sizeof(cf));\n-\n-\t/*\n-\t * For histogram diff, we can afford a smaller sample size and\n-\t * thus a poorer estimate of the number of lines, as the hash\n-\t * table (rhash) won't be filled up/grown. The number of lines\n-\t * (nrecs) will be updated correctly anyway by\n-\t * xdl_prepare_ctx().\n-\t */\n-\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n-\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n-\n-\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n-\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n-\n-\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n-\t\treturn -1;\n-\n-\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n-\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n-\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n-\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf2);\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\txdl_free_classifier(&cf);\n-\n-\treturn 0;\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n@@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2\n \n \treturn 0;\n }\n+\n+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n+\t\t    xdfenv_t *xe) {\n+\tlong enl1, enl2, sample;\n+\txdlclassifier_t cf;\n+\n+\tmemset(&cf, 0, sizeof(cf));\n+\n+\t/*\n+\t * For histogram diff, we can afford a smaller sample size and\n+\t * thus a poorer estimate of the number of lines, as the hash\n+\t * table (rhash) won't be filled up/grown. The number of lines\n+\t * (nrecs) will be updated correctly anyway by\n+\t * xdl_prepare_ctx().\n+\t */\n+\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n+\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n+\n+\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n+\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n+\n+\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n+\t\treturn -1;\n+\n+\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n+\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n+\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n+\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf2);\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\txdl_free_classifier(&cf);\n+\n+\treturn 0;\n+}\n-- \ngitgitgadget\n\n"},{"id":"527145","messageId":"0cfd75b1ffa3f02cf1230866e6a060e653691825.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 02/13] xdiff: delete local variables and initialize/free xdfile_t directly","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:19Z","receivedAt":"2025-09-23T21:24:36Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThese local variables are essentially a hand-rolled additional\nimplementation of xdl_free_ctx() inlined into xdl_prepare_ctx(). Modify\nthe code to use the existing xdl_free_ctx() function so there aren't\ntwo ways to free such variables.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 78 +++++++++++++++++++-----------------------------\n 1 file changed, 30 insertions(+), 48 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 249bfa678f..96134c9fbf 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -134,99 +134,81 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n }\n \n \n+static void xdl_free_ctx(xdfile_t *xdf)\n+{\n+\txdl_free(xdf->rhash);\n+\txdl_free(xdf->rindex);\n+\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->ha);\n+\txdl_free(xdf->recs);\n+\txdl_cha_free(&xdf->rcha);\n+}\n+\n+\n static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n \t\t\t   xdlclassifier_t *cf, xdfile_t *xdf) {\n-\tunsigned int hbits;\n-\tlong nrec, hsize, bsize;\n+\tlong bsize;\n \tunsigned long hav;\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n-\txrecord_t **recs;\n-\txrecord_t **rhash;\n-\tunsigned long *ha;\n-\tchar *rchg;\n-\tlong *rindex;\n \n-\tha = NULL;\n-\trindex = NULL;\n-\trchg = NULL;\n-\trhash = NULL;\n-\trecs = NULL;\n+\txdf->ha = NULL;\n+\txdf->rindex = NULL;\n+\txdf->rchg = NULL;\n+\txdf->rhash = NULL;\n+\txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n \t\tgoto abort;\n-\tif (!XDL_ALLOC_ARRAY(recs, narec))\n+\tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\thbits = xdl_hashbits((unsigned int) narec);\n-\thsize = 1 << hbits;\n-\tif (!XDL_CALLOC_ARRAY(rhash, hsize))\n+\txdf->hbits = xdl_hashbits((unsigned int) narec);\n+\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n \t\tgoto abort;\n \n-\tnrec = 0;\n+\txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n \t\t\tprev = cur;\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n-\t\t\tif (XDL_ALLOC_GROW(recs, nrec + 1, narec))\n+\t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n \t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n \t\t\t\tgoto abort;\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\trecs[nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)\n+\t\t\txdf->recs[xdf->nrec++] = crec;\n+\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(rchg, nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n-\t\tif (!XDL_ALLOC_ARRAY(rindex, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(ha, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n \t\t\tgoto abort;\n \t}\n \n-\txdf->nrec = nrec;\n-\txdf->recs = recs;\n-\txdf->hbits = hbits;\n-\txdf->rhash = rhash;\n-\txdf->rchg = rchg + 1;\n-\txdf->rindex = rindex;\n+\txdf->rchg += 1;\n \txdf->nreff = 0;\n-\txdf->ha = ha;\n \txdf->dstart = 0;\n-\txdf->dend = nrec - 1;\n+\txdf->dend = xdf->nrec - 1;\n \n \treturn 0;\n \n abort:\n-\txdl_free(ha);\n-\txdl_free(rindex);\n-\txdl_free(rchg);\n-\txdl_free(rhash);\n-\txdl_free(recs);\n-\txdl_cha_free(&xdf->rcha);\n+\txdl_free_ctx(xdf);\n \treturn -1;\n }\n \n \n-static void xdl_free_ctx(xdfile_t *xdf) {\n-\n-\txdl_free(xdf->rhash);\n-\txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n-\txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n-- \ngitgitgadget\n\n"},{"id":"527146","messageId":"92c81d2ff6059b0d91f9d919b36303af953f4d09.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 03/13] xdiff: delete unnecessary fields from xrecord_t and xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:20Z","receivedAt":"2025-09-23T21:24:37Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,\nbut never used for anything by the code. Remove them.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 15 ++-------------\n xdiff/xtypes.h   |  3 ---\n 2 files changed, 2 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 96134c9fbf..3576415c85 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -91,8 +91,7 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n }\n \n \n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec) {\n+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n \tchar const *line;\n \txdlclass_t *rcrec;\n@@ -126,17 +125,12 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \n \trec->ha = (unsigned long) rcrec->idx;\n \n-\thi = (long) XDL_HASHLONG(rec->ha, hbits);\n-\trec->next = rhash[hi];\n-\trhash[hi] = rec;\n-\n \treturn 0;\n }\n \n \n static void xdl_free_ctx(xdfile_t *xdf)\n {\n-\txdl_free(xdf->rhash);\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->ha);\n@@ -155,7 +149,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n-\txdf->rhash = NULL;\n \txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n@@ -163,10 +156,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\txdf->hbits = xdl_hashbits((unsigned int) narec);\n-\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n-\t\tgoto abort;\n-\n \txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n@@ -180,7 +169,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n \t\t\txdf->recs[xdf->nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n+\t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8442bd436e..8b8467360e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -39,7 +39,6 @@ typedef struct s_chastore {\n } chastore_t;\n \n typedef struct s_xrecord {\n-\tstruct s_xrecord *next;\n \tchar const *ptr;\n \tlong size;\n \tunsigned long ha;\n@@ -48,8 +47,6 @@ typedef struct s_xrecord {\n typedef struct s_xdfile {\n \tchastore_t rcha;\n \tlong nrec;\n-\tunsigned int hbits;\n-\txrecord_t **rhash;\n \tlong dstart, dend;\n \txrecord_t **recs;\n \tchar *rchg;\n-- \ngitgitgadget\n\n"},{"id":"527147","messageId":"7d3a7e617c68cba19e6ea1195bd6d753a5795a4d.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 04/13] xdiff: delete superfluous function xdl_get_rec() in xemit","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:21Z","receivedAt":"2025-09-23T21:24:39Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen xrecord_t was a linked list, and recs didn't exist, I assume this\nfunction walked the list until it found the right record. Accessing\na contiguous array is so trival that this function is now superfluous.\nDelete it.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 23 +++++++----------------\n 1 file changed, 7 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 1d40c9cb40..40fc8154f3 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -22,23 +22,14 @@\n \n #include \"xinclude.h\"\n \n-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n-\n-\t*rec = xdf->recs[ri]->ptr;\n-\n-\treturn xdf->recs[ri]->size;\n-}\n-\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n \tlong size, psize = strlen(pre);\n-\tchar const *rec;\n-\n-\tsize = xdl_get_rec(xdf, ri, &rec);\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n+\tchar const *rec = xdf->recs[ri]->ptr;\n \n+\tsize = xdf->recs[ri]->size;\n+\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0)\n \t\treturn -1;\n-\t}\n \n \treturn 0;\n }\n@@ -120,8 +111,8 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\tconst char *rec = xdf->recs[ri]->ptr;\n+\tlong len = xdf->recs[ri]->size;\n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec, len, buf, sz);\n \treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n@@ -160,8 +151,8 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\tconst char *rec = xdf->recs[ri]->ptr;\n+\tlong len = xdf->recs[ri]->size;\n \n \twhile (len > 0 && XDL_ISSPACE(*rec)) {\n \t\trec++;\n-- \ngitgitgadget\n\n"},{"id":"527148","messageId":"1d550cf308a189721ba089443f29678ead0344e5.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 05/13] xdiff: delete superfluous local variables that alias fields in xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:22Z","receivedAt":"2025-09-23T21:24:40Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nUse the type xrecord_t as the local variable for the functions in the\nfile xdiff/xemit.c.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 29 +++++++++++++----------------\n 1 file changed, 13 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 40fc8154f3..2161ac3cd0 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -23,12 +23,11 @@\n #include \"xinclude.h\"\n \n \n-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n-\tlong size, psize = strlen(pre);\n-\tchar const *rec = xdf->recs[ri]->ptr;\n+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n+{\n+\txrecord_t *rec = xdf->recs[ri];\n \n-\tsize = xdf->recs[ri]->size;\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0)\n+\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n \n \treturn 0;\n@@ -111,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec = xdf->recs[ri]->ptr;\n-\tlong len = xdf->recs[ri]->size;\n+\txrecord_t *rec = xdf->recs[ri];\n+\n \tif (!xecfg->find_func)\n-\t\treturn def_ff(rec, len, buf, sz);\n-\treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n+\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n+\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n }\n \n static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n@@ -151,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec = xdf->recs[ri]->ptr;\n-\tlong len = xdf->recs[ri]->size;\n+\txrecord_t *rec = xdf->recs[ri];\n+\tlong i = 0;\n \n-\twhile (len > 0 && XDL_ISSPACE(*rec)) {\n-\t\trec++;\n-\t\tlen--;\n-\t}\n-\treturn !len;\n+\tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n+\n+\treturn i == rec->size;\n }\n \n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n-- \ngitgitgadget\n\n"},{"id":"527149","messageId":"2a3a1b657ec5f422525f61b8047b3631a587ddf1.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 06/13] xdiff: delete struct diffdata_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:23Z","receivedAt":"2025-09-23T21:24:42Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nEvery field in this struct is an alias for a certain field in xdfile_t.\n\ndiffdata_t.nrec   -> xdfile_t.nreff\ndiffdata_t.ha     -> xdfile_t.ha\ndiffdata_t.rindex -> xdfile_t.rindex\ndiffdata_t.rchg   -> xdfile_t.rchg\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 32 ++++++++------------------------\n xdiff/xdiffi.h | 11 ++---------\n 2 files changed, 10 insertions(+), 33 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 5a96e36dfb..bbf0161f84 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -257,10 +257,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n  * sub-boxes by calling the box splitting function. Note that the real job\n  * (marking changed lines) is done in the two boundary reaching checks.\n  */\n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;\n+\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n@@ -273,17 +273,11 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t * be obviously changed.\n \t */\n \tif (off1 == lim1) {\n-\t\tchar *rchg2 = dd2->rchg;\n-\t\tlong *rindex2 = dd2->rindex;\n-\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\trchg2[rindex2[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n \t} else if (off2 == lim2) {\n-\t\tchar *rchg1 = dd1->rchg;\n-\t\tlong *rindex1 = dd1->rindex;\n-\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\trchg1[rindex1[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -300,9 +294,9 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t\t/*\n \t\t * ... et Impera.\n \t\t */\n-\t\tif (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,\n+\t\tif (xdl_recs_cmp(xdf1, off1, spl.i1, xdf2, off2, spl.i2,\n \t\t\t\t kvdf, kvdb, spl.min_lo, xenv) < 0 ||\n-\t\t    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,\n+\t\t    xdl_recs_cmp(xdf1, spl.i1, lim1, xdf2, spl.i2, lim2,\n \t\t\t\t kvdf, kvdb, spl.min_hi, xenv) < 0) {\n \n \t\t\treturn -1;\n@@ -318,7 +312,6 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \tlong ndiags;\n \tlong *kvd, *kvdf, *kvdb;\n \txdalgoenv_t xenv;\n-\tdiffdata_t dd1, dd2;\n \tint res;\n \n \tif (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)\n@@ -357,16 +350,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \txenv.snake_cnt = XDL_SNAKE_CNT;\n \txenv.heur_min = XDL_HEUR_MIN_COST;\n \n-\tdd1.nrec = xe->xdf1.nreff;\n-\tdd1.ha = xe->xdf1.ha;\n-\tdd1.rchg = xe->xdf1.rchg;\n-\tdd1.rindex = xe->xdf1.rindex;\n-\tdd2.nrec = xe->xdf2.nreff;\n-\tdd2.ha = xe->xdf2.ha;\n-\tdd2.rchg = xe->xdf2.rchg;\n-\tdd2.rindex = xe->xdf2.rindex;\n-\n-\tres = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,\n+\tres = xdl_recs_cmp(&xe->xdf1, 0, xe->xdf1.nreff, &xe->xdf2, 0, xe->xdf2.nreff,\n \t\t\t   kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,\n \t\t\t   &xenv);\n \txdl_free(kvd);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 126c9d8ff4..49e52c67f9 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -24,13 +24,6 @@\n #define XDIFFI_H\n \n \n-typedef struct s_diffdata {\n-\tlong nrec;\n-\tunsigned long const *ha;\n-\tlong *rindex;\n-\tchar *rchg;\n-} diffdata_t;\n-\n typedef struct s_xdalgoenv {\n \tlong mxcost;\n \tlong snake_cnt;\n@@ -46,8 +39,8 @@ typedef struct s_xdchange {\n \n \n \n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);\n int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\txdfenv_t *xe);\n-- \ngitgitgadget\n\n"},{"id":"527150","messageId":"4c6543cbe390b37c7847cae6b2a8cc4223c225bf.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 07/13] xdiff: delete redundant array xdfile_t.ha","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:24Z","receivedAt":"2025-09-23T21:24:44Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen 0 <= i < xdfile_t.nreff the following is true:\nxdfile_t.ha[i] == xdfile_t.recs[xdfile_t.rindex[i]]\n\nThis makes the code about 5% slower. The fields rindex and ha are\nspecific to the classic diff (myers and minimal). I plan on creating a\nstruct for classic diff, but there's a lot of cleanup that needs to be\ndone before that can happen and leaving ha in would make those cleanups\nharder to follow.\n\nA subsequent commit will delete the chastore cha from xdfile_t. That\nlater commit will investigate deleting ha and cha independently and\ntogether.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c   | 24 ++++++++++++++----------\n xdiff/xprepare.c | 12 ++----------\n xdiff/xtypes.h   |  1 -\n 3 files changed, 16 insertions(+), 21 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex bbf0161f84..11cd090b53 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -22,6 +22,11 @@\n \n #include \"xinclude.h\"\n \n+static unsigned long get_hash(xdfile_t *xdf, long index)\n+{\n+\treturn xdf->recs[xdf->rindex[index]]->ha;\n+}\n+\n #define XDL_MAX_COST_MIN 256\n #define XDL_HEUR_MIN_COST 256\n #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)\n@@ -42,8 +47,8 @@ typedef struct s_xdpsplit {\n  * using this algorithm, so a little bit of heuristic is needed to cut the\n  * search and to return a suboptimal point.\n  */\n-static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n-\t\t      unsigned long const *ha2, long off2, long lim2,\n+static long xdl_split(xdfile_t *xdf1, long off1, long lim1,\n+\t\t      xdfile_t *xdf2, long off2, long lim2,\n \t\t      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,\n \t\t      xdalgoenv_t *xenv) {\n \tlong dmin = off1 - lim2, dmax = lim1 - off2;\n@@ -87,7 +92,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdf[d + 1];\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);\n+\t\t\tfor (; i1 < lim1 && i2 < lim2 && get_hash(xdf1, i1) == get_hash(xdf2, i2); i1++, i2++);\n \t\t\tif (i1 - prev1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdf[d] = i1;\n@@ -124,7 +129,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdb[d + 1] - 1;\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);\n+\t\t\tfor (; i1 > off1 && i2 > off2 && get_hash(xdf1, i1 - 1) == get_hash(xdf2, i2 - 1); i1--, i2--);\n \t\t\tif (prev1 - i1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdb[d] = i1;\n@@ -159,7 +164,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&\n \t\t\t\t    off2 + xenv->snake_cnt <= i2 && i2 < lim2) {\n-\t\t\t\t\tfor (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)\n+\t\t\t\t\tfor (k = 1; get_hash(xdf1, i1 - k) == get_hash(xdf2, i2 - k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -183,7 +188,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&\n \t\t\t\t    off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {\n-\t\t\t\t\tfor (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)\n+\t\t\t\t\tfor (k = 0; get_hash(xdf1, i1 + k) == get_hash(xdf2, i2 + k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt - 1) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -260,13 +265,12 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n \t */\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, off1) == get_hash(xdf2, off2); off1++, off2++);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, lim1 - 1) == get_hash(xdf2, lim2 - 1); lim1--, lim2--);\n \n \t/*\n \t * If one dimension is empty, then all records on the other one must\n@@ -285,7 +289,7 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t/*\n \t\t * Divide ...\n \t\t */\n-\t\tif (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,\n+\t\tif (xdl_split(xdf1, off1, lim1, xdf2, off2, lim2, kvdf, kvdb,\n \t\t\t      need_min, &spl, xenv) < 0) {\n \n \t\t\treturn -1;\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 3576415c85..22c44f0683 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -133,7 +133,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n {\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n \txdl_free(xdf->recs);\n \txdl_cha_free(&xdf->rcha);\n }\n@@ -146,7 +145,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n \n-\txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n@@ -181,8 +179,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n \t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n-\t\t\tgoto abort;\n \t}\n \n \txdf->rchg += 1;\n@@ -300,9 +296,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (dis1[i] == 1 ||\n \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n-\t\t\txdf1->rindex[nreff] = i;\n-\t\t\txdf1->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf1->rchg[i] = 1;\n \t}\n@@ -312,9 +306,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf2->dend; i++, recs++) {\n \t\tif (dis2[i] == 1 ||\n \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n-\t\t\txdf2->rindex[nreff] = i;\n-\t\t\txdf2->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf2->rchg[i] = 1;\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8b8467360e..85848f1685 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -52,7 +52,6 @@ typedef struct s_xdfile {\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\n-\tunsigned long *ha;\n } xdfile_t;\n \n typedef struct s_xdfenv {\n-- \ngitgitgadget\n\n"},{"id":"527151","messageId":"21bf4b5a20273e2639826cdf014b8f6da98c044a.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 08/13] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:25Z","receivedAt":"2025-09-23T21:24:45Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe fields from xdlclass_t are aliases of xrecord_t:\nxdlclass_t.line -> xrecord_t.ptr\nxdlclass_t.size -> xrecord_t.size\nxdlclass_t.ha   -> xrecord_t.ha\n\nxdlclass_t carries a copy of the data in xrecord_t, but instead of\nembedding xrecord_t it duplicates the individual fields. A future\ncommit will change the types used in xrecord_t so embed it in\nxdlclass_t first, so we don't have to remember to change the types\nhere as well.\n\nBest-viewed-with: --color-words\nHelped-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 14 ++++----------\n 1 file changed, 4 insertions(+), 10 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 22c44f0683..e6e2c0e1c0 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -32,9 +32,7 @@\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n-\tunsigned long ha;\n-\tchar const *line;\n-\tlong size;\n+\txrecord_t rec;\n \tlong idx;\n \tlong len1, len2;\n } xdlclass_t;\n@@ -93,14 +91,12 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n \n static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n-\tchar const *line;\n \txdlclass_t *rcrec;\n \n-\tline = rec->ptr;\n \thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n-\t\tif (rcrec->ha == rec->ha &&\n-\t\t\t\txdl_recmatch(rcrec->line, rcrec->size,\n+\t\tif (rcrec->rec.ha == rec->ha &&\n+\t\t\t\txdl_recmatch(rcrec->rec.ptr, rcrec->rec.size,\n \t\t\t\t\trec->ptr, rec->size, cf->flags))\n \t\t\tbreak;\n \n@@ -113,9 +109,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \t\tif (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))\n \t\t\t\treturn -1;\n \t\tcf->rcrecs[rcrec->idx] = rcrec;\n-\t\trcrec->line = line;\n-\t\trcrec->size = rec->size;\n-\t\trcrec->ha = rec->ha;\n+\t\trcrec->rec = *rec;\n \t\trcrec->len1 = rcrec->len2 = 0;\n \t\trcrec->next = cf->rchash[hi];\n \t\tcf->rchash[hi] = rcrec;\n-- \ngitgitgadget\n\n"},{"id":"527152","messageId":"ef6ae7d29cdee1697d7457973e5c4da4005553f2.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 09/13] xdiff: delete chastore from xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:26Z","receivedAt":"2025-09-23T21:24:47Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxdfile_t currently uses chastore_t which is an arena allocator. I\nthink that xrecord_t used to be a linked list and recs didn't exist\noriginally. When recs was added I think they forgot to remove\nxdfile_t.next, but was overlooked. This dual data structure setup\nmakes the code somewhat confusing.\n\nAdditionally the C type chastore_t isn't FFI friendly, and provides\nlittle to no performance benefit over using realloc to grow an array.\n\nPerformance impact of deleting fields from xdfile_t:\nDeleting ha is about 5% slower.\nDeleting cha is about 5% faster.\n\nDelete ha, but keep cha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.269 s ±  0.017 s    [User: 1.135 s, System: 0.128 s]\n    Range (min … max):    1.249 s …  1.286 s    10 runs\n\n  Benchmark 2: build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.339 s ±  0.017 s    [User: 1.234 s, System: 0.099 s]\n    Range (min … max):    1.320 s …  1.358 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.06 ± 0.02 times faster than build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete cha, but keep ha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.290 s ±  0.001 s    [User: 1.154 s, System: 0.130 s]\n    Range (min … max):    1.288 s …  1.292 s    10 runs\n\n  Benchmark 2: build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.232 s ±  0.017 s    [User: 1.105 s, System: 0.121 s]\n    Range (min … max):    1.205 s …  1.249 s    10 runs\n\n  Summary\n    build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.05 ± 0.01 times faster than build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete ha AND chastore\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha_and_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.291 s ±  0.002 s    [User: 1.156 s, System: 0.129 s]\n    Range (min … max):    1.287 s …  1.295 s    10 runs\n\n  Benchmark 2: build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.306 s ±  0.001 s    [User: 1.195 s, System: 0.105 s]\n    Range (min … max):    1.305 s …  1.308 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.01 ± 0.00 times faster than build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 24 ++++++++++----------\n xdiff/xemit.c      |  6 ++---\n xdiff/xhistogram.c |  2 +-\n xdiff/xmerge.c     | 56 +++++++++++++++++++++++-----------------------\n xdiff/xpatience.c  | 10 ++++-----\n xdiff/xprepare.c   | 19 ++++++----------\n xdiff/xtypes.h     |  3 +--\n xdiff/xutils.c     | 12 +++++-----\n 8 files changed, 63 insertions(+), 69 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 11cd090b53..a66125d44a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -24,7 +24,7 @@\n \n static unsigned long get_hash(xdfile_t *xdf, long index)\n {\n-\treturn xdf->recs[xdf->rindex[index]]->ha;\n+\treturn xdf->recs[xdf->rindex[index]].ha;\n }\n \n #define XDL_MAX_COST_MIN 256\n@@ -489,13 +489,13 @@ static void measure_split(const xdfile_t *xdf, long split,\n \t\tm->indent = -1;\n \t} else {\n \t\tm->end_of_file = 0;\n-\t\tm->indent = get_indent(xdf->recs[split]);\n+\t\tm->indent = get_indent(&xdf->recs[split]);\n \t}\n \n \tm->pre_blank = 0;\n \tm->pre_indent = -1;\n \tfor (i = split - 1; i >= 0; i--) {\n-\t\tm->pre_indent = get_indent(xdf->recs[i]);\n+\t\tm->pre_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->pre_indent != -1)\n \t\t\tbreak;\n \t\tm->pre_blank += 1;\n@@ -508,7 +508,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n \tm->post_blank = 0;\n \tm->post_indent = -1;\n \tfor (i = split + 1; i < xdf->nrec; i++) {\n-\t\tm->post_indent = get_indent(xdf->recs[i]);\n+\t\tm->post_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->post_indent != -1)\n \t\t\tbreak;\n \t\tm->post_blank += 1;\n@@ -752,7 +752,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n-\t    recs_match(xdf->recs[g->start], xdf->recs[g->end])) {\n+\t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n \t\txdf->rchg[g->start++] = 0;\n \t\txdf->rchg[g->end++] = 1;\n \n@@ -773,7 +773,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n-\t    recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {\n+\t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n \t\txdf->rchg[--g->start] = 1;\n \t\txdf->rchg[--g->end] = 0;\n \n@@ -988,16 +988,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n \t\tint ignore = 1;\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tlong i;\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\txch->ignore = ignore;\n \t}\n@@ -1021,7 +1021,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \txdchange_t *xch;\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tint ignore = 1;\n \t\tlong i;\n \n@@ -1033,11 +1033,11 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\txch->ignore = ignore;\n \t}\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 2161ac3cd0..b2f1f30cd3 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -25,7 +25,7 @@\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n@@ -110,7 +110,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n@@ -150,7 +150,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \tlong i = 0;\n \n \tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 040d81e0bc..4d857e8ae2 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -86,7 +86,7 @@ struct region {\n \t((LINE_MAP(index, ptr))->cnt)\n \n #define REC(env, s, l) \\\n-\t(env->xdf##s.recs[l - 1])\n+\t(&env->xdf##s.recs[l - 1])\n \n static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n {\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex af40c88a5b..fd600cbb5d 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \t\tint line_count, long flags)\n {\n \tint i;\n-\txrecord_t **rec1 = xe1->xdf2.recs + i1;\n-\txrecord_t **rec2 = xe2->xdf2.recs + i2;\n+\txrecord_t *rec1 = xe1->xdf2.recs + i1;\n+\txrecord_t *rec2 = xe2->xdf2.recs + i2;\n \n \tfor (i = 0; i < line_count; i++) {\n-\t\tint result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,\n-\t\t\trec2[i]->ptr, rec2[i]->size, flags);\n+\t\tint result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n+\t\t\trec2[i].ptr, rec2[i].size, flags);\n \t\tif (!result)\n \t\t\treturn -1;\n \t}\n@@ -111,7 +111,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \n static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)\n {\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \tint size = 0;\n \n \trecs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n@@ -119,12 +119,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n \tif (count < 1)\n \t\treturn 0;\n \n-\tfor (i = 0; i < count; size += recs[i++]->size)\n+\tfor (i = 0; i < count; size += recs[i++].size)\n \t\tif (dest)\n-\t\t\tmemcpy(dest + size, recs[i]->ptr, recs[i]->size);\n+\t\t\tmemcpy(dest + size, recs[i].ptr, recs[i].size);\n \tif (add_nl) {\n-\t\ti = recs[count - 1]->size;\n-\t\tif (i == 0 || recs[count - 1]->ptr[i - 1] != '\\n') {\n+\t\ti = recs[count - 1].size;\n+\t\tif (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n \t\t\tif (needs_cr) {\n \t\t\t\tif (dest)\n \t\t\t\t\tdest[size] = '\\r';\n@@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)\n \n \tif (i < file->nrec - 1)\n \t\t/* All lines before the last *must* end in LF */\n-\t\treturn (size = file->recs[i]->size) > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\treturn (size = file->recs[i].size) > 1 &&\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!file->nrec)\n \t\t/* Cannot determine eol style from empty file */\n \t\treturn -1;\n-\tif ((size = file->recs[i]->size) &&\n-\t\t\tfile->recs[i]->ptr[size - 1] == '\\n')\n+\tif ((size = file->recs[i].size) &&\n+\t\t\tfile->recs[i].ptr[size - 1] == '\\n')\n \t\t/* Last line; ends in LF; Is it CR/LF? */\n \t\treturn size > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!i)\n \t\t/* The only line has no eol */\n \t\treturn -1;\n \t/* Determine eol from second-to-last line */\n-\treturn (size = file->recs[i - 1]->size) > 1 &&\n-\t\tfile->recs[i - 1]->ptr[size - 2] == '\\r';\n+\treturn (size = file->recs[i - 1].size) > 1 &&\n+\t\tfile->recs[i - 1].ptr[size - 2] == '\\r';\n }\n \n static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)\n@@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\txpparam_t const *xpp)\n {\n-\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n+\txrecord_t *rec1 = xe1->xdf2.recs, *rec2 = xe2->xdf2.recs;\n \tfor (; m; m = m->next) {\n \t\t/* let's handle just the conflicts */\n \t\tif (m->mode)\n \t\t\tcontinue;\n \n \t\twhile(m->chg1 && m->chg2 &&\n-\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n+\t\t      recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t\tm->i1++;\n \t\t\tm->i2++;\n \t\t}\n \t\twhile (m->chg1 && m->chg2 &&\n-\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n-\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n+\t\t       recmatch(&rec1[m->i1 + m->chg1 - 1],\n+\t\t\t\t&rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t}\n@@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t * This probably does not work outside git, since\n \t\t * we have a very simple mmfile structure.\n \t\t */\n-\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;\n-\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr\n-\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;\n-\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;\n-\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr\n-\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;\n+\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n+\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n+\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n+\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n+\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n+\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n \t\tif (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n \t\t\treturn -1;\n \t\tif (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||\n@@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)\n static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n {\n \tfor (; chg; chg--, i++)\n-\t\tif (line_contains_alnum(xe->xdf2.recs[i]->ptr,\n-\t\t\t\txe->xdf2.recs[i]->size))\n+\t\tif (line_contains_alnum(xe->xdf2.recs[i].ptr,\n+\t\t\t\txe->xdf2.recs[i].size))\n \t\t\treturn 1;\n \treturn 0;\n }\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 77dc411d19..bf69a58527 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)\n static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\t\t  int pass)\n {\n-\txrecord_t **records = pass == 1 ?\n+\txrecord_t *records = pass == 1 ?\n \t\tmap->env->xdf1.recs : map->env->xdf2.recs;\n-\txrecord_t *record = records[line - 1];\n+\txrecord_t *record = &records[line - 1];\n \t/*\n \t * After xdl_prepare_env() (or more precisely, due to\n \t * xdl_classify_record()), the \"ha\" member of the records (AKA lines)\n@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\treturn;\n \tmap->entries[index].line1 = line;\n \tmap->entries[index].hash = record->ha;\n-\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);\n+\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n \tif (!map->first)\n \t\tmap->first = map->entries + index;\n \tif (map->last) {\n@@ -246,8 +246,8 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)\n \n static int match(struct hashmap *map, int line1, int line2)\n {\n-\txrecord_t *record1 = map->env->xdf1.recs[line1 - 1];\n-\txrecord_t *record2 = map->env->xdf2.recs[line2 - 1];\n+\txrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n+\txrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n \treturn record1->ha == record2->ha;\n }\n \ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e6e2c0e1c0..27c5a4d636 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -128,7 +128,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n }\n \n \n@@ -143,8 +142,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n \n-\tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n-\t\tgoto abort;\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n@@ -155,12 +152,10 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n \t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n-\t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n-\t\t\t\tgoto abort;\n+\t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\txdf->recs[xdf->nrec++] = crec;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n@@ -260,7 +255,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, nm, nreff, mlim;\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \txdlclass_t *rcrec;\n \tchar *dis, *dis1, *dis2;\n \tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n@@ -273,7 +268,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n \t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -281,7 +276,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n \t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -317,13 +312,13 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n  */\n static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, lim;\n-\txrecord_t **recs1, **recs2;\n+\txrecord_t *recs1, *recs2;\n \n \trecs1 = xdf1->recs;\n \trecs2 = xdf2->recs;\n \tfor (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n \t     i++, recs1++, recs2++)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dstart = xdf2->dstart = i;\n@@ -331,7 +326,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \trecs1 = xdf1->recs + xdf1->nrec - 1;\n \trecs2 = xdf2->recs + xdf2->nrec - 1;\n \tfor (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dend = xdf1->nrec - i - 1;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 85848f1685..3d26cbf1ec 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -45,10 +45,9 @@ typedef struct s_xrecord {\n } xrecord_t;\n \n typedef struct s_xdfile {\n-\tchastore_t rcha;\n+\txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\txrecord_t **recs;\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 444a108f87..332982b509 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tmmfile_t subfile1, subfile2;\n \txdfenv_t env;\n \n-\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;\n-\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +\n-\t\tdiff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;\n-\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;\n-\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +\n-\t\tdiff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;\n+\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n+\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n+\t\tdiff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n+\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n+\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n+\t\tdiff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-- \ngitgitgadget\n\n"},{"id":"527153","messageId":"7b0856108a29f42e5a4d3faf7489d5522b683f71.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:27Z","receivedAt":"2025-09-23T21:24:48Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex a66125d44a..83c4cff6f7 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -932,16 +932,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n \n int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n \txdchange_t *cscr = NULL, *xch;\n-\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n \tlong i1, i2, l1, l2;\n \n \t/*\n \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n \t */\n \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n-\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n-\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n-\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n+\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n+\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n+\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n \n \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n \t\t\t\txdl_free_script(cscr);\n-- \ngitgitgadget\n\n"},{"id":"527154","messageId":"570ab9f8983d7507cd8f1937176f3268255da416.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 11/13] xdiff: rename rchg -> changed in xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:28Z","receivedAt":"2025-09-23T21:24:50Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 28 ++++++++++++++--------------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 12 ++++++------\n xdiff/xtypes.h     |  2 +-\n xdiff/xutils.c     |  4 ++--\n 6 files changed, 31 insertions(+), 31 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 83c4cff6f7..5535452061 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n+\t\t\txdf2->changed[xdf2->rindex[off2]] = 1;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n+\t\t\txdf1->changed[xdf1->rindex[off1]] = 1;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -708,7 +708,7 @@ struct xdlgroup {\n static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tg->start = g->end = 0;\n-\twhile (xdf->rchg[g->end])\n+\twhile (xdf->changed[g->end])\n \t\tg->end++;\n }\n \n@@ -722,7 +722,7 @@ static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->start = g->end + 1;\n-\tfor (g->end = g->start; xdf->rchg[g->end]; g->end++)\n+\tfor (g->end = g->start; xdf->changed[g->end]; g->end++)\n \t\t;\n \n \treturn 0;\n@@ -738,7 +738,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->end = g->start - 1;\n-\tfor (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n+\tfor (g->start = g->end; xdf->changed[g->start - 1]; g->start--)\n \t\t;\n \n \treturn 0;\n@@ -753,10 +753,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->rchg[g->start++] = 0;\n-\t\txdf->rchg[g->end++] = 1;\n+\t\txdf->changed[g->start++] = 0;\n+\t\txdf->changed[g->end++] = 1;\n \n-\t\twhile (xdf->rchg[g->end])\n+\t\twhile (xdf->changed[g->end])\n \t\t\tg->end++;\n \n \t\treturn 0;\n@@ -774,10 +774,10 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->rchg[--g->start] = 1;\n-\t\txdf->rchg[--g->end] = 0;\n+\t\txdf->changed[--g->start] = 1;\n+\t\txdf->changed[--g->end] = 0;\n \n-\t\twhile (xdf->rchg[g->start - 1])\n+\t\twhile (xdf->changed[g->start - 1])\n \t\t\tg->start--;\n \n \t\treturn 0;\n@@ -938,9 +938,9 @@ int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n \t */\n \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n-\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n-\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n-\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n+\t\tif (xe->xdf1.changed[i1 - 1] || xe->xdf2.changed[i2 - 1]) {\n+\t\t\tfor (l1 = i1; xe->xdf1.changed[i1 - 1]; i1--);\n+\t\t\tfor (l2 = i2; xe->xdf2.changed[i2 - 1]; i2--);\n \n \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n \t\t\t\txdl_free_script(cscr);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 4d857e8ae2..15ca15f6b0 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex bf69a58527..14092ffb86 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 27c5a4d636..b9b19c36de 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -126,7 +126,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n static void xdl_free_ctx(xdfile_t *xdf)\n {\n \txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->changed - 1);\n \txdl_free(xdf->recs);\n }\n \n@@ -139,7 +139,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txrecord_t *crec;\n \n \txdf->rindex = NULL;\n-\txdf->rchg = NULL;\n+\txdf->changed = NULL;\n \txdf->recs = NULL;\n \n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n@@ -161,7 +161,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->changed, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n@@ -170,7 +170,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tgoto abort;\n \t}\n \n-\txdf->rchg += 1;\n+\txdf->changed += 1;\n \txdf->nreff = 0;\n \txdf->dstart = 0;\n \txdf->dend = xdf->nrec - 1;\n@@ -287,7 +287,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf1->rchg[i] = 1;\n+\t\t\txdf1->changed[i] = 1;\n \t}\n \txdf1->nreff = nreff;\n \n@@ -297,7 +297,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf2->rchg[i] = 1;\n+\t\t\txdf2->changed[i] = 1;\n \t}\n \txdf2->nreff = nreff;\n \ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 3d26cbf1ec..c4b5d2d8fa 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -48,7 +48,7 @@ typedef struct s_xdfile {\n \txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\tchar *rchg;\n+\tchar *changed;\n \tlong *rindex;\n \tlong nreff;\n } xdfile_t;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 332982b509..ed65c222e6 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -425,8 +425,8 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-\tmemcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);\n-\tmemcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);\n+\tmemcpy(diff_env->xdf1.changed + line1 - 1, env.xdf1.changed, count1);\n+\tmemcpy(diff_env->xdf2.changed + line2 - 1, env.xdf2.changed, count2);\n \n \txdl_free_env(&env);\n \n-- \ngitgitgadget\n\n"},{"id":"527155","messageId":"08a0fceb72b2bd0a2803d24b9874b7f9bd03703a.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 12/13] xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:29Z","receivedAt":"2025-09-23T21:24:52Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nRename dis1, dis2 to matches1, matches2.\n\nDefine macros NONE(0), SOME(1), TOO_MANY(2) as the enum values for\nmatches1 and matches2. These states will influence whether changed[i]\nis set to 1 or kept as 0.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 90 ++++++++++++++++++++++++++++++++----------------\n 1 file changed, 60 insertions(+), 30 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex b9b19c36de..e1d575f779 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -29,6 +29,9 @@\n #define XDL_GUESS_NLINES1 256\n #define XDL_GUESS_NLINES2 20\n \n+#define NONE 0\n+#define SOME 1\n+#define TOO_MANY 2\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n@@ -190,12 +193,12 @@ void xdl_free_env(xdfenv_t *xe) {\n }\n \n \n-static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n+static bool xdl_clean_mmatch(uint8_t const *matches, long i, long s, long e) {\n \tlong r, rdis0, rpdis0, rdis1, rpdis1;\n \n \t/*\n-\t * Limits the window the is examined during the similar-lines\n-\t * scan. The loops below stops when dis[i - r] == 1 (line that\n+\t * Limits the window that is examined during the similar-lines\n+\t * scan. The loops below stops when matches[i - r] == SOME (line that\n \t * has no match), but there are corner cases where the loop\n \t * proceed all the way to the extremities by causing huge\n \t * performance penalties in case of big files.\n@@ -207,40 +210,44 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \n \t/*\n \t * Scans the lines before 'i' to find a run of lines that either\n-\t * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).\n-\t * Note that we always call this function with dis[i] > 1, so the\n+\t * have no match (matches[j] == NONE) or have multiple matches (matches[j] == TOO_MANY).\n+\t * Note that we always call this function with matches[i] == TOO_MANY, so the\n \t * current line (i) is already a multimatch line.\n \t */\n \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n-\t\tif (!dis[i - r])\n+\t\tif (matches[i - r] == NONE)\n \t\t\trdis0++;\n-\t\telse if (dis[i - r] == 2)\n+\t\telse if (matches[i - r] == TOO_MANY)\n \t\t\trpdis0++;\n-\t\telse\n+\t\telse if (matches[i - r] == SOME)\n \t\t\tbreak;\n+\t\telse\n+\t\t\tBUG(\"Illegal value for matches[i - r]\");\n \t}\n \t/*\n \t * If the run before the line 'i' found only multimatch lines, we\n-\t * return 0 and hence we don't make the current line (i) discarded.\n+\t * return false and hence we don't make the current line (i) discarded.\n \t * We want to discard multimatch lines only when they appear in the\n-\t * middle of runs with nomatch lines (dis[j] == 0).\n+\t * middle of runs with nomatch lines (matches[j] == NONE).\n \t */\n \tif (rdis0 == 0)\n \t\treturn 0;\n \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n-\t\tif (!dis[i + r])\n+\t\tif (matches[i + r] == NONE)\n \t\t\trdis1++;\n-\t\telse if (dis[i + r] == 2)\n+\t\telse if (matches[i + r] == TOO_MANY)\n \t\t\trpdis1++;\n-\t\telse\n+\t\telse if (matches[i + r] == SOME)\n \t\t\tbreak;\n+\t\telse\n+\t\t\tBUG(\"Illegal value for matches[i + r]\");\n \t}\n \t/*\n \t * If the run after the line 'i' found only multimatch lines, we\n-\t * return 0 and hence we don't make the current line (i) discarded.\n+\t * return false and hence we don't make the current line (i) discarded.\n \t */\n \tif (rdis1 == 0)\n-\t\treturn 0;\n+\t\treturn false;\n \trdis1 += rdis0;\n \trpdis1 += rpdis0;\n \n@@ -251,26 +258,41 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n /*\n  * Try to reduce the problem complexity, discard records that have no\n  * matches on the other file. Also, lines that have multiple matches\n- * might be potentially discarded if they happear in a run of discardable.\n+ * might be potentially discarded if they appear in a run of discardable.\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, nm, nreff, mlim;\n \txrecord_t *recs;\n \txdlclass_t *rcrec;\n-\tchar *dis, *dis1, *dis2;\n-\tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n+\tuint8_t *matches1, *matches2;\n+\tint status = 0;\n+\tbool need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n \n-\tif (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))\n-\t\treturn -1;\n-\tdis1 = dis;\n-\tdis2 = dis1 + xdf1->nrec + 1;\n+\tmatches1 = NULL;\n+\tmatches2 = NULL;\n+\n+\t/*\n+\t * Create temporary arrays that will help us decide if\n+\t * changed[i] should remain 0 or become 1.\n+\t */\n+\tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n+\t\tstatus = -1;\n+\t\tgoto cleanup;\n+\t}\n+\tif (!XDL_CALLOC_ARRAY(matches2, xdf2->nrec + 1)) {\n+\t\tstatus = -1;\n+\t\tgoto cleanup;\n+\t}\n \n+\t/*\n+\t * Initialize temporary arrays with NONE, SOME, or TOO_MANY.\n+\t */\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n-\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tmatches1[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\n \t}\n \n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n@@ -278,14 +300,19 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n-\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\tmatches2[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\n \t}\n \n+\t/*\n+\t * Use temporary arrays to decide if changed[i] should remain\n+\t * 0 or become 1.\n+\t */\n \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n-\t\tif (dis1[i] == 1 ||\n-\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n+\t\tif (matches1[i] == SOME ||\n+\t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n+\t\t\t/* changed[i] remains 0 */\n \t\t} else\n \t\t\txdf1->changed[i] = 1;\n \t}\n@@ -293,17 +320,20 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \n \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n \t     i <= xdf2->dend; i++, recs++) {\n-\t\tif (dis2[i] == 1 ||\n-\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n+\t\tif (matches2[i] == SOME ||\n+\t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n+\t\t\t/* changed[i] remains 0 */\n \t\t} else\n \t\t\txdf2->changed[i] = 1;\n \t}\n \txdf2->nreff = nreff;\n \n-\txdl_free(dis);\n+cleanup:\n+\txdl_free(matches1);\n+\txdl_free(matches2);\n \n-\treturn 0;\n+\treturn status;\n }\n \n \n-- \ngitgitgadget\n\n"},{"id":"527156","messageId":"975e845bfa80fe838433f7f64759a72f6d565aea.1758662670.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v5 13/13] xdiff: change type of xdfile_t.changed from char to bool","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-23T21:24:30Z","receivedAt":"2025-09-23T21:24:53Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe only values possible for 'changed' is 1 and 0, which exactly maps\nto a bool type. It might not look like this is the case because\nmatches1 and matches2 (which use to be dis1, and dis2) were also char\nand were assigned numerical values within a few lines of 'changed'\n(what used to be rchg).\n\nUsing NONE, SOME, TOO_MANY for matches1[i]/matches2[j], and true/false\nfor changed[k] makes it clear to future readers that these are\nlogically separate concepts.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 12 ++++++------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 12 ++++++------\n xdiff/xtypes.h     |  2 +-\n 5 files changed, 21 insertions(+), 21 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 5535452061..b902be9d0e 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->changed[xdf2->rindex[off2]] = 1;\n+\t\t\txdf2->changed[xdf2->rindex[off2]] = true;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->changed[xdf1->rindex[off1]] = 1;\n+\t\t\txdf1->changed[xdf1->rindex[off1]] = true;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -753,8 +753,8 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->changed[g->start++] = 0;\n-\t\txdf->changed[g->end++] = 1;\n+\t\txdf->changed[g->start++] = false;\n+\t\txdf->changed[g->end++] = true;\n \n \t\twhile (xdf->changed[g->end])\n \t\t\tg->end++;\n@@ -774,8 +774,8 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->changed[--g->start] = 1;\n-\t\txdf->changed[--g->end] = 0;\n+\t\txdf->changed[--g->start] = true;\n+\t\txdf->changed[--g->end] = false;\n \n \t\twhile (xdf->changed[g->start - 1])\n \t\t\tg->start--;\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 15ca15f6b0..6dc450b1fe 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n+\t\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n+\t\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 14092ffb86..669b653580 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e1d575f779..070d220f3b 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -273,7 +273,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \n \t/*\n \t * Create temporary arrays that will help us decide if\n-\t * changed[i] should remain 0 or become 1.\n+\t * changed[i] should remain false, or become true.\n \t */\n \tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n \t\tstatus = -1;\n@@ -305,16 +305,16 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \n \t/*\n \t * Use temporary arrays to decide if changed[i] should remain\n-\t * 0 or become 1.\n+\t * false, or become true.\n \t */\n \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (matches1[i] == SOME ||\n \t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n-\t\t\t/* changed[i] remains 0 */\n+\t\t\t/* changed[i] remains false */\n \t\t} else\n-\t\t\txdf1->changed[i] = 1;\n+\t\t\txdf1->changed[i] = true;\n \t}\n \txdf1->nreff = nreff;\n \n@@ -323,9 +323,9 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\tif (matches2[i] == SOME ||\n \t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n-\t\t\t/* changed[i] remains 0 */\n+\t\t\t/* changed[i] remains false */\n \t\t} else\n-\t\t\txdf2->changed[i] = 1;\n+\t\t\txdf2->changed[i] = true;\n \t}\n \txdf2->nreff = nreff;\n \ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex c4b5d2d8fa..f145abba3e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -48,7 +48,7 @@ typedef struct s_xdfile {\n \txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\tchar *changed;\n+\tbool *changed;\n \tlong *rindex;\n \tlong nreff;\n } xdfile_t;\n-- \ngitgitgadget\n"},{"id":"527199","messageId":"a17e00fa-bf4b-4b53-817b-297236178101@gmail.com","threadId":"64108","inReplyTo":"975e845bfa80fe838433f7f64759a72f6d565aea.1758662670.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 13/13] xdiff: change type of xdfile_t.changed from char to bool","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-24T10:21:46Z","receivedAt":"2025-09-24T10:21:23Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> \n> The only values possible for 'changed' is 1 and 0, which exactly maps\n> to a bool type. It might not look like this is the case because\n> matches1 and matches2 (which use to be dis1, and dis2) were also char\n> and were assigned numerical values within a few lines of 'changed'\n> (what used to be rchg).\n> \n> Using NONE, SOME, TOO_MANY for matches1[i]/matches2[j], and true/false\n> for changed[k] makes it clear to future readers that these are\n> logically separate concepts.\n\nNicely explained - I think this change is a very good idea and \nseparating it out like this makes it much clearer what's going on \ncompared to V4.\n\nThanks\n\nPhillip\n\n> Best-viewed-with: --color-words\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>   xdiff/xdiffi.c     | 12 ++++++------\n>   xdiff/xhistogram.c |  8 ++++----\n>   xdiff/xpatience.c  |  8 ++++----\n>   xdiff/xprepare.c   | 12 ++++++------\n>   xdiff/xtypes.h     |  2 +-\n>   5 files changed, 21 insertions(+), 21 deletions(-)\n> \n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index 5535452061..b902be9d0e 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n>   \t */\n>   \tif (off1 == lim1) {\n>   \t\tfor (; off2 < lim2; off2++)\n> -\t\t\txdf2->changed[xdf2->rindex[off2]] = 1;\n> +\t\t\txdf2->changed[xdf2->rindex[off2]] = true;\n>   \t} else if (off2 == lim2) {\n>   \t\tfor (; off1 < lim1; off1++)\n> -\t\t\txdf1->changed[xdf1->rindex[off1]] = 1;\n> +\t\t\txdf1->changed[xdf1->rindex[off1]] = true;\n>   \t} else {\n>   \t\txdpsplit_t spl;\n>   \t\tspl.i1 = spl.i2 = 0;\n> @@ -753,8 +753,8 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n>   {\n>   \tif (g->end < xdf->nrec &&\n>   \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n> -\t\txdf->changed[g->start++] = 0;\n> -\t\txdf->changed[g->end++] = 1;\n> +\t\txdf->changed[g->start++] = false;\n> +\t\txdf->changed[g->end++] = true;\n>   \n>   \t\twhile (xdf->changed[g->end])\n>   \t\t\tg->end++;\n> @@ -774,8 +774,8 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n>   {\n>   \tif (g->start > 0 &&\n>   \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n> -\t\txdf->changed[--g->start] = 1;\n> -\t\txdf->changed[--g->end] = 0;\n> +\t\txdf->changed[--g->start] = true;\n> +\t\txdf->changed[--g->end] = false;\n>   \n>   \t\twhile (xdf->changed[g->start - 1])\n>   \t\t\tg->start--;\n> diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\n> index 15ca15f6b0..6dc450b1fe 100644\n> --- a/xdiff/xhistogram.c\n> +++ b/xdiff/xhistogram.c\n> @@ -318,11 +318,11 @@ redo:\n>   \n>   \tif (!count1) {\n>   \t\twhile(count2--)\n> -\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n> +\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n>   \t\treturn 0;\n>   \t} else if (!count2) {\n>   \t\twhile(count1--)\n> -\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n> +\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n>   \t\treturn 0;\n>   \t}\n>   \n> @@ -335,9 +335,9 @@ redo:\n>   \telse {\n>   \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n>   \t\t\twhile (count1--)\n> -\t\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n> +\t\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n>   \t\t\twhile (count2--)\n> -\t\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n> +\t\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n>   \t\t\tresult = 0;\n>   \t\t} else {\n>   \t\t\tresult = histogram_diff(xpp, env,\n> diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\n> index 14092ffb86..669b653580 100644\n> --- a/xdiff/xpatience.c\n> +++ b/xdiff/xpatience.c\n> @@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n>   \t/* trivial case: one side is empty */\n>   \tif (!count1) {\n>   \t\twhile(count2--)\n> -\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n> +\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n>   \t\treturn 0;\n>   \t} else if (!count2) {\n>   \t\twhile(count1--)\n> -\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n> +\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n>   \t\treturn 0;\n>   \t}\n>   \n> @@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n>   \t/* are there any matching lines at all? */\n>   \tif (!map.has_matches) {\n>   \t\twhile(count1--)\n> -\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n> +\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n>   \t\twhile(count2--)\n> -\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n> +\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n>   \t\txdl_free(map.entries);\n>   \t\treturn 0;\n>   \t}\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index e1d575f779..070d220f3b 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -273,7 +273,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>   \n>   \t/*\n>   \t * Create temporary arrays that will help us decide if\n> -\t * changed[i] should remain 0 or become 1.\n> +\t * changed[i] should remain false, or become true.\n>   \t */\n>   \tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n>   \t\tstatus = -1;\n> @@ -305,16 +305,16 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>   \n>   \t/*\n>   \t * Use temporary arrays to decide if changed[i] should remain\n> -\t * 0 or become 1.\n> +\t * false, or become true.\n>   \t */\n>   \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n>   \t     i <= xdf1->dend; i++, recs++) {\n>   \t\tif (matches1[i] == SOME ||\n>   \t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n>   \t\t\txdf1->rindex[nreff++] = i;\n> -\t\t\t/* changed[i] remains 0 */\n> +\t\t\t/* changed[i] remains false */\n>   \t\t} else\n> -\t\t\txdf1->changed[i] = 1;\n> +\t\t\txdf1->changed[i] = true;\n>   \t}\n>   \txdf1->nreff = nreff;\n>   \n> @@ -323,9 +323,9 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>   \t\tif (matches2[i] == SOME ||\n>   \t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n>   \t\t\txdf2->rindex[nreff++] = i;\n> -\t\t\t/* changed[i] remains 0 */\n> +\t\t\t/* changed[i] remains false */\n>   \t\t} else\n> -\t\t\txdf2->changed[i] = 1;\n> +\t\t\txdf2->changed[i] = true;\n>   \t}\n>   \txdf2->nreff = nreff;\n>   \n> diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\n> index c4b5d2d8fa..f145abba3e 100644\n> --- a/xdiff/xtypes.h\n> +++ b/xdiff/xtypes.h\n> @@ -48,7 +48,7 @@ typedef struct s_xdfile {\n>   \txrecord_t *recs;\n>   \tlong nrec;\n>   \tlong dstart, dend;\n> -\tchar *changed;\n> +\tbool *changed;\n>   \tlong *rindex;\n>   \tlong nreff;\n>   } xdfile_t;\n\n"},{"id":"527200","messageId":"311f7dbe-b970-4a5b-9d53-05f019cc54cd@gmail.com","threadId":"64108","inReplyTo":"08a0fceb72b2bd0a2803d24b9874b7f9bd03703a.1758662670.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 12/13] xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-24T10:21:54Z","receivedAt":"2025-09-24T10:21:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> \n> Rename dis1, dis2 to matches1, matches2.\n> \n> Define macros NONE(0), SOME(1), TOO_MANY(2) as the enum values for\n> matches1 and matches2. These states will influence whether changed[i]\n> is set to 1 or kept as 0.\n\nThis message also says what is being changed rather than why it is being \nchanged. I think the rename here is a good idea but I'm not sure what \n\"rdis[01]\" and \"rpdis[01]\" are used for and whether they should be \nrenamed if we're renaming \"dis[01]\"\n\n>   \t/*\n> -\t * Limits the window the is examined during the similar-lines\n> -\t * scan. The loops below stops when dis[i - r] == 1 (line that\n> +\t * Limits the window that is examined during the similar-lines\n> +\t * scan. The loops below stops when matches[i - r] == SOME (line that\n\nThanks for updating the comments. Not reflowing the lines makes the diff \neasier to read but leaves the comments in a rather strange state with \nrandom long lines.\n\n>   \t * has no match), but there are corner cases where the loop\n>   \t * proceed all the way to the extremities by causing huge\n>   \t * performance penalties in case of big files.\n> @@ -207,40 +210,44 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n>   \n>   \t/*\n>   \t * Scans the lines before 'i' to find a run of lines that either\n> -\t * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).\n> -\t * Note that we always call this function with dis[i] > 1, so the\n> +\t * have no match (matches[j] == NONE) or have multiple matches (matches[j] == TOO_MANY).\n> +\t * Note that we always call this function with matches[i] == TOO_MANY, so the\n\nespecially here\n\n> -\t\tif (!dis[i + r])\n> +\t\tif (matches[i + r] == NONE)\n>   \t\t\trdis1++;\n> -\t\telse if (dis[i + r] == 2)\n> +\t\telse if (matches[i + r] == TOO_MANY)\n>   \t\t\trpdis1++;\n> -\t\telse\n> +\t\telse if (matches[i + r] == SOME)\n>   \t\t\tbreak;\n> +\t\telse\n> +\t\t\tBUG(\"Illegal value for matches[i + r]\");\n\nNice addition\n\n>   static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n>   \tlong i, nm, nreff, mlim;\n>   \txrecord_t *recs;\n>   \txdlclass_t *rcrec;\n> -\tchar *dis, *dis1, *dis2;\n> -\tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n> +\tuint8_t *matches1, *matches2;\n\nLet's initialize these where they're declared rather than later on\n\n> +\tint status = 0;\nI think we typically we call this \"ret\" or \"res\" in the rest of the code \nbase.\n\n> +\tbool need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n\nNice use of bool, strictly speaking I don't think we need the !! if \nwe're changing the type from int. I think Junio recently suggested that \nwe might start using (bool) instead of !! for cases like this.\n\nEverything below this looks good, though some of the lines are getting \nquite long with the renamed variables and symbolic values so we might \nwant to break them.\n\nThanks\n\nPhillip\n\n\n> -\tif (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))\n> -\t\treturn -1;\n> -\tdis1 = dis;\n> -\tdis2 = dis1 + xdf1->nrec + 1;\n> +\tmatches1 = NULL;\n> +\tmatches2 = NULL;\n> +\n> +\t/*\n> +\t * Create temporary arrays that will help us decide if\n> +\t * changed[i] should remain 0 or become 1.\n> +\t */\n> +\tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n> +\t\tstatus = -1;\n> +\t\tgoto cleanup;\n> +\t}\n> +\tif (!XDL_CALLOC_ARRAY(matches2, xdf2->nrec + 1)) {\n> +\t\tstatus = -1;\n> +\t\tgoto cleanup;\n> +\t}\n>   \n> +\t/*\n> +\t * Initialize temporary arrays with NONE, SOME, or TOO_MANY.\n> +\t */\n>   \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n>   \t\tmlim = XDL_MAX_EQLIMIT;\n>   \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n>   \t\trcrec = cf->rcrecs[recs->ha];\n>   \t\tnm = rcrec ? rcrec->len2 : 0;\n> -\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n> +\t\tmatches1[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\n>   \t}\n>   \n>   \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n> @@ -278,14 +300,19 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>   \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n>   \t\trcrec = cf->rcrecs[recs->ha];\n>   \t\tnm = rcrec ? rcrec->len1 : 0;\n> -\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n> +\t\tmatches2[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\n>   \t}\n>   \n> +\t/*\n> +\t * Use temporary arrays to decide if changed[i] should remain\n> +\t * 0 or become 1.\n> +\t */\n>   \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n>   \t     i <= xdf1->dend; i++, recs++) {\n> -\t\tif (dis1[i] == 1 ||\n> -\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n> +\t\tif (matches1[i] == SOME ||\n> +\t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n>   \t\t\txdf1->rindex[nreff++] = i;\n> +\t\t\t/* changed[i] remains 0 */\n>   \t\t} else\n>   \t\t\txdf1->changed[i] = 1;\n>   \t}\n> @@ -293,17 +320,20 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>   \n>   \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n>   \t     i <= xdf2->dend; i++, recs++) {\n> -\t\tif (dis2[i] == 1 ||\n> -\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n> +\t\tif (matches2[i] == SOME ||\n> +\t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n>   \t\t\txdf2->rindex[nreff++] = i;\n> +\t\t\t/* changed[i] remains 0 */\n>   \t\t} else\n>   \t\t\txdf2->changed[i] = 1;\n>   \t}\n>   \txdf2->nreff = nreff;\n>   \n> -\txdl_free(dis);\n> +cleanup:\n> +\txdl_free(matches1);\n> +\txdl_free(matches2);\n>   \n> -\treturn 0;\n> +\treturn status;\n>   }\n>   \n>   \n"},{"id":"527201","messageId":"82ee55d6-f8ae-4a88-84fc-47c6ce18139a@gmail.com","threadId":"64108","inReplyTo":"570ab9f8983d7507cd8f1937176f3268255da416.1758662670.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 11/13] xdiff: rename rchg -> changed in xdfile_t","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-24T10:22:03Z","receivedAt":"2025-09-24T10:21:40Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nI agree \"changed\" is a better name but the commit message should explain \nwhat \"rchg\" is used for so that someone who is not familiar with the \ncode can understand why the change in name is desirable.\n\nThanks\n\nPhillip\n> Best-viewed-with: --color-words\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>   xdiff/xdiffi.c     | 28 ++++++++++++++--------------\n>   xdiff/xhistogram.c |  8 ++++----\n>   xdiff/xpatience.c  |  8 ++++----\n>   xdiff/xprepare.c   | 12 ++++++------\n>   xdiff/xtypes.h     |  2 +-\n>   xdiff/xutils.c     |  4 ++--\n>   6 files changed, 31 insertions(+), 31 deletions(-)\n> \n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index 83c4cff6f7..5535452061 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n>   \t */\n>   \tif (off1 == lim1) {\n>   \t\tfor (; off2 < lim2; off2++)\n> -\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n> +\t\t\txdf2->changed[xdf2->rindex[off2]] = 1;\n>   \t} else if (off2 == lim2) {\n>   \t\tfor (; off1 < lim1; off1++)\n> -\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n> +\t\t\txdf1->changed[xdf1->rindex[off1]] = 1;\n>   \t} else {\n>   \t\txdpsplit_t spl;\n>   \t\tspl.i1 = spl.i2 = 0;\n> @@ -708,7 +708,7 @@ struct xdlgroup {\n>   static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n>   {\n>   \tg->start = g->end = 0;\n> -\twhile (xdf->rchg[g->end])\n> +\twhile (xdf->changed[g->end])\n>   \t\tg->end++;\n>   }\n>   \n> @@ -722,7 +722,7 @@ static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n>   \t\treturn -1;\n>   \n>   \tg->start = g->end + 1;\n> -\tfor (g->end = g->start; xdf->rchg[g->end]; g->end++)\n> +\tfor (g->end = g->start; xdf->changed[g->end]; g->end++)\n>   \t\t;\n>   \n>   \treturn 0;\n> @@ -738,7 +738,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n>   \t\treturn -1;\n>   \n>   \tg->end = g->start - 1;\n> -\tfor (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n> +\tfor (g->start = g->end; xdf->changed[g->start - 1]; g->start--)\n>   \t\t;\n>   \n>   \treturn 0;\n> @@ -753,10 +753,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n>   {\n>   \tif (g->end < xdf->nrec &&\n>   \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n> -\t\txdf->rchg[g->start++] = 0;\n> -\t\txdf->rchg[g->end++] = 1;\n> +\t\txdf->changed[g->start++] = 0;\n> +\t\txdf->changed[g->end++] = 1;\n>   \n> -\t\twhile (xdf->rchg[g->end])\n> +\t\twhile (xdf->changed[g->end])\n>   \t\t\tg->end++;\n>   \n>   \t\treturn 0;\n> @@ -774,10 +774,10 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n>   {\n>   \tif (g->start > 0 &&\n>   \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n> -\t\txdf->rchg[--g->start] = 1;\n> -\t\txdf->rchg[--g->end] = 0;\n> +\t\txdf->changed[--g->start] = 1;\n> +\t\txdf->changed[--g->end] = 0;\n>   \n> -\t\twhile (xdf->rchg[g->start - 1])\n> +\t\twhile (xdf->changed[g->start - 1])\n>   \t\t\tg->start--;\n>   \n>   \t\treturn 0;\n> @@ -938,9 +938,9 @@ int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n>   \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n>   \t */\n>   \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n> -\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n> -\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n> -\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n> +\t\tif (xe->xdf1.changed[i1 - 1] || xe->xdf2.changed[i2 - 1]) {\n> +\t\t\tfor (l1 = i1; xe->xdf1.changed[i1 - 1]; i1--);\n> +\t\t\tfor (l2 = i2; xe->xdf2.changed[i2 - 1]; i2--);\n>   \n>   \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n>   \t\t\t\txdl_free_script(cscr);\n> diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\n> index 4d857e8ae2..15ca15f6b0 100644\n> --- a/xdiff/xhistogram.c\n> +++ b/xdiff/xhistogram.c\n> @@ -318,11 +318,11 @@ redo:\n>   \n>   \tif (!count1) {\n>   \t\twhile(count2--)\n> -\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n> +\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n>   \t\treturn 0;\n>   \t} else if (!count2) {\n>   \t\twhile(count1--)\n> -\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n> +\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n>   \t\treturn 0;\n>   \t}\n>   \n> @@ -335,9 +335,9 @@ redo:\n>   \telse {\n>   \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n>   \t\t\twhile (count1--)\n> -\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n> +\t\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n>   \t\t\twhile (count2--)\n> -\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n> +\t\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n>   \t\t\tresult = 0;\n>   \t\t} else {\n>   \t\t\tresult = histogram_diff(xpp, env,\n> diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\n> index bf69a58527..14092ffb86 100644\n> --- a/xdiff/xpatience.c\n> +++ b/xdiff/xpatience.c\n> @@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n>   \t/* trivial case: one side is empty */\n>   \tif (!count1) {\n>   \t\twhile(count2--)\n> -\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n> +\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n>   \t\treturn 0;\n>   \t} else if (!count2) {\n>   \t\twhile(count1--)\n> -\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n> +\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n>   \t\treturn 0;\n>   \t}\n>   \n> @@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n>   \t/* are there any matching lines at all? */\n>   \tif (!map.has_matches) {\n>   \t\twhile(count1--)\n> -\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n> +\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n>   \t\twhile(count2--)\n> -\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n> +\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n>   \t\txdl_free(map.entries);\n>   \t\treturn 0;\n>   \t}\n> diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\n> index 27c5a4d636..b9b19c36de 100644\n> --- a/xdiff/xprepare.c\n> +++ b/xdiff/xprepare.c\n> @@ -126,7 +126,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n>   static void xdl_free_ctx(xdfile_t *xdf)\n>   {\n>   \txdl_free(xdf->rindex);\n> -\txdl_free(xdf->rchg - 1);\n> +\txdl_free(xdf->changed - 1);\n>   \txdl_free(xdf->recs);\n>   }\n>   \n> @@ -139,7 +139,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>   \txrecord_t *crec;\n>   \n>   \txdf->rindex = NULL;\n> -\txdf->rchg = NULL;\n> +\txdf->changed = NULL;\n>   \txdf->recs = NULL;\n>   \n>   \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n> @@ -161,7 +161,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>   \t\t}\n>   \t}\n>   \n> -\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n> +\tif (!XDL_CALLOC_ARRAY(xdf->changed, xdf->nrec + 2))\n>   \t\tgoto abort;\n>   \n>   \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n> @@ -170,7 +170,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n>   \t\t\tgoto abort;\n>   \t}\n>   \n> -\txdf->rchg += 1;\n> +\txdf->changed += 1;\n>   \txdf->nreff = 0;\n>   \txdf->dstart = 0;\n>   \txdf->dend = xdf->nrec - 1;\n> @@ -287,7 +287,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>   \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n>   \t\t\txdf1->rindex[nreff++] = i;\n>   \t\t} else\n> -\t\t\txdf1->rchg[i] = 1;\n> +\t\t\txdf1->changed[i] = 1;\n>   \t}\n>   \txdf1->nreff = nreff;\n>   \n> @@ -297,7 +297,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>   \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n>   \t\t\txdf2->rindex[nreff++] = i;\n>   \t\t} else\n> -\t\t\txdf2->rchg[i] = 1;\n> +\t\t\txdf2->changed[i] = 1;\n>   \t}\n>   \txdf2->nreff = nreff;\n>   \n> diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\n> index 3d26cbf1ec..c4b5d2d8fa 100644\n> --- a/xdiff/xtypes.h\n> +++ b/xdiff/xtypes.h\n> @@ -48,7 +48,7 @@ typedef struct s_xdfile {\n>   \txrecord_t *recs;\n>   \tlong nrec;\n>   \tlong dstart, dend;\n> -\tchar *rchg;\n> +\tchar *changed;\n>   \tlong *rindex;\n>   \tlong nreff;\n>   } xdfile_t;\n> diff --git a/xdiff/xutils.c b/xdiff/xutils.c\n> index 332982b509..ed65c222e6 100644\n> --- a/xdiff/xutils.c\n> +++ b/xdiff/xutils.c\n> @@ -425,8 +425,8 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n>   \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n>   \t\treturn -1;\n>   \n> -\tmemcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);\n> -\tmemcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);\n> +\tmemcpy(diff_env->xdf1.changed + line1 - 1, env.xdf1.changed, count1);\n> +\tmemcpy(diff_env->xdf2.changed + line2 - 1, env.xdf2.changed, count2);\n>   \n>   \txdl_free_env(&env);\n>   \n\n"},{"id":"527202","messageId":"0bb4f30b-6737-43a2-9aeb-3903c0dc5ce6@gmail.com","threadId":"64108","inReplyTo":"7b0856108a29f42e5a4d3faf7489d5522b683f71.1758662670.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-24T10:22:13Z","receivedAt":"2025-09-24T10:21:49Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe commit message should explain why this change is being made\n\nThanks\n\nPhillip\n\n> Best-viewed-with: --color-words\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>   xdiff/xdiffi.c | 7 +++----\n>   1 file changed, 3 insertions(+), 4 deletions(-)\n> \n> diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\n> index a66125d44a..83c4cff6f7 100644\n> --- a/xdiff/xdiffi.c\n> +++ b/xdiff/xdiffi.c\n> @@ -932,16 +932,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n>   \n>   int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n>   \txdchange_t *cscr = NULL, *xch;\n> -\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n>   \tlong i1, i2, l1, l2;\n>   \n>   \t/*\n>   \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n>   \t */\n>   \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n> -\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n> -\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n> -\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n> +\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n> +\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n> +\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n>   \n>   \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n>   \t\t\t\txdl_free_script(cscr);\n\n\n"},{"id":"527203","messageId":"3d03290e-bfad-41de-9735-91b126ae9e57@gmail.com","threadId":"64108","inReplyTo":"1d550cf308a189721ba089443f29678ead0344e5.1758662670.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 05/13] xdiff: delete superfluous local variables that alias fields in xrecord_t","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-24T10:22:21Z","receivedAt":"2025-09-24T10:21:57Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> \n> Use the type xrecord_t as the local variable for the functions in the\n> file xdiff/xemit.c.\n\nThis explains what the change is but not why it is being made. Commit \nmessages in this project are expected to explain the reason for the \nchange so that future readers can understand why a change was made.\n\nThanks\n\nPhillip\n\n> \n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n>   xdiff/xemit.c | 29 +++++++++++++----------------\n>   1 file changed, 13 insertions(+), 16 deletions(-)\n> \n> diff --git a/xdiff/xemit.c b/xdiff/xemit.c\n> index 40fc8154f3..2161ac3cd0 100644\n> --- a/xdiff/xemit.c\n> +++ b/xdiff/xemit.c\n> @@ -23,12 +23,11 @@\n>   #include \"xinclude.h\"\n>   \n>   \n> -static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n> -\tlong size, psize = strlen(pre);\n> -\tchar const *rec = xdf->recs[ri]->ptr;\n> +static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n> +{\n> +\txrecord_t *rec = xdf->recs[ri];\n>   \n> -\tsize = xdf->recs[ri]->size;\n> -\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0)\n> +\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n>   \t\treturn -1;\n>   \n>   \treturn 0;\n> @@ -111,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n>   static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n>   \t\t\t   char *buf, long sz)\n>   {\n> -\tconst char *rec = xdf->recs[ri]->ptr;\n> -\tlong len = xdf->recs[ri]->size;\n> +\txrecord_t *rec = xdf->recs[ri];\n> +\n>   \tif (!xecfg->find_func)\n> -\t\treturn def_ff(rec, len, buf, sz);\n> -\treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n> +\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n> +\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n>   }\n>   \n>   static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n> @@ -151,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n>   \n>   static int is_empty_rec(xdfile_t *xdf, long ri)\n>   {\n> -\tconst char *rec = xdf->recs[ri]->ptr;\n> -\tlong len = xdf->recs[ri]->size;\n> +\txrecord_t *rec = xdf->recs[ri];\n> +\tlong i = 0;\n>   \n> -\twhile (len > 0 && XDL_ISSPACE(*rec)) {\n> -\t\trec++;\n> -\t\tlen--;\n> -\t}\n> -\treturn !len;\n> +\tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n> +\n> +\treturn i == rec->size;\n>   }\n>   \n>   int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n\n"},{"id":"527216","messageId":"CAH=ZcbBcEbTqQ-FpYSdr_QjSM5sKerECKsSVdTyHoSQqG-V9iA@mail.gmail.com","threadId":"64108","inReplyTo":"311f7dbe-b970-4a5b-9d53-05f019cc54cd@gmail.com","subject":"Re: [PATCH v5 12/13] xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-24T14:46:31Z","receivedAt":"2025-09-24T14:46:44Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> > From: Ezekiel Newren <ezekielnewren@gmail.com>\n> >\n> > Rename dis1, dis2 to matches1, matches2.\n> >\n> > Define macros NONE(0), SOME(1), TOO_MANY(2) as the enum values for\n> > matches1 and matches2. These states will influence whether changed[i]\n> > is set to 1 or kept as 0.\n>\n> This message also says what is being changed rather than why it is being\n> changed. I think the rename here is a good idea but I'm not sure what\n> \"rdis[01]\" and \"rpdis[01]\" are used for and whether they should be\n> renamed if we're renaming \"dis[01]\"\n\n\"Rename dis1, dis2 to matches1, matches2 to give the variable names a\nmore obvious meaning.\"\n\nWould something like that work, or do I need to refine it further? I\nwould love to rename rdis, rpdis, etc... except that I don't\nunderstand what is happening or why. Could someone explain the purpose\nof these variables?\n\n> >       /*\n> > -      * Limits the window the is examined during the similar-lines\n> > -      * scan. The loops below stops when dis[i - r] == 1 (line that\n> > +      * Limits the window that is examined during the similar-lines\n> > +      * scan. The loops below stops when matches[i - r] == SOME (line that\n>\n> Thanks for updating the comments. Not reflowing the lines makes the diff\n> easier to read but leaves the comments in a rather strange state with\n> random long lines.\n\nWhat is the reflow limit for comments? 72? 80?\n\n> >        * has no match), but there are corner cases where the loop\n> >        * proceed all the way to the extremities by causing huge\n> >        * performance penalties in case of big files.\n> > @@ -207,40 +210,44 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n> >\n> >       /*\n> >        * Scans the lines before 'i' to find a run of lines that either\n> > -      * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).\n> > -      * Note that we always call this function with dis[i] > 1, so the\n> > +      * have no match (matches[j] == NONE) or have multiple matches (matches[j] == TOO_MANY).\n> > +      * Note that we always call this function with matches[i] == TOO_MANY, so the\n>\n> especially here\n>\n> > -             if (!dis[i + r])\n> > +             if (matches[i + r] == NONE)\n> >                       rdis1++;\n> > -             else if (dis[i + r] == 2)\n> > +             else if (matches[i + r] == TOO_MANY)\n> >                       rpdis1++;\n> > -             else\n> > +             else if (matches[i + r] == SOME)\n> >                       break;\n> > +             else\n> > +                     BUG(\"Illegal value for matches[i + r]\");\n>\n> Nice addition\n\nThanks.\n\n> >   static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n> >       long i, nm, nreff, mlim;\n> >       xrecord_t *recs;\n> >       xdlclass_t *rcrec;\n> > -     char *dis, *dis1, *dis2;\n> > -     int need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n> > +     uint8_t *matches1, *matches2;\n>\n> Let's initialize these where they're declared rather than later on\n\nI can do that.\n\n> > +     int status = 0;\n> I think we typically we call this \"ret\" or \"res\" in the rest of the code\n> base.\n>\n> > +     bool need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n>\n> Nice use of bool, strictly speaking I don't think we need the !! if\n> we're changing the type from int. I think Junio recently suggested that\n> we might start using (bool) instead of !! for cases like this.\n>\n> Everything below this looks good, though some of the lines are getting\n> quite long with the renamed variables and symbolic values so we might\n> want to break them.\n\nI didn't add !! and thought it looked funny myself. I didn't remove it\nbecause I wasn't sure if I should.\n"},{"id":"527217","messageId":"CAH=ZcbBtL5xVKhHGKeb2PkCjNE4dDEO_jc0XsxwR2-jnX-f8JA@mail.gmail.com","threadId":"64108","inReplyTo":"3d03290e-bfad-41de-9735-91b126ae9e57@gmail.com","subject":"Re: [PATCH v5 05/13] xdiff: delete superfluous local variables that alias fields in xrecord_t","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-24T14:52:10Z","receivedAt":"2025-09-24T14:52:23Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> > From: Ezekiel Newren <ezekielnewren@gmail.com>\n> >\n> > Use the type xrecord_t as the local variable for the functions in the\n> > file xdiff/xemit.c.\n>\n> This explains what the change is but not why it is being made. Commit\n> messages in this project are expected to explain the reason for the\n> change so that future readers can understand why a change was made.\n\n\"Use the type xrecord_t as the local variable for the functions in the\nfile xdiff/xemit.c. This helps tools like ctags or modern IDE's more\naccurately follow the usage of xrecord_t.\"\n\nIs this commit message good enough?\n"},{"id":"527218","messageId":"CAH=ZcbCf7bUTL=BwCD8B6-AVHZg+K3JafvksLQWUx2D-XR58wA@mail.gmail.com","threadId":"64108","inReplyTo":"0bb4f30b-6737-43a2-9aeb-3903c0dc5ce6@gmail.com","subject":"Re: [PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-24T15:01:17Z","receivedAt":"2025-09-24T15:01:31Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> > From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> The commit message should explain why this change is being made\n\nReasons to delete local variable aliasing:\n  * Usage tracking: Tools are better able to follow the usage.\n  * Refactor churn: Later commits will refactor rchg.\n  * No additional meaning: The local variables express the same meaning\n    as the struct field itself.\n\nWould that suffice?\n"},{"id":"527219","messageId":"CAH=ZcbA+MLbyNp3f7RbMFA6vrDZC6yRKVh7ryVer1Ozg3d4oSg@mail.gmail.com","threadId":"64108","inReplyTo":"82ee55d6-f8ae-4a88-84fc-47c6ce18139a@gmail.com","subject":"Re: [PATCH v5 11/13] xdiff: rename rchg -> changed in xdfile_t","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-24T15:10:49Z","receivedAt":"2025-09-24T15:11:03Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> > From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> I agree \"changed\" is a better name but the commit message should explain\n> what \"rchg\" is used for so that someone who is not familiar with the\n> code can understand why the change in name is desirable.\n\nThe field rchg (now 'changed') declares if a line in a file is changed\nor not. A later commit will change it's type from 'char' to 'bool'\nto make its purpose even more clear.\n\nSomething like that?\n"},{"id":"527220","messageId":"CAH=ZcbBzG+Ekzp-mE+MBDbU2PaP3qikyJJcgnh_G2+mOf67CWQ@mail.gmail.com","threadId":"64108","inReplyTo":"a17e00fa-bf4b-4b53-817b-297236178101@gmail.com","subject":"Re: [PATCH v5 13/13] xdiff: change type of xdfile_t.changed from char to bool","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-24T15:14:24Z","receivedAt":"2025-09-24T15:14:38Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> > From: Ezekiel Newren <ezekielnewren@gmail.com>\n> >\n> > The only values possible for 'changed' is 1 and 0, which exactly maps\n> > to a bool type. It might not look like this is the case because\n> > matches1 and matches2 (which use to be dis1, and dis2) were also char\n> > and were assigned numerical values within a few lines of 'changed'\n> > (what used to be rchg).\n> >\n> > Using NONE, SOME, TOO_MANY for matches1[i]/matches2[j], and true/false\n> > for changed[k] makes it clear to future readers that these are\n> > logically separate concepts.\n>\n> Nicely explained - I think this change is a very good idea and\n> separating it out like this makes it much clearer what's going on\n> compared to V4.\n\nThank you! It was obvious to me because I've been refactoring xdiff\nfor many months now, but I wasn't doing a good job of expressing why\nthese 2 concepts are closely related, but distinct.\n"},{"id":"527221","messageId":"1c3461c3-09bb-404d-a4dc-a895baba68f2@gmail.com","threadId":"64108","inReplyTo":"CAH=ZcbBcEbTqQ-FpYSdr_QjSM5sKerECKsSVdTyHoSQqG-V9iA@mail.gmail.com","subject":"Re: [PATCH v5 12/13] xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-24T15:18:28Z","receivedAt":"2025-09-24T15:18:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 24/09/2025 15:46, Ezekiel Newren wrote:\n> On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n>>> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>>>\n>>> Rename dis1, dis2 to matches1, matches2.\n>>>\n>>> Define macros NONE(0), SOME(1), TOO_MANY(2) as the enum values for\n>>> matches1 and matches2. These states will influence whether changed[i]\n>>> is set to 1 or kept as 0.\n>>\n>> This message also says what is being changed rather than why it is being\n>> changed. I think the rename here is a good idea but I'm not sure what\n>> \"rdis[01]\" and \"rpdis[01]\" are used for and whether they should be\n>> renamed if we're renaming \"dis[01]\"\n> \n> \"Rename dis1, dis2 to matches1, matches2 to give the variable names a\n> more obvious meaning.\"\n>\n> Would something like that work, or do I need to refine it further?\n\nI'd maybe add a sentence before that to explain that \"dis1 and dis2 are \nused to record if a line has zero, one or many matches on the other side \nof the diff\". I don't think any of these patches need huge commit \nmessages but a couple of sentences explaining the reasoning would be \nhelpful for anyone looking at them it the future.\n\n> I\n> would love to rename rdis, rpdis, etc... except that I don't\n> understand what is happening or why. Could someone explain the purpose\n> of these variables?\n\nGood question, I'm not sure anyone has an intimate knowledge of this \ncode. My understanding is that the code aims to remove runs of common \nlines when they occur between unique lines in order to reduce the number \nof lines we need to look at when we're calculating the diff. I haven't \nworked through the code in detail though.\n\n>>>        /*\n>>> -      * Limits the window the is examined during the similar-lines\n>>> -      * scan. The loops below stops when dis[i - r] == 1 (line that\n>>> +      * Limits the window that is examined during the similar-lines\n>>> +      * scan. The loops below stops when matches[i - r] == SOME (line that\n>>\n>> Thanks for updating the comments. Not reflowing the lines makes the diff\n>> easier to read but leaves the comments in a rather strange state with\n>> random long lines.\n> \n> What is the reflow limit for comments? 72? 80?\n\nFor code it's 80 columns with a little leaway if that makes things \nclearer. For comments I'd match whatever it is using at the moment.\n\n>>\n>>> +     bool need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n>>\n>> Nice use of bool, strictly speaking I don't think we need the !! if\n>> we're changing the type from int. I think Junio recently suggested that\n>> we might start using (bool) instead of !! for cases like this.\n>>\n>> Everything below this looks good, though some of the lines are getting\n>> quite long with the renamed variables and symbolic values so we might\n>> want to break them.\n> \n> I didn't add !! and thought it looked funny myself. I didn't remove it\n> because I wasn't sure if I should.\n\nOur coding guidelines say not to use \"!!x\" (I assume we're supposed to \ndo \"x != 0\" instead) but in practice it's pretty common to see it in our \ncodebase. I'd maybe try a (bool) cast and see what people say.\n\nThanks for cleaning up the xdiff code, it is much appreciated\n\nPhillip\n\n"},{"id":"527222","messageId":"6057413c-97a7-4b4f-add8-173ec6f74e2b@gmail.com","threadId":"64108","inReplyTo":"CAH=ZcbA+MLbyNp3f7RbMFA6vrDZC6yRKVh7ryVer1Ozg3d4oSg@mail.gmail.com","subject":"Re: [PATCH v5 11/13] xdiff: rename rchg -> changed in xdfile_t","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-09-24T15:18:58Z","receivedAt":"2025-09-24T15:19:01Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 24/09/2025 16:10, Ezekiel Newren wrote:\n> On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n>>> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>>\n>> I agree \"changed\" is a better name but the commit message should explain\n>> what \"rchg\" is used for so that someone who is not familiar with the\n>> code can understand why the change in name is desirable.\n> \n> The field rchg (now 'changed') declares if a line in a file is changed\n> or not. A later commit will change it's type from 'char' to 'bool'\n> to make its purpose even more clear.\n> \n> Something like that?\n\nThat's great\n\nPhillip\n\n"},{"id":"527227","messageId":"xmqqqzvvn9tq.fsf@gitster.g","threadId":"64108","inReplyTo":"CAH=ZcbCf7bUTL=BwCD8B6-AVHZg+K3JafvksLQWUx2D-XR58wA@mail.gmail.com","subject":"Re: [PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-24T15:34:25Z","receivedAt":"2025-09-24T15:34:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ezekiel Newren <ezekielnewren@gmail.com> writes:\n\n> On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n>> > From: Ezekiel Newren <ezekielnewren@gmail.com>\n>>\n>> The commit message should explain why this change is being made\n>\n> Reasons to delete local variable aliasing:\n>   * Usage tracking: Tools are better able to follow the usage.\n>   * Refactor churn: Later commits will refactor rchg.\n>   * No additional meaning: The local variables express the same meaning\n>     as the struct field itself.\n>\n> Would that suffice?\n\nIn general, I do not view the first one is a good excuse.\n\nWhen using a separate local variable enhannces readability of the\ncode (which often is true, with a pointer that points deep into a\nnested structure member) to humans, we shouldn't blindly bend the\ncode to cater to less intelligent tools; it needs balancing.\n\nThe third one alone is not a good excuse for the same reason.  It\n(and the first one) depends on how much benefit we are gaining from\nhaving a short-and-sweet local variables that may make the expressions\nand statements they are involved in easier to read.\n\nFor this particular change, I would think it is on borderline, and\nsubjective.  I would be OK with the third point if you rephrase it\nto additionally say that the conditional and the inner loop is easy\nenough to follow without using the local aliases to make the code\nshorter (which of course is the commit author's opinion, but they\ndeserve to have and express their opinion as part of the rationale\nfor a change).\n\nThanks.\n\n-\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n \tlong i1, i2, l1, l2;\n \n \t/*\n \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n \t */\n \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n-\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n-\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n-\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n+\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n+\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n+\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n"},{"id":"527229","messageId":"CAH=ZcbAcvoXJYPHE=mcZSqPLuUPxKcSs-vd307X7uXi+aG3evw@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqqzvvn9tq.fsf@gitster.g","subject":"Re: [PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-24T15:58:11Z","receivedAt":"2025-09-24T15:58:25Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Wed, Sep 24, 2025 at 9:34 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Ezekiel Newren <ezekielnewren@gmail.com> writes:\n>\n> > On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >>\n> >> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> >> > From: Ezekiel Newren <ezekielnewren@gmail.com>\n> >>\n> >> The commit message should explain why this change is being made\n> >\n> > Reasons to delete local variable aliasing:\n> >   * Usage tracking: Tools are better able to follow the usage.\n> >   * Refactor churn: Later commits will refactor rchg.\n> >   * No additional meaning: The local variables express the same meaning\n> >     as the struct field itself.\n> >\n> > Would that suffice?\n>\n> In general, I do not view the first one is a good excuse.\n>\n> When using a separate local variable enhances readability of the\n> code (which often is true, with a pointer that points deep into a\n> nested structure member) to humans, we shouldn't blindly bend the\n> code to cater to less intelligent tools; it needs balancing.\n>\n> The third one alone is not a good excuse for the same reason.  It\n> (and the first one) depends on how much benefit we are gaining from\n> having a short-and-sweet local variables that may make the expressions\n> and statements they are involved in easier to read.\n>\n> For this particular change, I would think it is borderline, and\n> subjective.  I would be OK with the third point if you rephrase it\n> to additionally say that the conditional and the inner loop is easy\n> enough to follow without using the local aliases to make the code\n> shorter (which of course is the commit author's opinion, but they\n> deserve to have and express their opinion as part of the rationale\n> for a change).\n\nOk, I can see that.\n\nI have a question for everyone: Does preparing C code to be translated\ninto Rust count as a valid reason for changing it? Provided that there\nis no violation of the Git style (or very small in some cases).\n\nIf my intent was to keep this as C code forever I'd agree, but... My\nother reason is that it more closely follows Rust paradigms. Creating\nmultiple pointers to the same memory in Rust subverts the borrow\nchecker's ability to keep track of who owns the memory. Since C\ndoesn't have a concept of borrowing, I decided to delete the aliasing\nhere, and in many other places. I'm removing as much aliasing from the\ncode as I can because it makes it easier to translate into idiomatic\nRust later. Using ctags and modern IDE's to follow variable usage is\nconvenient, but safe Rust refuses to compile in many cases where C\naliasing is common. We could use unsafe Rust to make literal\ntranslations of C to Rust, but then we'd forfeit the reasons and\nbenefits of why we want to add Rust to Git in the first place.\n\nTranslating C to Rust has been difficult because many styles in Git's\nC flat out won't compile in Rust. Many places need a little tweaking,\nothers need major overhauls. In all of my C cleanups I am keeping\nidiomatic Rust in mind.\n"},{"id":"527240","messageId":"xmqqtt0rlpxz.fsf@gitster.g","threadId":"64108","inReplyTo":"1c3461c3-09bb-404d-a4dc-a895baba68f2@gmail.com","subject":"Re: [PATCH v5 12/13] xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-24T17:29:12Z","receivedAt":"2025-09-24T17:29:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> Our coding guidelines say not to use \"!!x\" (I assume we're supposed to\n> do \"x != 0\" instead) but in practice it's pretty common to see it in\n> our codebase. I'd maybe try a (bool) cast and see what people say.\n\nOfftopic.  I am perfectly fine to remove the \"avoid !!x, as it is\ntoo clever and confusing to others\" entry from the guidelines.  As\nwe have many of them and it is a fairly well understood idiom in C,\nI would imagine that it have become less confusing already since the\nentry was written.\n\n> Thanks for cleaning up the xdiff code, it is much appreciated\n\nHear, hear.\n"},{"id":"527269","messageId":"xmqqikh7k05r.fsf@gitster.g","threadId":"64108","inReplyTo":"CAH=ZcbAcvoXJYPHE=mcZSqPLuUPxKcSs-vd307X7uXi+aG3evw@mail.gmail.com","subject":"Re: [PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-24T21:31:28Z","receivedAt":"2025-09-24T21:31:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ezekiel Newren <ezekielnewren@gmail.com> writes:\n\n> I have a question for everyone: Does preparing C code to be translated\n> into Rust count as a valid reason for changing it? Provided that there\n> is no violation of the Git style (or very small in some cases).\n>\n> If my intent was to keep this as C code forever I'd agree, but...\n\nYou'd agree that \"I am preparing this for eventual rewrite\" would be\na valid reason?  Or you are agreeing with something else?\n\n> My\n> other reason is that it more closely follows Rust paradigms. Creating\n> multiple pointers to the same memory in Rust subverts the borrow\n> checker's ability to keep track of who owns the memory.\n\nSure.  But looking at the use of rchg[12] in xdl_build_script(), if\nthey were \"const char *\", combined with the fact that they are local\nand their addresses are never taken (to be leaked to our callers),\nyou wouldn't have much trouble with the current code, or would you\nstill have issues?\n"},{"id":"527276","messageId":"CAH=ZcbAm8++qGNX883epk0h3NA-Z=8+RX1bDJeTe=n2jjSsdmg@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqikh7k05r.fsf@gitster.g","subject":"Re: [PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-24T22:46:34Z","receivedAt":"2025-09-24T22:46:47Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Wed, Sep 24, 2025 at 3:31 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Ezekiel Newren <ezekielnewren@gmail.com> writes:\n>\n> > I have a question for everyone: Does preparing C code to be translated\n> > into Rust count as a valid reason for changing it? Provided that there\n> > is no violation of the Git style (or very small in some cases).\n> >\n> > If my intent was to keep this as C code forever I'd agree, but...\n>\n> You'd agree that \"I am preparing this for eventual rewrite\" would be\n> a valid reason?  Or you are agreeing with something else?\n\nI'd agree that my reasons for making this change are insufficient. I\nthink usage tracking tools _is_ a weak argument, but perhaps not quite\nas weak as what you're thinking. For example, when I renamed the rchg\nfield to changed, it was as simple as right-clicking the field,\nchoosing Rename, typing 'changed', and letting the IDE update every\nuse. Patch 11/13, \"xdiff: rename rchg -> changed in xdfile_t\", was\ngenerated directly from that one action. That patch was clean because\nI had already gone through and removed all the aliases of that field.\n\n> > My\n> > other reason is that it more closely follows Rust paradigms. Creating\n> > multiple pointers to the same memory in Rust subverts the borrow\n> > checker's ability to keep track of who owns the memory.\n>\n> Sure.  But looking at the use of rchg[12] in xdl_build_script(), if\n> they were \"const char *\", combined with the fact that they are local\n> and their addresses are never taken (to be leaked to our callers),\n> you wouldn't have much trouble with the current code, or would you\n> still have issues?\n\nFor xdl_build_script() specifically it would work just fine keeping\nthe local variable aliasing in. I think this is another case of\npersonal preference vs established style. Which path would you prefer\nthat I take?\n\n1. Drop this commit and remember to refactor rchg1, rchg2 to changed1,\nand changed2.\n2. Keep this commit with reasons like this:\n  * Refactor churn: Later commits will refactor rchg.\n  * No additional meaning: The local variables express the same\nmeaning as the struct field itself. Also, the conditional and the\ninner loop is easy enough to follow without using the local aliases to\nmake the code shorter.\n\nMy preference is number 2.\n"},{"id":"527300","messageId":"xmqqh5wrhutd.fsf@gitster.g","threadId":"64108","inReplyTo":"CAH=ZcbAm8++qGNX883epk0h3NA-Z=8+RX1bDJeTe=n2jjSsdmg@mail.gmail.com","subject":"Re: [PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-25T07:09:50Z","receivedAt":"2025-09-25T07:09:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ezekiel Newren <ezekielnewren@gmail.com> writes:\n\n> I'd agree that my reasons for making this change are insufficient. I\n> think usage tracking tools _is_ a weak argument, but perhaps not quite\n> as weak as what you're thinking. For example, when I renamed the rchg\n> field to changed, it was as simple as right-clicking the field,\n> choosing Rename, typing 'changed', and letting the IDE update every\n> use. Patch 11/13, \"xdiff: rename rchg -> changed in xdfile_t\", was\n> generated directly from that one action. That patch was clean because\n> I had already gone through and removed all the aliases of that field.\n\nIf I am reading you correctly, you are describing IDE's syntax-aware\neditor's symbol renaming feature; I am not quite sure what it has to\ndo with \"usage tracking\", which would be more of static analysis\nthing, no?\n\nSurely, IDE makes these symbol renaming easy and that would be one\nreason that makes \"because we will change this part of the code in\nlater commit\" less relevant, isn't it?  Whether the struct member\nrchg is accessed directly in the conditional and loop, or is used as\nthe source of an assignment to a local variable, it needs to be\nrenamed either way.  And with tools, it is not as bad.\n\n>> Sure.  But looking at the use of rchg[12] in xdl_build_script(), if\n>> they were \"const char *\", combined with the fact that they are local\n>> and their addresses are never taken (to be leaked to our callers),\n>> you wouldn't have much trouble with the current code, or would you\n>> still have issues?\n>\n> For xdl_build_script() specifically it would work just fine keeping\n> the local variable aliasing in.\n\nAnd transliterating that directly to Rust would not cause the borrow\nissue as you described?  Then it would be great.  One less thing to\nworry about when we need to look at C and then write an equivalent\nin Rust.\n\n> 2. Keep this commit with reasons like this:\n>   * Refactor churn: Later commits will refactor rchg.\n>   * No additional meaning: The local variables express the same\n> meaning as the struct field itself. Also, the conditional and the\n> inner loop is easy enough to follow without using the local aliases to\n> make the code shorter.\n\nIf you have to go route #2, I can live with it, but \"No additional\nmeaning\" is _not_ a valid reason to remove aliasing variables.\n\nBy definition, a local variable that aliases something like a deeply\nnested structure member should *not* introduce any additional\nmeaning (in other words, if the code modifies that local variable\nmaking it out of sync with the underlying structure, the variable is\nnot without \"addtional meaning\" and is no longer an alias).\n\nThe whole reason I asked you to justify removal of the local\nvariable on the basis of lack of readability improvement (\"the\noriginal is simple enough to read without shorter variables\") was\njust that.  \"This variable is merely an alias to something else\",\naka \"There is no meaning added by the presence of this variable\", is\n*not* a valid reason to remove it by itself.\n\nSo, with the same code but with a better justification like \"the\noriginal uses a few local variables to shorten the code, but open\ncoding the access to underlying members of nested structure without\nthese local variables is not all that hard to read, so let's do so\",\nwould probably be an acceptable explanation with no need for other\nexcuses, I would think.\n\nThanks.\n"},{"id":"527348","messageId":"CAH=ZcbC-cB35AGmiKgEQdFa5ee+DftwOQ_dUe4=T+Vq-dxR+qQ@mail.gmail.com","threadId":"64108","inReplyTo":"1c3461c3-09bb-404d-a4dc-a895baba68f2@gmail.com","subject":"Re: [PATCH v5 12/13] xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-25T18:40:59Z","receivedAt":"2025-09-25T18:41:12Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Wed, Sep 24, 2025 at 9:18 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 24/09/2025 15:46, Ezekiel Newren wrote:\n> > On Wed, Sep 24, 2025 at 4:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n> >>\n> >> On 23/09/2025 22:24, Ezekiel Newren via GitGitGadget wrote:\n> >>> From: Ezekiel Newren <ezekielnewren@gmail.com>\n> >>>\n> >>> Rename dis1, dis2 to matches1, matches2.\n> >>>\n> >>> Define macros NONE(0), SOME(1), TOO_MANY(2) as the enum values for\n> >>> matches1 and matches2. These states will influence whether changed[i]\n> >>> is set to 1 or kept as 0.\n> >>\n> >> This message also says what is being changed rather than why it is being\n> >> changed. I think the rename here is a good idea but I'm not sure what\n> >> \"rdis[01]\" and \"rpdis[01]\" are used for and whether they should be\n> >> renamed if we're renaming \"dis[01]\"\n> >\n> > \"Rename dis1, dis2 to matches1, matches2 to give the variable names a\n> > more obvious meaning.\"\n> >\n> > Would something like that work, or do I need to refine it further?\n>\n> I'd maybe add a sentence before that to explain that \"dis1 and dis2 are\n> used to record if a line has zero, one or many matches on the other side\n> of the diff\". I don't think any of these patches need huge commit\n> messages but a couple of sentences explaining the reasoning would be\n> helpful for anyone looking at them it the future.\n>\n> > I\n> > would love to rename rdis, rpdis, etc... except that I don't\n> > understand what is happening or why. Could someone explain the purpose\n> > of these variables?\n>\n> Good question, I'm not sure anyone has an intimate knowledge of this\n> code. My understanding is that the code aims to remove runs of common\n> lines when they occur between unique lines in order to reduce the number\n> of lines we need to look at when we're calculating the diff. I haven't\n> worked through the code in detail though.\n\nI'm really struggling with how to write this commit message. I would\nvery much appreciate suggestions. Here is what I have so far:\n--- commit message start ---\nxdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c\n\nThe local variables dis1 and dis2 describe how a line should be treated\nbased on how many lines, in the other file, match this line. NONE means\nthe other file does not have any matches to this line. SOME means that\nthere are more than 0 matches, but less than some heuristic threshold.\nTOO_MANY is when there are more matches than that heuristic threshold.\n\nNote: When need_min is true, matches[i] is always set to SOME when the\nnumber of matches is greater than 0.\n\nThe names dis1 and dis2 don't convey what they mean, so let's rename\nthem to matches1 and matches2.\n\nDefine macros NONE(0), SOME(1), TOO_MANY(2) as the enum values for\nmatches1 and matches2. These states will influence whether changed[i]\nis set to 1 or kept as 0.\n\nThe variables r, rdis0, rpdis0, rdis1, rpdis1 in xdl_clean_mmatch()\nhave not been renamed because I don't understand their purpose.\n--- commit message end ---\n\nI'll explain the parts of the code that are relevant to the commit\nmessage with an example. The following snippet goes through every line\n(matches1[i]) of file1 to determine what matches1[i] should be by\nlooking at the number of times that line shows up in file2.\n\n    if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n        mlim = XDL_MAX_EQLIMIT;\n    for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <=\nxdf1->dend; i++, recs++) {\n        rcrec = cf->rcrecs[recs->ha];\n        nm = rcrec ? rcrec->len2 : 0;\n        matches1[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ?\nTOO_MANY: SOME;\n    }\n\nThe lines:\n    rcrec = cf->rcrecs[recs->ha];\n    nm = rcrec ? rcrec->len2 : 0;\ngets the number of matches from file2 (i.e. rcrec->len2), and then this line:\n    matches1[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\nIs the logic to set matches1[i] (the line in question of file1) to\nNONE, SOME, or TOO_MANY.\nmlim seems to be some heuristic threshold based on the XDL_MAX_EQLIMIT\nconstant which is set to 1024.\n"},{"id":"527369","messageId":"CAH=ZcbB8NOMLHqjCGYXFJ6Ms_zc3mMrDSPtargAJ1jcPE8fR7w@mail.gmail.com","threadId":"64108","inReplyTo":"xmqqh5wrhutd.fsf@gitster.g","subject":"Re: [PATCH v5 10/13] xdiff: delete rchg aliasing","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-25T22:02:33Z","receivedAt":"2025-09-25T22:02:47Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Thu, Sep 25, 2025 at 1:09 AM Junio C Hamano <gitster@pobox.com> wrote:\n> So, with the same code but with a better justification like \"the\n> original uses a few local variables to shorten the code, but open\n> coding the access to underlying members of nested structure without\n> these local variables is not all that hard to read, so let's do so\",\n> would probably be an acceptable explanation with no need for other\n> excuses, I would think.\n\nI've changed my opinion. I'm going to drop this commit because I found\nmyself using this design pattern in other functions, which would make\nme a hypocrite if this commit stays.\n"},{"id":"527390","messageId":"CAH=ZcbA0Y90eQV3fHFsROqQu40Mv==XOtctrdW0fQwzb0p05xA@mail.gmail.com","threadId":"64108","inReplyTo":"CAH=ZcbC-cB35AGmiKgEQdFa5ee+DftwOQ_dUe4=T+Vq-dxR+qQ@mail.gmail.com","subject":"Re: [PATCH v5 12/13] xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-26T02:29:16Z","receivedAt":"2025-09-26T02:29:29Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Thu, Sep 25, 2025 at 12:40 PM Ezekiel Newren <ezekielnewren@gmail.com> wrote:\n> I'm really struggling with how to write this commit message. I would\n> very much appreciate suggestions. Here is what I have so far:\n> --- commit message start ---\n> xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c\n> ...\n\nI think I understand what is happening now. The macros I should be\nusing are: DISCARD(0), KEEP(1), INVESTIGATE(2). The comments in and\naround xdl_cleanup_records() and xdl_clean_mmatch() uses the term\ndiscard, and not discarded. I think what DISCARD means is discard this\nline from consideration in the diff algorithm, and KEEP means the diff\nalgorithm is going to look at that line. INVESTIGATE means that the\ncurrent line has more matches in the other file than some threshold,\nso we need to do some more work to decide if the line should be kept\nor discarded.\n\nxdl_cleanup_records() does not belong in xprepare.c because it is only\nused by the classic diff (myers/minimal). It should be moved to\nxdiffi.c, but it depends on the classifier which is only defined in\nxprepare.c. So I can't move xdl_cleanup_records() until more of the\ncode has been cleaned up.\n\nI'll work on the commit, and hopefully publish the next version soon.\n"},{"id":"527451","messageId":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v5.git.git.1758662670.gitgitgadget@gmail.com","subject":"[PATCH v6 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:47Z","receivedAt":"2025-09-26T22:42:02Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"Changes since v5.\n\n * Address review feedback on commit messages.\n * Drop commit \"xdiff: delete rchg aliasing\"\n * Use DISCARD/KEEP/INVESTIGATE instead of NONE/SOME/TOO_MANY\n * Fix the word wrapping in the comments of xprepare.c\n\nCleanup of the functions xdl_cleanup_records() and xdl_clean_mmatch() is out\nof scope for this patch series. The changes to them are incidental to\nexplaining why 'char rchg' is refactored to 'bool changed'.\n\nChanges since v4.\n\n * Make it clear that the field xdfile_t.rchg (now 'xdfile_t.changed') is\n   distinct from the local variables dis1, dis2 (now 'matches1',\n   'matches2').\n * Use NONE, SOME, TOO_MANY instead of NO, YES, MAYBE.\n * Use bool literals for xdfile_t.changed.\n\nChanges since v3.\n\n * Address review feedback.\n * Split the deletion of xdl_get_rec() into 2 commits.\n * Move NO, YES, MAYBE into xprepare.c, and use bool literals.\n * refactor 'char rchg' to 'bool changed'\n\nChanges since v2.\n\n * No patch changes, just resending to get patch 9 to show up on the mailing\n   list.\n * A few tweaks to the cover letter.\n\nChanges since v1, to address review feedback.\n\n * Only include the clean up patches; The remaining patches will be split\n   into a separate series.\n * Commit message clarifications.\n * Minor style cleanups.\n * Performance impacts included in commit message of patch 8.\n\n\nRelevant part of the original cover letter follows:\n===================================================\n\nBefore:\n\ntypedef struct s_xrecord {\n\tstruct s_xrecord *next;\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\tchastore_t rcha;\n\tlong nrec;\n\tunsigned int hbits;\n\txrecord_t **rhash;\n\tlong dstart, dend;\n\txrecord_t **recs;\n\tchar *rchg;\n\tlong *rindex;\n\tlong nreff;\n\tunsigned long *ha;\n} xdfile_t;\n\n\nAfter cleanup:\n\ntypedef struct s_xrecord {\n\tchar const *ptr;\n\tlong size;\n\tunsigned long ha;\n} xrecord_t;\n\ntypedef struct s_xdfile {\n\txrecord_t *recs;\n\tlong nrec;\n\tlong dstart, dend;\n\tbool *changed;\n\tlong *rindex;\n\tlong nreff;\n} xdfile_t;\n\n\n===\n\nEzekiel Newren (12):\n  xdiff: delete static forward declarations in xprepare\n  xdiff: delete local variables and initialize/free xdfile_t directly\n  xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  xdiff: delete superfluous function xdl_get_rec() in xemit\n  xdiff: delete local variables that alias fields in xrecord_t\n  xdiff: delete struct diffdata_t\n  xdiff: delete redundant array xdfile_t.ha\n  xdiff: delete fields ha, line, size in xdlclass_t in favor of an\n    xrecord_t\n  xdiff: delete chastore from xdfile_t\n  xdiff: rename rchg -> changed in xdfile_t\n  xdiff: add macros DISCARD(0), KEEP(1), INVESTIGATE(2) in xprepare.c\n  xdiff: change type of xdfile_t.changed from char to bool\n\n xdiff/xdiffi.c     | 102 ++++++-------\n xdiff/xdiffi.h     |  11 +-\n xdiff/xemit.c      |  38 ++---\n xdiff/xhistogram.c |  10 +-\n xdiff/xmerge.c     |  56 ++++----\n xdiff/xpatience.c  |  18 +--\n xdiff/xprepare.c   | 346 ++++++++++++++++++++-------------------------\n xdiff/xtypes.h     |   9 +-\n xdiff/xutils.c     |  16 +--\n 9 files changed, 269 insertions(+), 337 deletions(-)\n\n\nbase-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2048%2Fezekielnewren%2Fuse_rust_types_in_xdiff-v6\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2048/ezekielnewren/use_rust_types_in_xdiff-v6\nPull-Request: https://github.com/git/git/pull/2048\n\nRange-diff vs v5:\n\n  1:  890e508000 =  1:  890e508000 xdiff: delete static forward declarations in xprepare\n  2:  0cfd75b1ff =  2:  0cfd75b1ff xdiff: delete local variables and initialize/free xdfile_t directly\n  3:  92c81d2ff6 =  3:  92c81d2ff6 xdiff: delete unnecessary fields from xrecord_t and xdfile_t\n  4:  7d3a7e617c =  4:  7d3a7e617c xdiff: delete superfluous function xdl_get_rec() in xemit\n  5:  1d550cf308 !  5:  7a9380328e xdiff: delete superfluous local variables that alias fields in xrecord_t\n     @@ Metadata\n      Author: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## Commit message ##\n     -    xdiff: delete superfluous local variables that alias fields in xrecord_t\n     +    xdiff: delete local variables that alias fields in xrecord_t\n      \n          Use the type xrecord_t as the local variable for the functions in the\n     -    file xdiff/xemit.c.\n     +    file xdiff/xemit.c. Most places directly reference the fields inside of\n     +    this struct, doing that here makes it more consistent with the rest of\n     +    the code.\n      \n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n  6:  2a3a1b657e !  6:  6dce41cd3d xdiff: delete struct diffdata_t\n     @@ Commit message\n          diffdata_t.rindex -> xdfile_t.rindex\n          diffdata_t.rchg   -> xdfile_t.rchg\n      \n     +    I think this struct existed before xdfile_t, and was kept for backward\n     +    compatibility reasons. I think xdiffi should have been refactored to\n     +    use the new (xdfile_t) struct, but was easier to alias it instead.\n     +\n     +    The local variables rchg* and rindex* don't shorten the lines by much,\n     +    nor do they really need to be there to make the code more readable.\n     +    Delete them.\n     +\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## xdiff/xdiffi.c ##\n  7:  4c6543cbe3 =  7:  637d1032ab xdiff: delete redundant array xdfile_t.ha\n  8:  21bf4b5a20 =  8:  738daab090 xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t\n  9:  ef6ae7d29c =  9:  59b00b63b8 xdiff: delete chastore from xdfile_t\n 10:  7b0856108a <  -:  ---------- xdiff: delete rchg aliasing\n 11:  570ab9f898 ! 10:  5702ca6912 xdiff: rename rchg -> changed in xdfile_t\n     @@ Metadata\n       ## Commit message ##\n          xdiff: rename rchg -> changed in xdfile_t\n      \n     +    The field rchg (now 'changed') declares if a line in a file is changed\n     +    or not. A later commit will change it's type from 'char' to 'bool'\n     +    to make its purpose even more clear.\n     +\n          Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n     @@ xdiff/xdiffi.c: static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n       \t\t\tg->start--;\n       \n       \t\treturn 0;\n     -@@ xdiff/xdiffi.c: int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n     +@@ xdiff/xdiffi.c: int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n     + \n     + int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n     + \txdchange_t *cscr = NULL, *xch;\n     +-\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n     ++\tchar *changed1 = xe->xdf1.changed, *changed2 = xe->xdf2.changed;\n     + \tlong i1, i2, l1, l2;\n     + \n     + \t/*\n       \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n       \t */\n       \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n     --\t\tif (xe->xdf1.rchg[i1 - 1] || xe->xdf2.rchg[i2 - 1]) {\n     --\t\t\tfor (l1 = i1; xe->xdf1.rchg[i1 - 1]; i1--);\n     --\t\t\tfor (l2 = i2; xe->xdf2.rchg[i2 - 1]; i2--);\n     -+\t\tif (xe->xdf1.changed[i1 - 1] || xe->xdf2.changed[i2 - 1]) {\n     -+\t\t\tfor (l1 = i1; xe->xdf1.changed[i1 - 1]; i1--);\n     -+\t\t\tfor (l2 = i2; xe->xdf2.changed[i2 - 1]; i2--);\n     +-\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n     +-\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n     +-\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n     ++\t\tif (changed1[i1 - 1] || changed2[i2 - 1]) {\n     ++\t\t\tfor (l1 = i1; changed1[i1 - 1]; i1--);\n     ++\t\t\tfor (l2 = i2; changed2[i2 - 1]; i2--);\n       \n       \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n       \t\t\t\txdl_free_script(cscr);\n 12:  08a0fceb72 ! 11:  f08782a977 xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c\n     @@ Metadata\n      Author: Ezekiel Newren <ezekielnewren@gmail.com>\n      \n       ## Commit message ##\n     -    xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c\n     +    xdiff: add macros DISCARD(0), KEEP(1), INVESTIGATE(2) in xprepare.c\n      \n     -    Rename dis1, dis2 to matches1, matches2.\n     +    This commit is refactor-only; no behavior is changed. A future commit\n     +    will use bool literals for changed[i].\n      \n     -    Define macros NONE(0), SOME(1), TOO_MANY(2) as the enum values for\n     -    matches1 and matches2. These states will influence whether changed[i]\n     -    is set to 1 or kept as 0.\n     +    The functions xdl_clean_mmatch() and xdl_cleanup_records() will be\n     +    cleaned up more in a future patch series. The changes to\n     +    xdl_cleanup_records(), in this patch, is just to make it clear why\n     +    `char rchg` is refactored to `bool changed`.\n     +\n     +    Rename dis* to action* and replace literal numericals with macros.\n     +    The old names came from when dis* (which I think was short for discard)\n     +    was treated like a boolean, but over time it grew into a ternary state\n     +    machine. The result was confusing because dis* and rchg* both used 0/1\n     +    values with different meanings.\n     +\n     +    The new names and macros make the states explicit. nm is short for\n     +    number of matches, and mlim is a heuristic limit:\n     +\n     +      nm == 0       -> action[i] = DISCARD     -> changed[i] = true\n     +      0 < nm < mlim -> action[i] = KEEP        -> changed[i] = false\n     +      nm >= mlim    -> action[i] = INVESTIGATE -> changed[i] = xdl_clean_mmatch()\n     +\n     +    When need_min is true, only DISCARD and KEEP occur because the limit\n     +    is effectively infinite.\n      \n          Best-viewed-with: --color-words\n          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n     @@ xdiff/xprepare.c\n       #define XDL_GUESS_NLINES1 256\n       #define XDL_GUESS_NLINES2 20\n       \n     -+#define NONE 0\n     -+#define SOME 1\n     -+#define TOO_MANY 2\n     ++#define DISCARD 0\n     ++#define KEEP 1\n     ++#define INVESTIGATE 2\n       \n       typedef struct s_xdlclass {\n       \tstruct s_xdlclass *next;\n     @@ xdiff/xprepare.c: void xdl_free_env(xdfenv_t *xe) {\n       \n       \n      -static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n     -+static bool xdl_clean_mmatch(uint8_t const *matches, long i, long s, long e) {\n     ++static bool xdl_clean_mmatch(uint8_t const *action, long i, long s, long e) {\n       \tlong r, rdis0, rpdis0, rdis1, rpdis1;\n       \n       \t/*\n      -\t * Limits the window the is examined during the similar-lines\n      -\t * scan. The loops below stops when dis[i - r] == 1 (line that\n     +-\t * has no match), but there are corner cases where the loop\n     +-\t * proceed all the way to the extremities by causing huge\n     +-\t * performance penalties in case of big files.\n      +\t * Limits the window that is examined during the similar-lines\n     -+\t * scan. The loops below stops when matches[i - r] == SOME (line that\n     - \t * has no match), but there are corner cases where the loop\n     - \t * proceed all the way to the extremities by causing huge\n     - \t * performance penalties in case of big files.\n     ++\t * scan. The loops below stops when action[i - r] == KEEP\n     ++\t * (line that has no match), but there are corner cases where\n     ++\t * the loop proceed all the way to the extremities by causing\n     ++\t * huge performance penalties in case of big files.\n     + \t */\n     + \tif (i - s > XDL_SIMSCAN_WINDOW)\n     + \t\ts = i - XDL_SIMSCAN_WINDOW;\n      @@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n       \n       \t/*\n       \t * Scans the lines before 'i' to find a run of lines that either\n      -\t * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).\n      -\t * Note that we always call this function with dis[i] > 1, so the\n     -+\t * have no match (matches[j] == NONE) or have multiple matches (matches[j] == TOO_MANY).\n     -+\t * Note that we always call this function with matches[i] == TOO_MANY, so the\n     - \t * current line (i) is already a multimatch line.\n     +-\t * current line (i) is already a multimatch line.\n     ++\t * have no match (action[j] == DISCARD) or have multiple matches\n     ++\t * (action[j] == INVESTIGATE). Note that we always call this\n     ++\t * function with action[i] == INVESTIGATE, so the current line\n     ++\t * (i) is already a multimatch line.\n       \t */\n       \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n      -\t\tif (!dis[i - r])\n     -+\t\tif (matches[i - r] == NONE)\n     ++\t\tif (action[i - r] == DISCARD)\n       \t\t\trdis0++;\n      -\t\telse if (dis[i - r] == 2)\n     -+\t\telse if (matches[i - r] == TOO_MANY)\n     ++\t\telse if (action[i - r] == INVESTIGATE)\n       \t\t\trpdis0++;\n      -\t\telse\n     -+\t\telse if (matches[i - r] == SOME)\n     ++\t\telse if (action[i - r] == KEEP)\n       \t\t\tbreak;\n      +\t\telse\n     -+\t\t\tBUG(\"Illegal value for matches[i - r]\");\n     ++\t\t\tBUG(\"Illegal value for action[i - r]\");\n       \t}\n       \t/*\n     - \t * If the run before the line 'i' found only multimatch lines, we\n     +-\t * If the run before the line 'i' found only multimatch lines, we\n      -\t * return 0 and hence we don't make the current line (i) discarded.\n     -+\t * return false and hence we don't make the current line (i) discarded.\n     - \t * We want to discard multimatch lines only when they appear in the\n     +-\t * We want to discard multimatch lines only when they appear in the\n      -\t * middle of runs with nomatch lines (dis[j] == 0).\n     -+\t * middle of runs with nomatch lines (matches[j] == NONE).\n     ++\t * If the run before the line 'i' found only multimatch lines,\n     ++\t * we return false and hence we don't make the current line (i)\n     ++\t * discarded. We want to discard multimatch lines only when\n     ++\t * they appear in the middle of runs with nomatch lines\n     ++\t * (action[j] == DISCARD).\n       \t */\n       \tif (rdis0 == 0)\n       \t\treturn 0;\n       \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n      -\t\tif (!dis[i + r])\n     -+\t\tif (matches[i + r] == NONE)\n     ++\t\tif (action[i + r] == DISCARD)\n       \t\t\trdis1++;\n      -\t\telse if (dis[i + r] == 2)\n     -+\t\telse if (matches[i + r] == TOO_MANY)\n     ++\t\telse if (action[i + r] == INVESTIGATE)\n       \t\t\trpdis1++;\n      -\t\telse\n     -+\t\telse if (matches[i + r] == SOME)\n     ++\t\telse if (action[i + r] == KEEP)\n       \t\t\tbreak;\n      +\t\telse\n     -+\t\t\tBUG(\"Illegal value for matches[i + r]\");\n     ++\t\t\tBUG(\"Illegal value for action[i + r]\");\n       \t}\n       \t/*\n     - \t * If the run after the line 'i' found only multimatch lines, we\n     +-\t * If the run after the line 'i' found only multimatch lines, we\n      -\t * return 0 and hence we don't make the current line (i) discarded.\n     -+\t * return false and hence we don't make the current line (i) discarded.\n     ++\t * If the run after the line 'i' found only multimatch lines,\n     ++\t * we return false and hence we don't make the current line (i)\n     ++\t * discarded.\n       \t */\n       \tif (rdis1 == 0)\n      -\t\treturn 0;\n     @@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, l\n       \txdlclass_t *rcrec;\n      -\tchar *dis, *dis1, *dis2;\n      -\tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n     -+\tuint8_t *matches1, *matches2;\n     -+\tint status = 0;\n     ++\tuint8_t *action1 = NULL, *action2 = NULL;\n      +\tbool need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n     ++\tint ret = 0;\n       \n      -\tif (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))\n      -\t\treturn -1;\n      -\tdis1 = dis;\n      -\tdis2 = dis1 + xdf1->nrec + 1;\n     -+\tmatches1 = NULL;\n     -+\tmatches2 = NULL;\n     -+\n      +\t/*\n      +\t * Create temporary arrays that will help us decide if\n      +\t * changed[i] should remain 0 or become 1.\n      +\t */\n     -+\tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n     -+\t\tstatus = -1;\n     ++\tif (!XDL_CALLOC_ARRAY(action1, xdf1->nrec + 1)) {\n     ++\t\tret = -1;\n      +\t\tgoto cleanup;\n      +\t}\n     -+\tif (!XDL_CALLOC_ARRAY(matches2, xdf2->nrec + 1)) {\n     -+\t\tstatus = -1;\n     ++\tif (!XDL_CALLOC_ARRAY(action2, xdf2->nrec + 1)) {\n     ++\t\tret = -1;\n      +\t\tgoto cleanup;\n      +\t}\n       \n      +\t/*\n     -+\t * Initialize temporary arrays with NONE, SOME, or TOO_MANY.\n     ++\t * Initialize temporary arrays with DISCARD, KEEP, or INVESTIGATE.\n      +\t */\n       \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n       \t\tmlim = XDL_MAX_EQLIMIT;\n     @@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, l\n       \t\trcrec = cf->rcrecs[recs->ha];\n       \t\tnm = rcrec ? rcrec->len2 : 0;\n      -\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n     -+\t\tmatches1[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\n     ++\t\taction1[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP;\n       \t}\n       \n       \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n     @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n       \t\trcrec = cf->rcrecs[recs->ha];\n       \t\tnm = rcrec ? rcrec->len1 : 0;\n      -\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n     -+\t\tmatches2[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\n     ++\t\taction2[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP;\n       \t}\n       \n      +\t/*\n     @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n       \t     i <= xdf1->dend; i++, recs++) {\n      -\t\tif (dis1[i] == 1 ||\n      -\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n     -+\t\tif (matches1[i] == SOME ||\n     -+\t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n     ++\t\tif (action1[i] == KEEP ||\n     ++\t\t    (action1[i] == INVESTIGATE && !xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend))) {\n       \t\t\txdf1->rindex[nreff++] = i;\n     -+\t\t\t/* changed[i] remains 0 */\n     ++\t\t\t/* changed[i] remains 0, i.e. keep */\n       \t\t} else\n       \t\t\txdf1->changed[i] = 1;\n     ++\t\t\t/* i.e. discard */\n       \t}\n     -@@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n     + \txdf1->nreff = nreff;\n       \n       \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n       \t     i <= xdf2->dend; i++, recs++) {\n      -\t\tif (dis2[i] == 1 ||\n      -\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n     -+\t\tif (matches2[i] == SOME ||\n     -+\t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n     ++\t\tif (action2[i] == KEEP ||\n     ++\t\t    (action2[i] == INVESTIGATE && !xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend))) {\n       \t\t\txdf2->rindex[nreff++] = i;\n     -+\t\t\t/* changed[i] remains 0 */\n     ++\t\t\t/* changed[i] remains 0, i.e. keep */\n       \t\t} else\n       \t\t\txdf2->changed[i] = 1;\n     ++\t\t\t/* i.e. discard */\n       \t}\n       \txdf2->nreff = nreff;\n       \n      -\txdl_free(dis);\n      +cleanup:\n     -+\txdl_free(matches1);\n     -+\txdl_free(matches2);\n     ++\txdl_free(action1);\n     ++\txdl_free(action2);\n       \n      -\treturn 0;\n     -+\treturn status;\n     ++\treturn ret;\n       }\n       \n       \n 13:  975e845bfa ! 12:  83e1ace5bd xdiff: change type of xdfile_t.changed from char to bool\n     @@ Commit message\n          xdiff: change type of xdfile_t.changed from char to bool\n      \n          The only values possible for 'changed' is 1 and 0, which exactly maps\n     -    to a bool type. It might not look like this is the case because\n     -    matches1 and matches2 (which use to be dis1, and dis2) were also char\n     -    and were assigned numerical values within a few lines of 'changed'\n     -    (what used to be rchg).\n     +    to a bool type. It might not look like this because action1 and action2\n     +    (which use to be dis1, and dis2) were also of type char and were\n     +    assigned numerical values within a few lines of 'changed' (what used to\n     +    be rchg).\n      \n     -    Using NONE, SOME, TOO_MANY for matches1[i]/matches2[j], and true/false\n     +    Using DISCARD/KEEP/INVESTIGATE for action1[i]/action2[j], and true/false\n          for changed[k] makes it clear to future readers that these are\n          logically separate concepts.\n      \n     @@ xdiff/xdiffi.c: static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n       \n       \t\twhile (xdf->changed[g->start - 1])\n       \t\t\tg->start--;\n     +@@ xdiff/xdiffi.c: int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n     + \n     + int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n     + \txdchange_t *cscr = NULL, *xch;\n     +-\tchar *changed1 = xe->xdf1.changed, *changed2 = xe->xdf2.changed;\n     ++\tbool *changed1 = xe->xdf1.changed, *changed2 = xe->xdf2.changed;\n     + \tlong i1, i2, l1, l2;\n     + \n     + \t/*\n      \n       ## xdiff/xhistogram.c ##\n      @@ xdiff/xhistogram.c: redo:\n     @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n      -\t * changed[i] should remain 0 or become 1.\n      +\t * changed[i] should remain false, or become true.\n       \t */\n     - \tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n     - \t\tstatus = -1;\n     + \tif (!XDL_CALLOC_ARRAY(action1, xdf1->nrec + 1)) {\n     + \t\tret = -1;\n      @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n       \n       \t/*\n     @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n       \t */\n       \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n       \t     i <= xdf1->dend; i++, recs++) {\n     - \t\tif (matches1[i] == SOME ||\n     - \t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n     + \t\tif (action1[i] == KEEP ||\n     + \t\t    (action1[i] == INVESTIGATE && !xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend))) {\n       \t\t\txdf1->rindex[nreff++] = i;\n     --\t\t\t/* changed[i] remains 0 */\n     -+\t\t\t/* changed[i] remains false */\n     +-\t\t\t/* changed[i] remains 0, i.e. keep */\n     ++\t\t\t/* changed[i] remains false, i.e. keep */\n       \t\t} else\n      -\t\t\txdf1->changed[i] = 1;\n      +\t\t\txdf1->changed[i] = true;\n     + \t\t\t/* i.e. discard */\n       \t}\n       \txdf1->nreff = nreff;\n     - \n      @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n     - \t\tif (matches2[i] == SOME ||\n     - \t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n     + \t\tif (action2[i] == KEEP ||\n     + \t\t    (action2[i] == INVESTIGATE && !xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend))) {\n       \t\t\txdf2->rindex[nreff++] = i;\n     --\t\t\t/* changed[i] remains 0 */\n     -+\t\t\t/* changed[i] remains false */\n     +-\t\t\t/* changed[i] remains 0, i.e. keep */\n     ++\t\t\t/* changed[i] remains false, i.e. keep */\n       \t\t} else\n      -\t\t\txdf2->changed[i] = 1;\n      +\t\t\txdf2->changed[i] = true;\n     + \t\t\t/* i.e. discard */\n       \t}\n       \txdf2->nreff = nreff;\n     - \n      \n       ## xdiff/xtypes.h ##\n      @@ xdiff/xtypes.h: typedef struct s_xdfile {\n\n-- \ngitgitgadget\n"},{"id":"527452","messageId":"890e5080008a6ccf8f8157a9a11fbffa401953a6.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 01/12] xdiff: delete static forward declarations in xprepare","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:48Z","receivedAt":"2025-09-26T22:42:03Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nMove xdl_prepare_env() later in the file to avoid the need\nfor static forward declarations.\n\nBest-viewed-with: --color-moved\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 116 ++++++++++++++++++++---------------------------\n 1 file changed, 50 insertions(+), 66 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e1d4017b2d..249bfa678f 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -53,21 +53,6 @@ typedef struct s_xdlclassifier {\n \n \n \n-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);\n-static void xdl_free_classifier(xdlclassifier_t *cf);\n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec);\n-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n-\t\t\t   xdlclassifier_t *cf, xdfile_t *xdf);\n-static void xdl_free_ctx(xdfile_t *xdf);\n-static int xdl_clean_mmatch(char const *dis, long i, long s, long e);\n-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);\n-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);\n-\n-\n-\n-\n static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {\n \tcf->flags = flags;\n \n@@ -242,57 +227,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {\n }\n \n \n-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n-\t\t    xdfenv_t *xe) {\n-\tlong enl1, enl2, sample;\n-\txdlclassifier_t cf;\n-\n-\tmemset(&cf, 0, sizeof(cf));\n-\n-\t/*\n-\t * For histogram diff, we can afford a smaller sample size and\n-\t * thus a poorer estimate of the number of lines, as the hash\n-\t * table (rhash) won't be filled up/grown. The number of lines\n-\t * (nrecs) will be updated correctly anyway by\n-\t * xdl_prepare_ctx().\n-\t */\n-\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n-\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n-\n-\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n-\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n-\n-\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n-\t\treturn -1;\n-\n-\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n-\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n-\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n-\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n-\n-\t\txdl_free_ctx(&xe->xdf2);\n-\t\txdl_free_ctx(&xe->xdf1);\n-\t\txdl_free_classifier(&cf);\n-\t\treturn -1;\n-\t}\n-\n-\txdl_free_classifier(&cf);\n-\n-\treturn 0;\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n@@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2\n \n \treturn 0;\n }\n+\n+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n+\t\t    xdfenv_t *xe) {\n+\tlong enl1, enl2, sample;\n+\txdlclassifier_t cf;\n+\n+\tmemset(&cf, 0, sizeof(cf));\n+\n+\t/*\n+\t * For histogram diff, we can afford a smaller sample size and\n+\t * thus a poorer estimate of the number of lines, as the hash\n+\t * table (rhash) won't be filled up/grown. The number of lines\n+\t * (nrecs) will be updated correctly anyway by\n+\t * xdl_prepare_ctx().\n+\t */\n+\tsample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF\n+\t\t  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);\n+\n+\tenl1 = xdl_guess_lines(mf1, sample) + 1;\n+\tenl2 = xdl_guess_lines(mf2, sample) + 1;\n+\n+\tif (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)\n+\t\treturn -1;\n+\n+\tif (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {\n+\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\tif (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n+\t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&\n+\t    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {\n+\n+\t\txdl_free_ctx(&xe->xdf2);\n+\t\txdl_free_ctx(&xe->xdf1);\n+\t\txdl_free_classifier(&cf);\n+\t\treturn -1;\n+\t}\n+\n+\txdl_free_classifier(&cf);\n+\n+\treturn 0;\n+}\n-- \ngitgitgadget\n\n"},{"id":"527453","messageId":"0cfd75b1ffa3f02cf1230866e6a060e653691825.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 02/12] xdiff: delete local variables and initialize/free xdfile_t directly","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:49Z","receivedAt":"2025-09-26T22:42:05Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThese local variables are essentially a hand-rolled additional\nimplementation of xdl_free_ctx() inlined into xdl_prepare_ctx(). Modify\nthe code to use the existing xdl_free_ctx() function so there aren't\ntwo ways to free such variables.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 78 +++++++++++++++++++-----------------------------\n 1 file changed, 30 insertions(+), 48 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 249bfa678f..96134c9fbf 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -134,99 +134,81 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n }\n \n \n+static void xdl_free_ctx(xdfile_t *xdf)\n+{\n+\txdl_free(xdf->rhash);\n+\txdl_free(xdf->rindex);\n+\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->ha);\n+\txdl_free(xdf->recs);\n+\txdl_cha_free(&xdf->rcha);\n+}\n+\n+\n static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,\n \t\t\t   xdlclassifier_t *cf, xdfile_t *xdf) {\n-\tunsigned int hbits;\n-\tlong nrec, hsize, bsize;\n+\tlong bsize;\n \tunsigned long hav;\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n-\txrecord_t **recs;\n-\txrecord_t **rhash;\n-\tunsigned long *ha;\n-\tchar *rchg;\n-\tlong *rindex;\n \n-\tha = NULL;\n-\trindex = NULL;\n-\trchg = NULL;\n-\trhash = NULL;\n-\trecs = NULL;\n+\txdf->ha = NULL;\n+\txdf->rindex = NULL;\n+\txdf->rchg = NULL;\n+\txdf->rhash = NULL;\n+\txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n \t\tgoto abort;\n-\tif (!XDL_ALLOC_ARRAY(recs, narec))\n+\tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\thbits = xdl_hashbits((unsigned int) narec);\n-\thsize = 1 << hbits;\n-\tif (!XDL_CALLOC_ARRAY(rhash, hsize))\n+\txdf->hbits = xdl_hashbits((unsigned int) narec);\n+\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n \t\tgoto abort;\n \n-\tnrec = 0;\n+\txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n \t\t\tprev = cur;\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n-\t\t\tif (XDL_ALLOC_GROW(recs, nrec + 1, narec))\n+\t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n \t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n \t\t\t\tgoto abort;\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\trecs[nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)\n+\t\t\txdf->recs[xdf->nrec++] = crec;\n+\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(rchg, nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n-\t\tif (!XDL_ALLOC_ARRAY(rindex, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(ha, nrec + 1))\n+\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n \t\t\tgoto abort;\n \t}\n \n-\txdf->nrec = nrec;\n-\txdf->recs = recs;\n-\txdf->hbits = hbits;\n-\txdf->rhash = rhash;\n-\txdf->rchg = rchg + 1;\n-\txdf->rindex = rindex;\n+\txdf->rchg += 1;\n \txdf->nreff = 0;\n-\txdf->ha = ha;\n \txdf->dstart = 0;\n-\txdf->dend = nrec - 1;\n+\txdf->dend = xdf->nrec - 1;\n \n \treturn 0;\n \n abort:\n-\txdl_free(ha);\n-\txdl_free(rindex);\n-\txdl_free(rchg);\n-\txdl_free(rhash);\n-\txdl_free(recs);\n-\txdl_cha_free(&xdf->rcha);\n+\txdl_free_ctx(xdf);\n \treturn -1;\n }\n \n \n-static void xdl_free_ctx(xdfile_t *xdf) {\n-\n-\txdl_free(xdf->rhash);\n-\txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n-\txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n-}\n-\n-\n void xdl_free_env(xdfenv_t *xe) {\n \n \txdl_free_ctx(&xe->xdf2);\n-- \ngitgitgadget\n\n"},{"id":"527454","messageId":"92c81d2ff6059b0d91f9d919b36303af953f4d09.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 03/12] xdiff: delete unnecessary fields from xrecord_t and xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:50Z","receivedAt":"2025-09-26T22:42:06Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,\nbut never used for anything by the code. Remove them.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 15 ++-------------\n xdiff/xtypes.h   |  3 ---\n 2 files changed, 2 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 96134c9fbf..3576415c85 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -91,8 +91,7 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n }\n \n \n-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,\n-\t\t\t       unsigned int hbits, xrecord_t *rec) {\n+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n \tchar const *line;\n \txdlclass_t *rcrec;\n@@ -126,17 +125,12 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \n \trec->ha = (unsigned long) rcrec->idx;\n \n-\thi = (long) XDL_HASHLONG(rec->ha, hbits);\n-\trec->next = rhash[hi];\n-\trhash[hi] = rec;\n-\n \treturn 0;\n }\n \n \n static void xdl_free_ctx(xdfile_t *xdf)\n {\n-\txdl_free(xdf->rhash);\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->ha);\n@@ -155,7 +149,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n-\txdf->rhash = NULL;\n \txdf->recs = NULL;\n \n \tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n@@ -163,10 +156,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n-\txdf->hbits = xdl_hashbits((unsigned int) narec);\n-\tif (!XDL_CALLOC_ARRAY(xdf->rhash, 1 << xdf->hbits))\n-\t\tgoto abort;\n-\n \txdf->nrec = 0;\n \tif ((cur = blk = xdl_mmfile_first(mf, &bsize))) {\n \t\tfor (top = blk + bsize; cur < top; ) {\n@@ -180,7 +169,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n \t\t\txdf->recs[xdf->nrec++] = crec;\n-\t\t\tif (xdl_classify_record(pass, cf, xdf->rhash, xdf->hbits, crec) < 0)\n+\t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8442bd436e..8b8467360e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -39,7 +39,6 @@ typedef struct s_chastore {\n } chastore_t;\n \n typedef struct s_xrecord {\n-\tstruct s_xrecord *next;\n \tchar const *ptr;\n \tlong size;\n \tunsigned long ha;\n@@ -48,8 +47,6 @@ typedef struct s_xrecord {\n typedef struct s_xdfile {\n \tchastore_t rcha;\n \tlong nrec;\n-\tunsigned int hbits;\n-\txrecord_t **rhash;\n \tlong dstart, dend;\n \txrecord_t **recs;\n \tchar *rchg;\n-- \ngitgitgadget\n\n"},{"id":"527455","messageId":"7d3a7e617c68cba19e6ea1195bd6d753a5795a4d.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 04/12] xdiff: delete superfluous function xdl_get_rec() in xemit","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:51Z","receivedAt":"2025-09-26T22:42:07Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen xrecord_t was a linked list, and recs didn't exist, I assume this\nfunction walked the list until it found the right record. Accessing\na contiguous array is so trival that this function is now superfluous.\nDelete it.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 23 +++++++----------------\n 1 file changed, 7 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 1d40c9cb40..40fc8154f3 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -22,23 +22,14 @@\n \n #include \"xinclude.h\"\n \n-static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {\n-\n-\t*rec = xdf->recs[ri]->ptr;\n-\n-\treturn xdf->recs[ri]->size;\n-}\n-\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n \tlong size, psize = strlen(pre);\n-\tchar const *rec;\n-\n-\tsize = xdl_get_rec(xdf, ri, &rec);\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {\n+\tchar const *rec = xdf->recs[ri]->ptr;\n \n+\tsize = xdf->recs[ri]->size;\n+\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0)\n \t\treturn -1;\n-\t}\n \n \treturn 0;\n }\n@@ -120,8 +111,8 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\tconst char *rec = xdf->recs[ri]->ptr;\n+\tlong len = xdf->recs[ri]->size;\n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec, len, buf, sz);\n \treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n@@ -160,8 +151,8 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec;\n-\tlong len = xdl_get_rec(xdf, ri, &rec);\n+\tconst char *rec = xdf->recs[ri]->ptr;\n+\tlong len = xdf->recs[ri]->size;\n \n \twhile (len > 0 && XDL_ISSPACE(*rec)) {\n \t\trec++;\n-- \ngitgitgadget\n\n"},{"id":"527456","messageId":"7a9380328e3ee36d74093f5a174c1cb07c5802e5.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 05/12] xdiff: delete local variables that alias fields in xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:52Z","receivedAt":"2025-09-26T22:42:08Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nUse the type xrecord_t as the local variable for the functions in the\nfile xdiff/xemit.c. Most places directly reference the fields inside of\nthis struct, doing that here makes it more consistent with the rest of\nthe code.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xemit.c | 29 +++++++++++++----------------\n 1 file changed, 13 insertions(+), 16 deletions(-)\n\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 40fc8154f3..2161ac3cd0 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -23,12 +23,11 @@\n #include \"xinclude.h\"\n \n \n-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {\n-\tlong size, psize = strlen(pre);\n-\tchar const *rec = xdf->recs[ri]->ptr;\n+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n+{\n+\txrecord_t *rec = xdf->recs[ri];\n \n-\tsize = xdf->recs[ri]->size;\n-\tif (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0)\n+\tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n \n \treturn 0;\n@@ -111,11 +110,11 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\tconst char *rec = xdf->recs[ri]->ptr;\n-\tlong len = xdf->recs[ri]->size;\n+\txrecord_t *rec = xdf->recs[ri];\n+\n \tif (!xecfg->find_func)\n-\t\treturn def_ff(rec, len, buf, sz);\n-\treturn xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);\n+\t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n+\treturn xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv);\n }\n \n static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)\n@@ -151,14 +150,12 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\tconst char *rec = xdf->recs[ri]->ptr;\n-\tlong len = xdf->recs[ri]->size;\n+\txrecord_t *rec = xdf->recs[ri];\n+\tlong i = 0;\n \n-\twhile (len > 0 && XDL_ISSPACE(*rec)) {\n-\t\trec++;\n-\t\tlen--;\n-\t}\n-\treturn !len;\n+\tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\n+\n+\treturn i == rec->size;\n }\n \n int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,\n-- \ngitgitgadget\n\n"},{"id":"527457","messageId":"6dce41cd3d296e39c25e85c53d8642b87e12a1aa.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 06/12] xdiff: delete struct diffdata_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:53Z","receivedAt":"2025-09-26T22:42:09Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nEvery field in this struct is an alias for a certain field in xdfile_t.\n\ndiffdata_t.nrec   -> xdfile_t.nreff\ndiffdata_t.ha     -> xdfile_t.ha\ndiffdata_t.rindex -> xdfile_t.rindex\ndiffdata_t.rchg   -> xdfile_t.rchg\n\nI think this struct existed before xdfile_t, and was kept for backward\ncompatibility reasons. I think xdiffi should have been refactored to\nuse the new (xdfile_t) struct, but was easier to alias it instead.\n\nThe local variables rchg* and rindex* don't shorten the lines by much,\nnor do they really need to be there to make the code more readable.\nDelete them.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c | 32 ++++++++------------------------\n xdiff/xdiffi.h | 11 ++---------\n 2 files changed, 10 insertions(+), 33 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 5a96e36dfb..bbf0161f84 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -257,10 +257,10 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n  * sub-boxes by calling the box splitting function. Note that the real job\n  * (marking changed lines) is done in the two boundary reaching checks.\n  */\n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;\n+\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n@@ -273,17 +273,11 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t * be obviously changed.\n \t */\n \tif (off1 == lim1) {\n-\t\tchar *rchg2 = dd2->rchg;\n-\t\tlong *rindex2 = dd2->rindex;\n-\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\trchg2[rindex2[off2]] = 1;\n+\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n \t} else if (off2 == lim2) {\n-\t\tchar *rchg1 = dd1->rchg;\n-\t\tlong *rindex1 = dd1->rindex;\n-\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\trchg1[rindex1[off1]] = 1;\n+\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -300,9 +294,9 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n \t\t/*\n \t\t * ... et Impera.\n \t\t */\n-\t\tif (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,\n+\t\tif (xdl_recs_cmp(xdf1, off1, spl.i1, xdf2, off2, spl.i2,\n \t\t\t\t kvdf, kvdb, spl.min_lo, xenv) < 0 ||\n-\t\t    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,\n+\t\t    xdl_recs_cmp(xdf1, spl.i1, lim1, xdf2, spl.i2, lim2,\n \t\t\t\t kvdf, kvdb, spl.min_hi, xenv) < 0) {\n \n \t\t\treturn -1;\n@@ -318,7 +312,6 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \tlong ndiags;\n \tlong *kvd, *kvdf, *kvdb;\n \txdalgoenv_t xenv;\n-\tdiffdata_t dd1, dd2;\n \tint res;\n \n \tif (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)\n@@ -357,16 +350,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \txenv.snake_cnt = XDL_SNAKE_CNT;\n \txenv.heur_min = XDL_HEUR_MIN_COST;\n \n-\tdd1.nrec = xe->xdf1.nreff;\n-\tdd1.ha = xe->xdf1.ha;\n-\tdd1.rchg = xe->xdf1.rchg;\n-\tdd1.rindex = xe->xdf1.rindex;\n-\tdd2.nrec = xe->xdf2.nreff;\n-\tdd2.ha = xe->xdf2.ha;\n-\tdd2.rchg = xe->xdf2.rchg;\n-\tdd2.rindex = xe->xdf2.rindex;\n-\n-\tres = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,\n+\tres = xdl_recs_cmp(&xe->xdf1, 0, xe->xdf1.nreff, &xe->xdf2, 0, xe->xdf2.nreff,\n \t\t\t   kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,\n \t\t\t   &xenv);\n \txdl_free(kvd);\ndiff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h\nindex 126c9d8ff4..49e52c67f9 100644\n--- a/xdiff/xdiffi.h\n+++ b/xdiff/xdiffi.h\n@@ -24,13 +24,6 @@\n #define XDIFFI_H\n \n \n-typedef struct s_diffdata {\n-\tlong nrec;\n-\tunsigned long const *ha;\n-\tlong *rindex;\n-\tchar *rchg;\n-} diffdata_t;\n-\n typedef struct s_xdalgoenv {\n \tlong mxcost;\n \tlong snake_cnt;\n@@ -46,8 +39,8 @@ typedef struct s_xdchange {\n \n \n \n-int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,\n-\t\t diffdata_t *dd2, long off2, long lim2,\n+int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n+\t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);\n int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,\n \t\txdfenv_t *xe);\n-- \ngitgitgadget\n\n"},{"id":"527458","messageId":"637d1032abbd33b7673d3c101267816fbf1a343c.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 07/12] xdiff: delete redundant array xdfile_t.ha","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:54Z","receivedAt":"2025-09-26T22:42:10Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nWhen 0 <= i < xdfile_t.nreff the following is true:\nxdfile_t.ha[i] == xdfile_t.recs[xdfile_t.rindex[i]]\n\nThis makes the code about 5% slower. The fields rindex and ha are\nspecific to the classic diff (myers and minimal). I plan on creating a\nstruct for classic diff, but there's a lot of cleanup that needs to be\ndone before that can happen and leaving ha in would make those cleanups\nharder to follow.\n\nA subsequent commit will delete the chastore cha from xdfile_t. That\nlater commit will investigate deleting ha and cha independently and\ntogether.\n\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c   | 24 ++++++++++++++----------\n xdiff/xprepare.c | 12 ++----------\n xdiff/xtypes.h   |  1 -\n 3 files changed, 16 insertions(+), 21 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex bbf0161f84..11cd090b53 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -22,6 +22,11 @@\n \n #include \"xinclude.h\"\n \n+static unsigned long get_hash(xdfile_t *xdf, long index)\n+{\n+\treturn xdf->recs[xdf->rindex[index]]->ha;\n+}\n+\n #define XDL_MAX_COST_MIN 256\n #define XDL_HEUR_MIN_COST 256\n #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)\n@@ -42,8 +47,8 @@ typedef struct s_xdpsplit {\n  * using this algorithm, so a little bit of heuristic is needed to cut the\n  * search and to return a suboptimal point.\n  */\n-static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n-\t\t      unsigned long const *ha2, long off2, long lim2,\n+static long xdl_split(xdfile_t *xdf1, long off1, long lim1,\n+\t\t      xdfile_t *xdf2, long off2, long lim2,\n \t\t      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,\n \t\t      xdalgoenv_t *xenv) {\n \tlong dmin = off1 - lim2, dmax = lim1 - off2;\n@@ -87,7 +92,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdf[d + 1];\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);\n+\t\t\tfor (; i1 < lim1 && i2 < lim2 && get_hash(xdf1, i1) == get_hash(xdf2, i2); i1++, i2++);\n \t\t\tif (i1 - prev1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdf[d] = i1;\n@@ -124,7 +129,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\ti1 = kvdb[d + 1] - 1;\n \t\t\tprev1 = i1;\n \t\t\ti2 = i1 - d;\n-\t\t\tfor (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);\n+\t\t\tfor (; i1 > off1 && i2 > off2 && get_hash(xdf1, i1 - 1) == get_hash(xdf2, i2 - 1); i1--, i2--);\n \t\t\tif (prev1 - i1 > xenv->snake_cnt)\n \t\t\t\tgot_snake = 1;\n \t\t\tkvdb[d] = i1;\n@@ -159,7 +164,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&\n \t\t\t\t    off2 + xenv->snake_cnt <= i2 && i2 < lim2) {\n-\t\t\t\t\tfor (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)\n+\t\t\t\t\tfor (k = 1; get_hash(xdf1, i1 - k) == get_hash(xdf2, i2 - k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -183,7 +188,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n \t\t\t\tif (v > XDL_K_HEUR * ec && v > best &&\n \t\t\t\t    off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&\n \t\t\t\t    off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {\n-\t\t\t\t\tfor (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)\n+\t\t\t\t\tfor (k = 0; get_hash(xdf1, i1 + k) == get_hash(xdf2, i2 + k); k++)\n \t\t\t\t\t\tif (k == xenv->snake_cnt - 1) {\n \t\t\t\t\t\t\tbest = v;\n \t\t\t\t\t\t\tspl->i1 = i1;\n@@ -260,13 +265,12 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,\n int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t xdfile_t *xdf2, long off2, long lim2,\n \t\t long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {\n-\tunsigned long const *ha1 = xdf1->ha, *ha2 = xdf2->ha;\n \n \t/*\n \t * Shrink the box by walking through each diagonal snake (SW and NE).\n \t */\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);\n-\tfor (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, off1) == get_hash(xdf2, off2); off1++, off2++);\n+\tfor (; off1 < lim1 && off2 < lim2 && get_hash(xdf1, lim1 - 1) == get_hash(xdf2, lim2 - 1); lim1--, lim2--);\n \n \t/*\n \t * If one dimension is empty, then all records on the other one must\n@@ -285,7 +289,7 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t\t/*\n \t\t * Divide ...\n \t\t */\n-\t\tif (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,\n+\t\tif (xdl_split(xdf1, off1, lim1, xdf2, off2, lim2, kvdf, kvdb,\n \t\t\t      need_min, &spl, xenv) < 0) {\n \n \t\t\treturn -1;\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 3576415c85..22c44f0683 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -133,7 +133,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n {\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n-\txdl_free(xdf->ha);\n \txdl_free(xdf->recs);\n \txdl_cha_free(&xdf->rcha);\n }\n@@ -146,7 +145,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \tchar const *blk, *cur, *top, *prev;\n \txrecord_t *crec;\n \n-\txdf->ha = NULL;\n \txdf->rindex = NULL;\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n@@ -181,8 +179,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {\n \t\tif (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))\n \t\t\tgoto abort;\n-\t\tif (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))\n-\t\t\tgoto abort;\n \t}\n \n \txdf->rchg += 1;\n@@ -300,9 +296,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (dis1[i] == 1 ||\n \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n-\t\t\txdf1->rindex[nreff] = i;\n-\t\t\txdf1->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf1->rchg[i] = 1;\n \t}\n@@ -312,9 +306,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t     i <= xdf2->dend; i++, recs++) {\n \t\tif (dis2[i] == 1 ||\n \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n-\t\t\txdf2->rindex[nreff] = i;\n-\t\t\txdf2->ha[nreff] = (*recs)->ha;\n-\t\t\tnreff++;\n+\t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n \t\t\txdf2->rchg[i] = 1;\n \t}\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 8b8467360e..85848f1685 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -52,7 +52,6 @@ typedef struct s_xdfile {\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\n-\tunsigned long *ha;\n } xdfile_t;\n \n typedef struct s_xdfenv {\n-- \ngitgitgadget\n\n"},{"id":"527459","messageId":"738daab090dec58859f4c829eb42f757f035ac70.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 08/12] xdiff: delete fields ha, line, size in xdlclass_t in favor of an xrecord_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:55Z","receivedAt":"2025-09-26T22:42:12Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe fields from xdlclass_t are aliases of xrecord_t:\nxdlclass_t.line -> xrecord_t.ptr\nxdlclass_t.size -> xrecord_t.size\nxdlclass_t.ha   -> xrecord_t.ha\n\nxdlclass_t carries a copy of the data in xrecord_t, but instead of\nembedding xrecord_t it duplicates the individual fields. A future\ncommit will change the types used in xrecord_t so embed it in\nxdlclass_t first, so we don't have to remember to change the types\nhere as well.\n\nBest-viewed-with: --color-words\nHelped-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 14 ++++----------\n 1 file changed, 4 insertions(+), 10 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 22c44f0683..e6e2c0e1c0 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -32,9 +32,7 @@\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n-\tunsigned long ha;\n-\tchar const *line;\n-\tlong size;\n+\txrecord_t rec;\n \tlong idx;\n \tlong len1, len2;\n } xdlclass_t;\n@@ -93,14 +91,12 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {\n \n static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {\n \tlong hi;\n-\tchar const *line;\n \txdlclass_t *rcrec;\n \n-\tline = rec->ptr;\n \thi = (long) XDL_HASHLONG(rec->ha, cf->hbits);\n \tfor (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)\n-\t\tif (rcrec->ha == rec->ha &&\n-\t\t\t\txdl_recmatch(rcrec->line, rcrec->size,\n+\t\tif (rcrec->rec.ha == rec->ha &&\n+\t\t\t\txdl_recmatch(rcrec->rec.ptr, rcrec->rec.size,\n \t\t\t\t\trec->ptr, rec->size, cf->flags))\n \t\t\tbreak;\n \n@@ -113,9 +109,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n \t\tif (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))\n \t\t\t\treturn -1;\n \t\tcf->rcrecs[rcrec->idx] = rcrec;\n-\t\trcrec->line = line;\n-\t\trcrec->size = rec->size;\n-\t\trcrec->ha = rec->ha;\n+\t\trcrec->rec = *rec;\n \t\trcrec->len1 = rcrec->len2 = 0;\n \t\trcrec->next = cf->rchash[hi];\n \t\tcf->rchash[hi] = rcrec;\n-- \ngitgitgadget\n\n"},{"id":"527460","messageId":"59b00b63b89789c28c2a07d3a4ea3002a8f077a8.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 09/12] xdiff: delete chastore from xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:56Z","receivedAt":"2025-09-26T22:42:13Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nxdfile_t currently uses chastore_t which is an arena allocator. I\nthink that xrecord_t used to be a linked list and recs didn't exist\noriginally. When recs was added I think they forgot to remove\nxdfile_t.next, but was overlooked. This dual data structure setup\nmakes the code somewhat confusing.\n\nAdditionally the C type chastore_t isn't FFI friendly, and provides\nlittle to no performance benefit over using realloc to grow an array.\n\nPerformance impact of deleting fields from xdfile_t:\nDeleting ha is about 5% slower.\nDeleting cha is about 5% faster.\n\nDelete ha, but keep cha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.269 s ±  0.017 s    [User: 1.135 s, System: 0.128 s]\n    Range (min … max):    1.249 s …  1.286 s    10 runs\n\n  Benchmark 2: build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.339 s ±  0.017 s    [User: 1.234 s, System: 0.099 s]\n    Range (min … max):    1.320 s …  1.358 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.06 ± 0.02 times faster than build_delete_ha/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete cha, but keep ha\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.290 s ±  0.001 s    [User: 1.154 s, System: 0.130 s]\n    Range (min … max):    1.288 s …  1.292 s    10 runs\n\n  Benchmark 2: build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.232 s ±  0.017 s    [User: 1.105 s, System: 0.121 s]\n    Range (min … max):    1.205 s …  1.249 s    10 runs\n\n  Summary\n    build_delete_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.05 ± 0.01 times faster than build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nDelete ha AND chastore\n  time hyperfine --warmup 3 -L exe build_v2.51.0/git,build_delete_ha_and_chastore/git '{exe} log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null'\n  Benchmark 1: build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.291 s ±  0.002 s    [User: 1.156 s, System: 0.129 s]\n    Range (min … max):    1.287 s …  1.295 s    10 runs\n\n  Benchmark 2: build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n    Time (mean ± σ):      1.306 s ±  0.001 s    [User: 1.195 s, System: 0.105 s]\n    Range (min … max):    1.305 s …  1.308 s    10 runs\n\n  Summary\n    build_v2.51.0/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null ran\n      1.01 ± 0.00 times faster than build_delete_ha_and_chastore/git log --oneline --shortstat --diff-algorithm=myers -3000 v2.39.1 >/dev/null\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 24 ++++++++++----------\n xdiff/xemit.c      |  6 ++---\n xdiff/xhistogram.c |  2 +-\n xdiff/xmerge.c     | 56 +++++++++++++++++++++++-----------------------\n xdiff/xpatience.c  | 10 ++++-----\n xdiff/xprepare.c   | 19 ++++++----------\n xdiff/xtypes.h     |  3 +--\n xdiff/xutils.c     | 12 +++++-----\n 8 files changed, 63 insertions(+), 69 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex 11cd090b53..a66125d44a 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -24,7 +24,7 @@\n \n static unsigned long get_hash(xdfile_t *xdf, long index)\n {\n-\treturn xdf->recs[xdf->rindex[index]]->ha;\n+\treturn xdf->recs[xdf->rindex[index]].ha;\n }\n \n #define XDL_MAX_COST_MIN 256\n@@ -489,13 +489,13 @@ static void measure_split(const xdfile_t *xdf, long split,\n \t\tm->indent = -1;\n \t} else {\n \t\tm->end_of_file = 0;\n-\t\tm->indent = get_indent(xdf->recs[split]);\n+\t\tm->indent = get_indent(&xdf->recs[split]);\n \t}\n \n \tm->pre_blank = 0;\n \tm->pre_indent = -1;\n \tfor (i = split - 1; i >= 0; i--) {\n-\t\tm->pre_indent = get_indent(xdf->recs[i]);\n+\t\tm->pre_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->pre_indent != -1)\n \t\t\tbreak;\n \t\tm->pre_blank += 1;\n@@ -508,7 +508,7 @@ static void measure_split(const xdfile_t *xdf, long split,\n \tm->post_blank = 0;\n \tm->post_indent = -1;\n \tfor (i = split + 1; i < xdf->nrec; i++) {\n-\t\tm->post_indent = get_indent(xdf->recs[i]);\n+\t\tm->post_indent = get_indent(&xdf->recs[i]);\n \t\tif (m->post_indent != -1)\n \t\t\tbreak;\n \t\tm->post_blank += 1;\n@@ -752,7 +752,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n-\t    recs_match(xdf->recs[g->start], xdf->recs[g->end])) {\n+\t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n \t\txdf->rchg[g->start++] = 0;\n \t\txdf->rchg[g->end++] = 1;\n \n@@ -773,7 +773,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n-\t    recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {\n+\t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n \t\txdf->rchg[--g->start] = 1;\n \t\txdf->rchg[--g->end] = 0;\n \n@@ -988,16 +988,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n \t\tint ignore = 1;\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tlong i;\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);\n+\t\t\tignore = xdl_blankline(rec[i].ptr, rec[i].size, flags);\n \n \t\txch->ignore = ignore;\n \t}\n@@ -1021,7 +1021,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \txdchange_t *xch;\n \n \tfor (xch = xscr; xch; xch = xch->next) {\n-\t\txrecord_t **rec;\n+\t\txrecord_t *rec;\n \t\tint ignore = 1;\n \t\tlong i;\n \n@@ -1033,11 +1033,11 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,\n \n \t\trec = &xe->xdf1.recs[xch->i1];\n \t\tfor (i = 0; i < xch->chg1 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\trec = &xe->xdf2.recs[xch->i2];\n \t\tfor (i = 0; i < xch->chg2 && ignore; i++)\n-\t\t\tignore = record_matches_regex(rec[i], xpp);\n+\t\t\tignore = record_matches_regex(&rec[i], xpp);\n \n \t\txch->ignore = ignore;\n \t}\ndiff --git a/xdiff/xemit.c b/xdiff/xemit.c\nindex 2161ac3cd0..b2f1f30cd3 100644\n--- a/xdiff/xemit.c\n+++ b/xdiff/xemit.c\n@@ -25,7 +25,7 @@\n \n static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0)\n \t\treturn -1;\n@@ -110,7 +110,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz)\n static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,\n \t\t\t   char *buf, long sz)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \n \tif (!xecfg->find_func)\n \t\treturn def_ff(rec->ptr, rec->size, buf, sz);\n@@ -150,7 +150,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,\n \n static int is_empty_rec(xdfile_t *xdf, long ri)\n {\n-\txrecord_t *rec = xdf->recs[ri];\n+\txrecord_t *rec = &xdf->recs[ri];\n \tlong i = 0;\n \n \tfor (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 040d81e0bc..4d857e8ae2 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -86,7 +86,7 @@ struct region {\n \t((LINE_MAP(index, ptr))->cnt)\n \n #define REC(env, s, l) \\\n-\t(env->xdf##s.recs[l - 1])\n+\t(&env->xdf##s.recs[l - 1])\n \n static int cmp_recs(xrecord_t *r1, xrecord_t *r2)\n {\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex af40c88a5b..fd600cbb5d 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \t\tint line_count, long flags)\n {\n \tint i;\n-\txrecord_t **rec1 = xe1->xdf2.recs + i1;\n-\txrecord_t **rec2 = xe2->xdf2.recs + i2;\n+\txrecord_t *rec1 = xe1->xdf2.recs + i1;\n+\txrecord_t *rec2 = xe2->xdf2.recs + i2;\n \n \tfor (i = 0; i < line_count; i++) {\n-\t\tint result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,\n-\t\t\trec2[i]->ptr, rec2[i]->size, flags);\n+\t\tint result = xdl_recmatch(rec1[i].ptr, rec1[i].size,\n+\t\t\trec2[i].ptr, rec2[i].size, flags);\n \t\tif (!result)\n \t\t\treturn -1;\n \t}\n@@ -111,7 +111,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \n static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)\n {\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \tint size = 0;\n \n \trecs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n@@ -119,12 +119,12 @@ static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int nee\n \tif (count < 1)\n \t\treturn 0;\n \n-\tfor (i = 0; i < count; size += recs[i++]->size)\n+\tfor (i = 0; i < count; size += recs[i++].size)\n \t\tif (dest)\n-\t\t\tmemcpy(dest + size, recs[i]->ptr, recs[i]->size);\n+\t\t\tmemcpy(dest + size, recs[i].ptr, recs[i].size);\n \tif (add_nl) {\n-\t\ti = recs[count - 1]->size;\n-\t\tif (i == 0 || recs[count - 1]->ptr[i - 1] != '\\n') {\n+\t\ti = recs[count - 1].size;\n+\t\tif (i == 0 || recs[count - 1].ptr[i - 1] != '\\n') {\n \t\t\tif (needs_cr) {\n \t\t\t\tif (dest)\n \t\t\t\t\tdest[size] = '\\r';\n@@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)\n \n \tif (i < file->nrec - 1)\n \t\t/* All lines before the last *must* end in LF */\n-\t\treturn (size = file->recs[i]->size) > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\treturn (size = file->recs[i].size) > 1 &&\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!file->nrec)\n \t\t/* Cannot determine eol style from empty file */\n \t\treturn -1;\n-\tif ((size = file->recs[i]->size) &&\n-\t\t\tfile->recs[i]->ptr[size - 1] == '\\n')\n+\tif ((size = file->recs[i].size) &&\n+\t\t\tfile->recs[i].ptr[size - 1] == '\\n')\n \t\t/* Last line; ends in LF; Is it CR/LF? */\n \t\treturn size > 1 &&\n-\t\t\tfile->recs[i]->ptr[size - 2] == '\\r';\n+\t\t\tfile->recs[i].ptr[size - 2] == '\\r';\n \tif (!i)\n \t\t/* The only line has no eol */\n \t\treturn -1;\n \t/* Determine eol from second-to-last line */\n-\treturn (size = file->recs[i - 1]->size) > 1 &&\n-\t\tfile->recs[i - 1]->ptr[size - 2] == '\\r';\n+\treturn (size = file->recs[i - 1].size) > 1 &&\n+\t\tfile->recs[i - 1].ptr[size - 2] == '\\r';\n }\n \n static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)\n@@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\txpparam_t const *xpp)\n {\n-\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n+\txrecord_t *rec1 = xe1->xdf2.recs, *rec2 = xe2->xdf2.recs;\n \tfor (; m; m = m->next) {\n \t\t/* let's handle just the conflicts */\n \t\tif (m->mode)\n \t\t\tcontinue;\n \n \t\twhile(m->chg1 && m->chg2 &&\n-\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n+\t\t      recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t\tm->i1++;\n \t\t\tm->i2++;\n \t\t}\n \t\twhile (m->chg1 && m->chg2 &&\n-\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n-\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n+\t\t       recmatch(&rec1[m->i1 + m->chg1 - 1],\n+\t\t\t\t&rec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n \t\t\tm->chg1--;\n \t\t\tm->chg2--;\n \t\t}\n@@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t * This probably does not work outside git, since\n \t\t * we have a very simple mmfile structure.\n \t\t */\n-\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;\n-\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr\n-\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;\n-\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;\n-\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr\n-\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;\n+\t\tt1.ptr = (char *)xe1->xdf2.recs[m->i1].ptr;\n+\t\tt1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1].ptr\n+\t\t\t+ xe1->xdf2.recs[m->i1 + m->chg1 - 1].size - t1.ptr;\n+\t\tt2.ptr = (char *)xe2->xdf2.recs[m->i2].ptr;\n+\t\tt2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1].ptr\n+\t\t\t+ xe2->xdf2.recs[m->i2 + m->chg2 - 1].size - t2.ptr;\n \t\tif (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)\n \t\t\treturn -1;\n \t\tif (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||\n@@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)\n static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n {\n \tfor (; chg; chg--, i++)\n-\t\tif (line_contains_alnum(xe->xdf2.recs[i]->ptr,\n-\t\t\t\txe->xdf2.recs[i]->size))\n+\t\tif (line_contains_alnum(xe->xdf2.recs[i].ptr,\n+\t\t\t\txe->xdf2.recs[i].size))\n \t\t\treturn 1;\n \treturn 0;\n }\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 77dc411d19..bf69a58527 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)\n static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\t\t  int pass)\n {\n-\txrecord_t **records = pass == 1 ?\n+\txrecord_t *records = pass == 1 ?\n \t\tmap->env->xdf1.recs : map->env->xdf2.recs;\n-\txrecord_t *record = records[line - 1];\n+\txrecord_t *record = &records[line - 1];\n \t/*\n \t * After xdl_prepare_env() (or more precisely, due to\n \t * xdl_classify_record()), the \"ha\" member of the records (AKA lines)\n@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,\n \t\treturn;\n \tmap->entries[index].line1 = line;\n \tmap->entries[index].hash = record->ha;\n-\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);\n+\tmap->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1].ptr);\n \tif (!map->first)\n \t\tmap->first = map->entries + index;\n \tif (map->last) {\n@@ -246,8 +246,8 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)\n \n static int match(struct hashmap *map, int line1, int line2)\n {\n-\txrecord_t *record1 = map->env->xdf1.recs[line1 - 1];\n-\txrecord_t *record2 = map->env->xdf2.recs[line2 - 1];\n+\txrecord_t *record1 = &map->env->xdf1.recs[line1 - 1];\n+\txrecord_t *record2 = &map->env->xdf2.recs[line2 - 1];\n \treturn record1->ha == record2->ha;\n }\n \ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex e6e2c0e1c0..27c5a4d636 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -128,7 +128,6 @@ static void xdl_free_ctx(xdfile_t *xdf)\n \txdl_free(xdf->rindex);\n \txdl_free(xdf->rchg - 1);\n \txdl_free(xdf->recs);\n-\txdl_cha_free(&xdf->rcha);\n }\n \n \n@@ -143,8 +142,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txdf->rchg = NULL;\n \txdf->recs = NULL;\n \n-\tif (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)\n-\t\tgoto abort;\n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n \t\tgoto abort;\n \n@@ -155,12 +152,10 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\thav = xdl_hash_record(&cur, top, xpp->flags);\n \t\t\tif (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))\n \t\t\t\tgoto abort;\n-\t\t\tif (!(crec = xdl_cha_alloc(&xdf->rcha)))\n-\t\t\t\tgoto abort;\n+\t\t\tcrec = &xdf->recs[xdf->nrec++];\n \t\t\tcrec->ptr = prev;\n \t\t\tcrec->size = (long) (cur - prev);\n \t\t\tcrec->ha = hav;\n-\t\t\txdf->recs[xdf->nrec++] = crec;\n \t\t\tif (xdl_classify_record(pass, cf, crec) < 0)\n \t\t\t\tgoto abort;\n \t\t}\n@@ -260,7 +255,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, nm, nreff, mlim;\n-\txrecord_t **recs;\n+\txrecord_t *recs;\n \txdlclass_t *rcrec;\n \tchar *dis, *dis1, *dis2;\n \tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n@@ -273,7 +268,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n \t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -281,7 +276,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n-\t\trcrec = cf->rcrecs[(*recs)->ha];\n+\t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n \t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n \t}\n@@ -317,13 +312,13 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n  */\n static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, lim;\n-\txrecord_t **recs1, **recs2;\n+\txrecord_t *recs1, *recs2;\n \n \trecs1 = xdf1->recs;\n \trecs2 = xdf2->recs;\n \tfor (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;\n \t     i++, recs1++, recs2++)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dstart = xdf2->dstart = i;\n@@ -331,7 +326,7 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {\n \trecs1 = xdf1->recs + xdf1->nrec - 1;\n \trecs2 = xdf2->recs + xdf2->nrec - 1;\n \tfor (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)\n-\t\tif ((*recs1)->ha != (*recs2)->ha)\n+\t\tif (recs1->ha != recs2->ha)\n \t\t\tbreak;\n \n \txdf1->dend = xdf1->nrec - i - 1;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 85848f1685..3d26cbf1ec 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -45,10 +45,9 @@ typedef struct s_xrecord {\n } xrecord_t;\n \n typedef struct s_xdfile {\n-\tchastore_t rcha;\n+\txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\txrecord_t **recs;\n \tchar *rchg;\n \tlong *rindex;\n \tlong nreff;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 444a108f87..332982b509 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tmmfile_t subfile1, subfile2;\n \txdfenv_t env;\n \n-\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;\n-\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +\n-\t\tdiff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;\n-\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;\n-\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +\n-\t\tdiff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;\n+\tsubfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1].ptr;\n+\tsubfile1.size = diff_env->xdf1.recs[line1 + count1 - 2].ptr +\n+\t\tdiff_env->xdf1.recs[line1 + count1 - 2].size - subfile1.ptr;\n+\tsubfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1].ptr;\n+\tsubfile2.size = diff_env->xdf2.recs[line2 + count2 - 2].ptr +\n+\t\tdiff_env->xdf2.recs[line2 + count2 - 2].size - subfile2.ptr;\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-- \ngitgitgadget\n\n"},{"id":"527461","messageId":"5702ca6912c7f1d300bcfc5b7b99c9ca5ef295ab.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 10/12] xdiff: rename rchg -> changed in xdfile_t","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:57Z","receivedAt":"2025-09-26T22:42:14Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe field rchg (now 'changed') declares if a line in a file is changed\nor not. A later commit will change it's type from 'char' to 'bool'\nto make its purpose even more clear.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 30 +++++++++++++++---------------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 12 ++++++------\n xdiff/xtypes.h     |  2 +-\n xdiff/xutils.c     |  4 ++--\n 6 files changed, 32 insertions(+), 32 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex a66125d44a..bd5b31c664 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->rchg[xdf2->rindex[off2]] = 1;\n+\t\t\txdf2->changed[xdf2->rindex[off2]] = 1;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->rchg[xdf1->rindex[off1]] = 1;\n+\t\t\txdf1->changed[xdf1->rindex[off1]] = 1;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -708,7 +708,7 @@ struct xdlgroup {\n static void group_init(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tg->start = g->end = 0;\n-\twhile (xdf->rchg[g->end])\n+\twhile (xdf->changed[g->end])\n \t\tg->end++;\n }\n \n@@ -722,7 +722,7 @@ static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->start = g->end + 1;\n-\tfor (g->end = g->start; xdf->rchg[g->end]; g->end++)\n+\tfor (g->end = g->start; xdf->changed[g->end]; g->end++)\n \t\t;\n \n \treturn 0;\n@@ -738,7 +738,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)\n \t\treturn -1;\n \n \tg->end = g->start - 1;\n-\tfor (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)\n+\tfor (g->start = g->end; xdf->changed[g->start - 1]; g->start--)\n \t\t;\n \n \treturn 0;\n@@ -753,10 +753,10 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->rchg[g->start++] = 0;\n-\t\txdf->rchg[g->end++] = 1;\n+\t\txdf->changed[g->start++] = 0;\n+\t\txdf->changed[g->end++] = 1;\n \n-\t\twhile (xdf->rchg[g->end])\n+\t\twhile (xdf->changed[g->end])\n \t\t\tg->end++;\n \n \t\treturn 0;\n@@ -774,10 +774,10 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->rchg[--g->start] = 1;\n-\t\txdf->rchg[--g->end] = 0;\n+\t\txdf->changed[--g->start] = 1;\n+\t\txdf->changed[--g->end] = 0;\n \n-\t\twhile (xdf->rchg[g->start - 1])\n+\t\twhile (xdf->changed[g->start - 1])\n \t\t\tg->start--;\n \n \t\treturn 0;\n@@ -932,16 +932,16 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n \n int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n \txdchange_t *cscr = NULL, *xch;\n-\tchar *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;\n+\tchar *changed1 = xe->xdf1.changed, *changed2 = xe->xdf2.changed;\n \tlong i1, i2, l1, l2;\n \n \t/*\n \t * Trivial. Collects \"groups\" of changes and creates an edit script.\n \t */\n \tfor (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)\n-\t\tif (rchg1[i1 - 1] || rchg2[i2 - 1]) {\n-\t\t\tfor (l1 = i1; rchg1[i1 - 1]; i1--);\n-\t\t\tfor (l2 = i2; rchg2[i2 - 1]; i2--);\n+\t\tif (changed1[i1 - 1] || changed2[i2 - 1]) {\n+\t\t\tfor (l1 = i1; changed1[i1 - 1]; i1--);\n+\t\t\tfor (l2 = i2; changed2[i2 - 1]; i2--);\n \n \t\t\tif (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {\n \t\t\t\txdl_free_script(cscr);\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 4d857e8ae2..15ca15f6b0 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex bf69a58527..14092ffb86 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.rchg[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.rchg[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 27c5a4d636..b9b19c36de 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -126,7 +126,7 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t\n static void xdl_free_ctx(xdfile_t *xdf)\n {\n \txdl_free(xdf->rindex);\n-\txdl_free(xdf->rchg - 1);\n+\txdl_free(xdf->changed - 1);\n \txdl_free(xdf->recs);\n }\n \n@@ -139,7 +139,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \txrecord_t *crec;\n \n \txdf->rindex = NULL;\n-\txdf->rchg = NULL;\n+\txdf->changed = NULL;\n \txdf->recs = NULL;\n \n \tif (!XDL_ALLOC_ARRAY(xdf->recs, narec))\n@@ -161,7 +161,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t}\n \t}\n \n-\tif (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))\n+\tif (!XDL_CALLOC_ARRAY(xdf->changed, xdf->nrec + 2))\n \t\tgoto abort;\n \n \tif ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&\n@@ -170,7 +170,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_\n \t\t\tgoto abort;\n \t}\n \n-\txdf->rchg += 1;\n+\txdf->changed += 1;\n \txdf->nreff = 0;\n \txdf->dstart = 0;\n \txdf->dend = xdf->nrec - 1;\n@@ -287,7 +287,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf1->rchg[i] = 1;\n+\t\t\txdf1->changed[i] = 1;\n \t}\n \txdf1->nreff = nreff;\n \n@@ -297,7 +297,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n \t\t} else\n-\t\t\txdf2->rchg[i] = 1;\n+\t\t\txdf2->changed[i] = 1;\n \t}\n \txdf2->nreff = nreff;\n \ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex 3d26cbf1ec..c4b5d2d8fa 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -48,7 +48,7 @@ typedef struct s_xdfile {\n \txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\tchar *rchg;\n+\tchar *changed;\n \tlong *rindex;\n \tlong nreff;\n } xdfile_t;\ndiff --git a/xdiff/xutils.c b/xdiff/xutils.c\nindex 332982b509..ed65c222e6 100644\n--- a/xdiff/xutils.c\n+++ b/xdiff/xutils.c\n@@ -425,8 +425,8 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,\n \tif (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)\n \t\treturn -1;\n \n-\tmemcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);\n-\tmemcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);\n+\tmemcpy(diff_env->xdf1.changed + line1 - 1, env.xdf1.changed, count1);\n+\tmemcpy(diff_env->xdf2.changed + line2 - 1, env.xdf2.changed, count2);\n \n \txdl_free_env(&env);\n \n-- \ngitgitgadget\n\n"},{"id":"527462","messageId":"f08782a97795662cca744817a33bd8d9e340b999.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 11/12] xdiff: add macros DISCARD(0), KEEP(1), INVESTIGATE(2) in xprepare.c","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:58Z","receivedAt":"2025-09-26T22:42:15Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThis commit is refactor-only; no behavior is changed. A future commit\nwill use bool literals for changed[i].\n\nThe functions xdl_clean_mmatch() and xdl_cleanup_records() will be\ncleaned up more in a future patch series. The changes to\nxdl_cleanup_records(), in this patch, is just to make it clear why\n`char rchg` is refactored to `bool changed`.\n\nRename dis* to action* and replace literal numericals with macros.\nThe old names came from when dis* (which I think was short for discard)\nwas treated like a boolean, but over time it grew into a ternary state\nmachine. The result was confusing because dis* and rchg* both used 0/1\nvalues with different meanings.\n\nThe new names and macros make the states explicit. nm is short for\nnumber of matches, and mlim is a heuristic limit:\n\n  nm == 0       -> action[i] = DISCARD     -> changed[i] = true\n  0 < nm < mlim -> action[i] = KEEP        -> changed[i] = false\n  nm >= mlim    -> action[i] = INVESTIGATE -> changed[i] = xdl_clean_mmatch()\n\nWhen need_min is true, only DISCARD and KEEP occur because the limit\nis effectively infinite.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xprepare.c | 106 ++++++++++++++++++++++++++++++-----------------\n 1 file changed, 69 insertions(+), 37 deletions(-)\n\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex b9b19c36de..55e3b50ce6 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -29,6 +29,9 @@\n #define XDL_GUESS_NLINES1 256\n #define XDL_GUESS_NLINES2 20\n \n+#define DISCARD 0\n+#define KEEP 1\n+#define INVESTIGATE 2\n \n typedef struct s_xdlclass {\n \tstruct s_xdlclass *next;\n@@ -190,15 +193,15 @@ void xdl_free_env(xdfenv_t *xe) {\n }\n \n \n-static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n+static bool xdl_clean_mmatch(uint8_t const *action, long i, long s, long e) {\n \tlong r, rdis0, rpdis0, rdis1, rpdis1;\n \n \t/*\n-\t * Limits the window the is examined during the similar-lines\n-\t * scan. The loops below stops when dis[i - r] == 1 (line that\n-\t * has no match), but there are corner cases where the loop\n-\t * proceed all the way to the extremities by causing huge\n-\t * performance penalties in case of big files.\n+\t * Limits the window that is examined during the similar-lines\n+\t * scan. The loops below stops when action[i - r] == KEEP\n+\t * (line that has no match), but there are corner cases where\n+\t * the loop proceed all the way to the extremities by causing\n+\t * huge performance penalties in case of big files.\n \t */\n \tif (i - s > XDL_SIMSCAN_WINDOW)\n \t\ts = i - XDL_SIMSCAN_WINDOW;\n@@ -207,40 +210,47 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n \n \t/*\n \t * Scans the lines before 'i' to find a run of lines that either\n-\t * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).\n-\t * Note that we always call this function with dis[i] > 1, so the\n-\t * current line (i) is already a multimatch line.\n+\t * have no match (action[j] == DISCARD) or have multiple matches\n+\t * (action[j] == INVESTIGATE). Note that we always call this\n+\t * function with action[i] == INVESTIGATE, so the current line\n+\t * (i) is already a multimatch line.\n \t */\n \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n-\t\tif (!dis[i - r])\n+\t\tif (action[i - r] == DISCARD)\n \t\t\trdis0++;\n-\t\telse if (dis[i - r] == 2)\n+\t\telse if (action[i - r] == INVESTIGATE)\n \t\t\trpdis0++;\n-\t\telse\n+\t\telse if (action[i - r] == KEEP)\n \t\t\tbreak;\n+\t\telse\n+\t\t\tBUG(\"Illegal value for action[i - r]\");\n \t}\n \t/*\n-\t * If the run before the line 'i' found only multimatch lines, we\n-\t * return 0 and hence we don't make the current line (i) discarded.\n-\t * We want to discard multimatch lines only when they appear in the\n-\t * middle of runs with nomatch lines (dis[j] == 0).\n+\t * If the run before the line 'i' found only multimatch lines,\n+\t * we return false and hence we don't make the current line (i)\n+\t * discarded. We want to discard multimatch lines only when\n+\t * they appear in the middle of runs with nomatch lines\n+\t * (action[j] == DISCARD).\n \t */\n \tif (rdis0 == 0)\n \t\treturn 0;\n \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n-\t\tif (!dis[i + r])\n+\t\tif (action[i + r] == DISCARD)\n \t\t\trdis1++;\n-\t\telse if (dis[i + r] == 2)\n+\t\telse if (action[i + r] == INVESTIGATE)\n \t\t\trpdis1++;\n-\t\telse\n+\t\telse if (action[i + r] == KEEP)\n \t\t\tbreak;\n+\t\telse\n+\t\t\tBUG(\"Illegal value for action[i + r]\");\n \t}\n \t/*\n-\t * If the run after the line 'i' found only multimatch lines, we\n-\t * return 0 and hence we don't make the current line (i) discarded.\n+\t * If the run after the line 'i' found only multimatch lines,\n+\t * we return false and hence we don't make the current line (i)\n+\t * discarded.\n \t */\n \tif (rdis1 == 0)\n-\t\treturn 0;\n+\t\treturn false;\n \trdis1 += rdis0;\n \trpdis1 += rpdis0;\n \n@@ -251,26 +261,38 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n /*\n  * Try to reduce the problem complexity, discard records that have no\n  * matches on the other file. Also, lines that have multiple matches\n- * might be potentially discarded if they happear in a run of discardable.\n+ * might be potentially discarded if they appear in a run of discardable.\n  */\n static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {\n \tlong i, nm, nreff, mlim;\n \txrecord_t *recs;\n \txdlclass_t *rcrec;\n-\tchar *dis, *dis1, *dis2;\n-\tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n+\tuint8_t *action1 = NULL, *action2 = NULL;\n+\tbool need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n+\tint ret = 0;\n \n-\tif (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))\n-\t\treturn -1;\n-\tdis1 = dis;\n-\tdis2 = dis1 + xdf1->nrec + 1;\n+\t/*\n+\t * Create temporary arrays that will help us decide if\n+\t * changed[i] should remain 0 or become 1.\n+\t */\n+\tif (!XDL_CALLOC_ARRAY(action1, xdf1->nrec + 1)) {\n+\t\tret = -1;\n+\t\tgoto cleanup;\n+\t}\n+\tif (!XDL_CALLOC_ARRAY(action2, xdf2->nrec + 1)) {\n+\t\tret = -1;\n+\t\tgoto cleanup;\n+\t}\n \n+\t/*\n+\t * Initialize temporary arrays with DISCARD, KEEP, or INVESTIGATE.\n+\t */\n \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n \t\tmlim = XDL_MAX_EQLIMIT;\n \tfor (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len2 : 0;\n-\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\taction1[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP;\n \t}\n \n \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n@@ -278,32 +300,42 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \tfor (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {\n \t\trcrec = cf->rcrecs[recs->ha];\n \t\tnm = rcrec ? rcrec->len1 : 0;\n-\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n+\t\taction2[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP;\n \t}\n \n+\t/*\n+\t * Use temporary arrays to decide if changed[i] should remain\n+\t * 0 or become 1.\n+\t */\n \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n-\t\tif (dis1[i] == 1 ||\n-\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n+\t\tif (action1[i] == KEEP ||\n+\t\t    (action1[i] == INVESTIGATE && !xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n+\t\t\t/* changed[i] remains 0, i.e. keep */\n \t\t} else\n \t\t\txdf1->changed[i] = 1;\n+\t\t\t/* i.e. discard */\n \t}\n \txdf1->nreff = nreff;\n \n \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n \t     i <= xdf2->dend; i++, recs++) {\n-\t\tif (dis2[i] == 1 ||\n-\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n+\t\tif (action2[i] == KEEP ||\n+\t\t    (action2[i] == INVESTIGATE && !xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n+\t\t\t/* changed[i] remains 0, i.e. keep */\n \t\t} else\n \t\t\txdf2->changed[i] = 1;\n+\t\t\t/* i.e. discard */\n \t}\n \txdf2->nreff = nreff;\n \n-\txdl_free(dis);\n+cleanup:\n+\txdl_free(action1);\n+\txdl_free(action2);\n \n-\treturn 0;\n+\treturn ret;\n }\n \n \n-- \ngitgitgadget\n\n"},{"id":"527463","messageId":"83e1ace5bdc16f76fcf236d7178d1be420961505.1758926520.git.gitgitgadget@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"[PATCH v6 12/12] xdiff: change type of xdfile_t.changed from char to bool","fromName":"Ezekiel Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2025-09-26T22:41:59Z","receivedAt":"2025-09-26T22:42:16Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"From: Ezekiel Newren <ezekielnewren@gmail.com>\n\nThe only values possible for 'changed' is 1 and 0, which exactly maps\nto a bool type. It might not look like this because action1 and action2\n(which use to be dis1, and dis2) were also of type char and were\nassigned numerical values within a few lines of 'changed' (what used to\nbe rchg).\n\nUsing DISCARD/KEEP/INVESTIGATE for action1[i]/action2[j], and true/false\nfor changed[k] makes it clear to future readers that these are\nlogically separate concepts.\n\nBest-viewed-with: --color-words\nSigned-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n---\n xdiff/xdiffi.c     | 14 +++++++-------\n xdiff/xhistogram.c |  8 ++++----\n xdiff/xpatience.c  |  8 ++++----\n xdiff/xprepare.c   | 12 ++++++------\n xdiff/xtypes.h     |  2 +-\n 5 files changed, 22 insertions(+), 22 deletions(-)\n\ndiff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c\nindex bd5b31c664..6f3998ee54 100644\n--- a/xdiff/xdiffi.c\n+++ b/xdiff/xdiffi.c\n@@ -278,10 +278,10 @@ int xdl_recs_cmp(xdfile_t *xdf1, long off1, long lim1,\n \t */\n \tif (off1 == lim1) {\n \t\tfor (; off2 < lim2; off2++)\n-\t\t\txdf2->changed[xdf2->rindex[off2]] = 1;\n+\t\t\txdf2->changed[xdf2->rindex[off2]] = true;\n \t} else if (off2 == lim2) {\n \t\tfor (; off1 < lim1; off1++)\n-\t\t\txdf1->changed[xdf1->rindex[off1]] = 1;\n+\t\t\txdf1->changed[xdf1->rindex[off1]] = true;\n \t} else {\n \t\txdpsplit_t spl;\n \t\tspl.i1 = spl.i2 = 0;\n@@ -753,8 +753,8 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->end < xdf->nrec &&\n \t    recs_match(&xdf->recs[g->start], &xdf->recs[g->end])) {\n-\t\txdf->changed[g->start++] = 0;\n-\t\txdf->changed[g->end++] = 1;\n+\t\txdf->changed[g->start++] = false;\n+\t\txdf->changed[g->end++] = true;\n \n \t\twhile (xdf->changed[g->end])\n \t\t\tg->end++;\n@@ -774,8 +774,8 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n {\n \tif (g->start > 0 &&\n \t    recs_match(&xdf->recs[g->start - 1], &xdf->recs[g->end - 1])) {\n-\t\txdf->changed[--g->start] = 1;\n-\t\txdf->changed[--g->end] = 0;\n+\t\txdf->changed[--g->start] = true;\n+\t\txdf->changed[--g->end] = false;\n \n \t\twhile (xdf->changed[g->start - 1])\n \t\t\tg->start--;\n@@ -932,7 +932,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n \n int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n \txdchange_t *cscr = NULL, *xch;\n-\tchar *changed1 = xe->xdf1.changed, *changed2 = xe->xdf2.changed;\n+\tbool *changed1 = xe->xdf1.changed, *changed2 = xe->xdf2.changed;\n \tlong i1, i2, l1, l2;\n \n \t/*\ndiff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c\nindex 15ca15f6b0..6dc450b1fe 100644\n--- a/xdiff/xhistogram.c\n+++ b/xdiff/xhistogram.c\n@@ -318,11 +318,11 @@ redo:\n \n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\treturn 0;\n \t}\n \n@@ -335,9 +335,9 @@ redo:\n \telse {\n \t\tif (lcs.begin1 == 0 && lcs.begin2 == 0) {\n \t\t\twhile (count1--)\n-\t\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n+\t\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\t\twhile (count2--)\n-\t\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n+\t\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\t\tresult = 0;\n \t\t} else {\n \t\t\tresult = histogram_diff(xpp, env,\ndiff --git a/xdiff/xpatience.c b/xdiff/xpatience.c\nindex 14092ffb86..669b653580 100644\n--- a/xdiff/xpatience.c\n+++ b/xdiff/xpatience.c\n@@ -331,11 +331,11 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* trivial case: one side is empty */\n \tif (!count1) {\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\treturn 0;\n \t} else if (!count2) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\treturn 0;\n \t}\n \n@@ -347,9 +347,9 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,\n \t/* are there any matching lines at all? */\n \tif (!map.has_matches) {\n \t\twhile(count1--)\n-\t\t\tenv->xdf1.changed[line1++ - 1] = 1;\n+\t\t\tenv->xdf1.changed[line1++ - 1] = true;\n \t\twhile(count2--)\n-\t\t\tenv->xdf2.changed[line2++ - 1] = 1;\n+\t\t\tenv->xdf2.changed[line2++ - 1] = true;\n \t\txdl_free(map.entries);\n \t\treturn 0;\n \t}\ndiff --git a/xdiff/xprepare.c b/xdiff/xprepare.c\nindex 55e3b50ce6..192334f1b7 100644\n--- a/xdiff/xprepare.c\n+++ b/xdiff/xprepare.c\n@@ -273,7 +273,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \n \t/*\n \t * Create temporary arrays that will help us decide if\n-\t * changed[i] should remain 0 or become 1.\n+\t * changed[i] should remain false, or become true.\n \t */\n \tif (!XDL_CALLOC_ARRAY(action1, xdf1->nrec + 1)) {\n \t\tret = -1;\n@@ -305,16 +305,16 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \n \t/*\n \t * Use temporary arrays to decide if changed[i] should remain\n-\t * 0 or become 1.\n+\t * false, or become true.\n \t */\n \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n \t     i <= xdf1->dend; i++, recs++) {\n \t\tif (action1[i] == KEEP ||\n \t\t    (action1[i] == INVESTIGATE && !xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend))) {\n \t\t\txdf1->rindex[nreff++] = i;\n-\t\t\t/* changed[i] remains 0, i.e. keep */\n+\t\t\t/* changed[i] remains false, i.e. keep */\n \t\t} else\n-\t\t\txdf1->changed[i] = 1;\n+\t\t\txdf1->changed[i] = true;\n \t\t\t/* i.e. discard */\n \t}\n \txdf1->nreff = nreff;\n@@ -324,9 +324,9 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n \t\tif (action2[i] == KEEP ||\n \t\t    (action2[i] == INVESTIGATE && !xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend))) {\n \t\t\txdf2->rindex[nreff++] = i;\n-\t\t\t/* changed[i] remains 0, i.e. keep */\n+\t\t\t/* changed[i] remains false, i.e. keep */\n \t\t} else\n-\t\t\txdf2->changed[i] = 1;\n+\t\t\txdf2->changed[i] = true;\n \t\t\t/* i.e. discard */\n \t}\n \txdf2->nreff = nreff;\ndiff --git a/xdiff/xtypes.h b/xdiff/xtypes.h\nindex c4b5d2d8fa..f145abba3e 100644\n--- a/xdiff/xtypes.h\n+++ b/xdiff/xtypes.h\n@@ -48,7 +48,7 @@ typedef struct s_xdfile {\n \txrecord_t *recs;\n \tlong nrec;\n \tlong dstart, dend;\n-\tchar *changed;\n+\tbool *changed;\n \tlong *rindex;\n \tlong nreff;\n } xdfile_t;\n-- \ngitgitgadget\n"},{"id":"527629","messageId":"db85429d-2c89-43ec-a92e-96ad8d0049ce@app.fastmail.com","threadId":"64108","inReplyTo":"7d3a7e617c68cba19e6ea1195bd6d753a5795a4d.1758662670.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 04/13] xdiff: delete superfluous function xdl_get_rec() in xemit","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-09-30T13:31:25Z","receivedAt":"2025-09-30T13:31:46Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Tue, Sep 23, 2025, at 23:24, Ezekiel Newren via GitGitGadget wrote:\n> From: Ezekiel Newren <ezekielnewren@gmail.com>\n>\n> When xrecord_t was a linked list, and recs didn't exist, I assume this\n> function walked the list until it found the right record. Accessing\n> a contiguous array is so trival that this function is now superfluous.\n\ns/trival/trivial/\n\n> Delete it.\n>\n> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n> ---\n"},{"id":"527650","messageId":"CAH=ZcbDjFh0wtWyU_1x-GCgzt3a0xUye+RU-LY-9rMBtVoSnTA@mail.gmail.com","threadId":"64108","inReplyTo":"db85429d-2c89-43ec-a92e-96ad8d0049ce@app.fastmail.com","subject":"Re: [PATCH v5 04/13] xdiff: delete superfluous function xdl_get_rec() in xemit","fromName":"Ezekiel Newren","fromEmail":"ezekielnewren@gmail.com","sentAt":"2025-09-30T19:35:37Z","receivedAt":"2025-09-30T19:35:51Z","isPatch":true,"sender":{"key":"ezekielnewren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/18313324?v=4"},"body":"On Tue, Sep 30, 2025 at 7:31 AM Kristoffer Haugsbakk\n<kristofferhaugsbakk@fastmail.com> wrote:\n> > When xrecord_t was a linked list, and recs didn't exist, I assume this\n> > function walked the list until it found the right record. Accessing\n> > a contiguous array is so trival that this function is now superfluous.\n>\n> s/trival/trivial/\n\nI think that other than this typo this patch series is ready to be\nmerged in. I would prefer that Junio fix this typo, so I don't spam\nthe mailing list with such a small change.\n"},{"id":"527663","messageId":"xmqqh5wj4sfq.fsf@gitster.g","threadId":"64108","inReplyTo":"CAH=ZcbDjFh0wtWyU_1x-GCgzt3a0xUye+RU-LY-9rMBtVoSnTA@mail.gmail.com","subject":"Re: [PATCH v5 04/13] xdiff: delete superfluous function xdl_get_rec() in xemit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-30T20:05:29Z","receivedAt":"2025-09-30T20:05:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ezekiel Newren <ezekielnewren@gmail.com> writes:\n\n> On Tue, Sep 30, 2025 at 7:31 AM Kristoffer Haugsbakk\n> <kristofferhaugsbakk@fastmail.com> wrote:\n>> > When xrecord_t was a linked list, and recs didn't exist, I assume this\n>> > function walked the list until it found the right record. Accessing\n>> > a contiguous array is so trival that this function is now superfluous.\n>>\n>> s/trival/trivial/\n>\n> I think that other than this typo this patch series is ready to be\n> merged in. I would prefer that Junio fix this typo, so I don't spam\n> the mailing list with such a small change.\n\nIf it is only this instance, I can \"rebase -i\" it away, sure.\n\nThanks.\n\n"},{"id":"527880","messageId":"01959215-3cb5-4c12-91eb-1b28e18addf9@gmail.com","threadId":"64108","inReplyTo":"pull.2048.v6.git.git.1758926520.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 00/12] Cleanup xdfile_t and xrecord_t in xdiff.","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-10-03T13:47:49Z","receivedAt":"2025-10-03T13:47:54Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ezekiel\n\nOn 26/09/2025 23:41, Ezekiel Newren via GitGitGadget wrote:\n> Changes since v5.\n> \n>   * Address review feedback on commit messages.\n>   * Drop commit \"xdiff: delete rchg aliasing\"\n>   * Use DISCARD/KEEP/INVESTIGATE instead of NONE/SOME/TOO_MANY\n>   * Fix the word wrapping in the comments of xprepare.c\n\nThanks for expanding the commit messages, I think the range diff looks \ngood. There's a typo in patch 12 (see below) but its not worth \nre-rolling just for that.\n\n> Range-diff vs v5:\n> [...] \n>   12:  08a0fceb72 ! 11:  f08782a977 xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c\n>       @@ Metadata\n>        Author: Ezekiel Newren <ezekielnewren@gmail.com>\n>        \n>         ## Commit message ##\n>       -    xdiff: use enum macros NONE(0), SOME(1), TOO_MANY(2) in xprepare.c\n>       +    xdiff: add macros DISCARD(0), KEEP(1), INVESTIGATE(2) in xprepare.c\n>        \n>       -    Rename dis1, dis2 to matches1, matches2.\n>       +    This commit is refactor-only; no behavior is changed. A future commit\n>       +    will use bool literals for changed[i].\n>        \n>       -    Define macros NONE(0), SOME(1), TOO_MANY(2) as the enum values for\n>       -    matches1 and matches2. These states will influence whether changed[i]\n>       -    is set to 1 or kept as 0.\n>       +    The functions xdl_clean_mmatch() and xdl_cleanup_records() will be\n>       +    cleaned up more in a future patch series. The changes to\n>       +    xdl_cleanup_records(), in this patch, is just to make it clear why\n\nNot worth a re-roll on its own s/is/are/\n\nThanks for working on this\n\nPhillip\n\n>       +    `char rchg` is refactored to `bool changed`.\n>       +\n>       +    Rename dis* to action* and replace literal numericals with macros.\n>       +    The old names came from when dis* (which I think was short for discard)\n>       +    was treated like a boolean, but over time it grew into a ternary state\n>       +    machine. The result was confusing because dis* and rchg* both used 0/1\n>       +    values with different meanings.\n>       +\n>       +    The new names and macros make the states explicit. nm is short for\n>       +    number of matches, and mlim is a heuristic limit:\n>       +\n>       +      nm == 0       -> action[i] = DISCARD     -> changed[i] = true\n>       +      0 < nm < mlim -> action[i] = KEEP        -> changed[i] = false\n>       +      nm >= mlim    -> action[i] = INVESTIGATE -> changed[i] = xdl_clean_mmatch()\n>       +\n>       +    When need_min is true, only DISCARD and KEEP occur because the limit\n>       +    is effectively infinite.\n>        \n>            Best-viewed-with: --color-words\n>            Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>\n>       @@ xdiff/xprepare.c\n>         #define XDL_GUESS_NLINES1 256\n>         #define XDL_GUESS_NLINES2 20\n>         \n>       -+#define NONE 0\n>       -+#define SOME 1\n>       -+#define TOO_MANY 2\n>       ++#define DISCARD 0\n>       ++#define KEEP 1\n>       ++#define INVESTIGATE 2\n>         \n>         typedef struct s_xdlclass {\n>         \tstruct s_xdlclass *next;\n>       @@ xdiff/xprepare.c: void xdl_free_env(xdfenv_t *xe) {\n>         \n>         \n>        -static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n>       -+static bool xdl_clean_mmatch(uint8_t const *matches, long i, long s, long e) {\n>       ++static bool xdl_clean_mmatch(uint8_t const *action, long i, long s, long e) {\n>         \tlong r, rdis0, rpdis0, rdis1, rpdis1;\n>         \n>         \t/*\n>        -\t * Limits the window the is examined during the similar-lines\n>        -\t * scan. The loops below stops when dis[i - r] == 1 (line that\n>       +-\t * has no match), but there are corner cases where the loop\n>       +-\t * proceed all the way to the extremities by causing huge\n>       +-\t * performance penalties in case of big files.\n>        +\t * Limits the window that is examined during the similar-lines\n>       -+\t * scan. The loops below stops when matches[i - r] == SOME (line that\n>       - \t * has no match), but there are corner cases where the loop\n>       - \t * proceed all the way to the extremities by causing huge\n>       - \t * performance penalties in case of big files.\n>       ++\t * scan. The loops below stops when action[i - r] == KEEP\n>       ++\t * (line that has no match), but there are corner cases where\n>       ++\t * the loop proceed all the way to the extremities by causing\n>       ++\t * huge performance penalties in case of big files.\n>       + \t */\n>       + \tif (i - s > XDL_SIMSCAN_WINDOW)\n>       + \t\ts = i - XDL_SIMSCAN_WINDOW;\n>        @@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {\n>         \n>         \t/*\n>         \t * Scans the lines before 'i' to find a run of lines that either\n>        -\t * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).\n>        -\t * Note that we always call this function with dis[i] > 1, so the\n>       -+\t * have no match (matches[j] == NONE) or have multiple matches (matches[j] == TOO_MANY).\n>       -+\t * Note that we always call this function with matches[i] == TOO_MANY, so the\n>       - \t * current line (i) is already a multimatch line.\n>       +-\t * current line (i) is already a multimatch line.\n>       ++\t * have no match (action[j] == DISCARD) or have multiple matches\n>       ++\t * (action[j] == INVESTIGATE). Note that we always call this\n>       ++\t * function with action[i] == INVESTIGATE, so the current line\n>       ++\t * (i) is already a multimatch line.\n>         \t */\n>         \tfor (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {\n>        -\t\tif (!dis[i - r])\n>       -+\t\tif (matches[i - r] == NONE)\n>       ++\t\tif (action[i - r] == DISCARD)\n>         \t\t\trdis0++;\n>        -\t\telse if (dis[i - r] == 2)\n>       -+\t\telse if (matches[i - r] == TOO_MANY)\n>       ++\t\telse if (action[i - r] == INVESTIGATE)\n>         \t\t\trpdis0++;\n>        -\t\telse\n>       -+\t\telse if (matches[i - r] == SOME)\n>       ++\t\telse if (action[i - r] == KEEP)\n>         \t\t\tbreak;\n>        +\t\telse\n>       -+\t\t\tBUG(\"Illegal value for matches[i - r]\");\n>       ++\t\t\tBUG(\"Illegal value for action[i - r]\");\n>         \t}\n>         \t/*\n>       - \t * If the run before the line 'i' found only multimatch lines, we\n>       +-\t * If the run before the line 'i' found only multimatch lines, we\n>        -\t * return 0 and hence we don't make the current line (i) discarded.\n>       -+\t * return false and hence we don't make the current line (i) discarded.\n>       - \t * We want to discard multimatch lines only when they appear in the\n>       +-\t * We want to discard multimatch lines only when they appear in the\n>        -\t * middle of runs with nomatch lines (dis[j] == 0).\n>       -+\t * middle of runs with nomatch lines (matches[j] == NONE).\n>       ++\t * If the run before the line 'i' found only multimatch lines,\n>       ++\t * we return false and hence we don't make the current line (i)\n>       ++\t * discarded. We want to discard multimatch lines only when\n>       ++\t * they appear in the middle of runs with nomatch lines\n>       ++\t * (action[j] == DISCARD).\n>         \t */\n>         \tif (rdis0 == 0)\n>         \t\treturn 0;\n>         \tfor (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {\n>        -\t\tif (!dis[i + r])\n>       -+\t\tif (matches[i + r] == NONE)\n>       ++\t\tif (action[i + r] == DISCARD)\n>         \t\t\trdis1++;\n>        -\t\telse if (dis[i + r] == 2)\n>       -+\t\telse if (matches[i + r] == TOO_MANY)\n>       ++\t\telse if (action[i + r] == INVESTIGATE)\n>         \t\t\trpdis1++;\n>        -\t\telse\n>       -+\t\telse if (matches[i + r] == SOME)\n>       ++\t\telse if (action[i + r] == KEEP)\n>         \t\t\tbreak;\n>        +\t\telse\n>       -+\t\t\tBUG(\"Illegal value for matches[i + r]\");\n>       ++\t\t\tBUG(\"Illegal value for action[i + r]\");\n>         \t}\n>         \t/*\n>       - \t * If the run after the line 'i' found only multimatch lines, we\n>       +-\t * If the run after the line 'i' found only multimatch lines, we\n>        -\t * return 0 and hence we don't make the current line (i) discarded.\n>       -+\t * return false and hence we don't make the current line (i) discarded.\n>       ++\t * If the run after the line 'i' found only multimatch lines,\n>       ++\t * we return false and hence we don't make the current line (i)\n>       ++\t * discarded.\n>         \t */\n>         \tif (rdis1 == 0)\n>        -\t\treturn 0;\n>       @@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, l\n>         \txdlclass_t *rcrec;\n>        -\tchar *dis, *dis1, *dis2;\n>        -\tint need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n>       -+\tuint8_t *matches1, *matches2;\n>       -+\tint status = 0;\n>       ++\tuint8_t *action1 = NULL, *action2 = NULL;\n>        +\tbool need_min = !!(cf->flags & XDF_NEED_MINIMAL);\n>       ++\tint ret = 0;\n>         \n>        -\tif (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))\n>        -\t\treturn -1;\n>        -\tdis1 = dis;\n>        -\tdis2 = dis1 + xdf1->nrec + 1;\n>       -+\tmatches1 = NULL;\n>       -+\tmatches2 = NULL;\n>       -+\n>        +\t/*\n>        +\t * Create temporary arrays that will help us decide if\n>        +\t * changed[i] should remain 0 or become 1.\n>        +\t */\n>       -+\tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n>       -+\t\tstatus = -1;\n>       ++\tif (!XDL_CALLOC_ARRAY(action1, xdf1->nrec + 1)) {\n>       ++\t\tret = -1;\n>        +\t\tgoto cleanup;\n>        +\t}\n>       -+\tif (!XDL_CALLOC_ARRAY(matches2, xdf2->nrec + 1)) {\n>       -+\t\tstatus = -1;\n>       ++\tif (!XDL_CALLOC_ARRAY(action2, xdf2->nrec + 1)) {\n>       ++\t\tret = -1;\n>        +\t\tgoto cleanup;\n>        +\t}\n>         \n>        +\t/*\n>       -+\t * Initialize temporary arrays with NONE, SOME, or TOO_MANY.\n>       ++\t * Initialize temporary arrays with DISCARD, KEEP, or INVESTIGATE.\n>        +\t */\n>         \tif ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)\n>         \t\tmlim = XDL_MAX_EQLIMIT;\n>       @@ xdiff/xprepare.c: static int xdl_clean_mmatch(char const *dis, long i, long s, l\n>         \t\trcrec = cf->rcrecs[recs->ha];\n>         \t\tnm = rcrec ? rcrec->len2 : 0;\n>        -\t\tdis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n>       -+\t\tmatches1[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\n>       ++\t\taction1[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP;\n>         \t}\n>         \n>         \tif ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)\n>       @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n>         \t\trcrec = cf->rcrecs[recs->ha];\n>         \t\tnm = rcrec ? rcrec->len1 : 0;\n>        -\t\tdis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;\n>       -+\t\tmatches2[i] = (nm == 0) ? NONE: (nm >= mlim && !need_min) ? TOO_MANY: SOME;\n>       ++\t\taction2[i] = (nm == 0) ? DISCARD: (nm >= mlim && !need_min) ? INVESTIGATE: KEEP;\n>         \t}\n>         \n>        +\t/*\n>       @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n>         \t     i <= xdf1->dend; i++, recs++) {\n>        -\t\tif (dis1[i] == 1 ||\n>        -\t\t    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {\n>       -+\t\tif (matches1[i] == SOME ||\n>       -+\t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n>       ++\t\tif (action1[i] == KEEP ||\n>       ++\t\t    (action1[i] == INVESTIGATE && !xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend))) {\n>         \t\t\txdf1->rindex[nreff++] = i;\n>       -+\t\t\t/* changed[i] remains 0 */\n>       ++\t\t\t/* changed[i] remains 0, i.e. keep */\n>         \t\t} else\n>         \t\t\txdf1->changed[i] = 1;\n>       ++\t\t\t/* i.e. discard */\n>         \t}\n>       -@@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>       + \txdf1->nreff = nreff;\n>         \n>         \tfor (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];\n>         \t     i <= xdf2->dend; i++, recs++) {\n>        -\t\tif (dis2[i] == 1 ||\n>        -\t\t    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {\n>       -+\t\tif (matches2[i] == SOME ||\n>       -+\t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n>       ++\t\tif (action2[i] == KEEP ||\n>       ++\t\t    (action2[i] == INVESTIGATE && !xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend))) {\n>         \t\t\txdf2->rindex[nreff++] = i;\n>       -+\t\t\t/* changed[i] remains 0 */\n>       ++\t\t\t/* changed[i] remains 0, i.e. keep */\n>         \t\t} else\n>         \t\t\txdf2->changed[i] = 1;\n>       ++\t\t\t/* i.e. discard */\n>         \t}\n>         \txdf2->nreff = nreff;\n>         \n>        -\txdl_free(dis);\n>        +cleanup:\n>       -+\txdl_free(matches1);\n>       -+\txdl_free(matches2);\n>       ++\txdl_free(action1);\n>       ++\txdl_free(action2);\n>         \n>        -\treturn 0;\n>       -+\treturn status;\n>       ++\treturn ret;\n>         }\n>         \n>         \n>   13:  975e845bfa ! 12:  83e1ace5bd xdiff: change type of xdfile_t.changed from char to bool\n>       @@ Commit message\n>            xdiff: change type of xdfile_t.changed from char to bool\n>        \n>            The only values possible for 'changed' is 1 and 0, which exactly maps\n>       -    to a bool type. It might not look like this is the case because\n>       -    matches1 and matches2 (which use to be dis1, and dis2) were also char\n>       -    and were assigned numerical values within a few lines of 'changed'\n>       -    (what used to be rchg).\n>       +    to a bool type. It might not look like this because action1 and action2\n>       +    (which use to be dis1, and dis2) were also of type char and were\n>       +    assigned numerical values within a few lines of 'changed' (what used to\n>       +    be rchg).\n>        \n>       -    Using NONE, SOME, TOO_MANY for matches1[i]/matches2[j], and true/false\n>       +    Using DISCARD/KEEP/INVESTIGATE for action1[i]/action2[j], and true/false\n>            for changed[k] makes it clear to future readers that these are\n>            logically separate concepts.\n>        \n>       @@ xdiff/xdiffi.c: static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)\n>         \n>         \t\twhile (xdf->changed[g->start - 1])\n>         \t\t\tg->start--;\n>       +@@ xdiff/xdiffi.c: int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {\n>       +\n>       + int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {\n>       + \txdchange_t *cscr = NULL, *xch;\n>       +-\tchar *changed1 = xe->xdf1.changed, *changed2 = xe->xdf2.changed;\n>       ++\tbool *changed1 = xe->xdf1.changed, *changed2 = xe->xdf2.changed;\n>       + \tlong i1, i2, l1, l2;\n>       +\n>       + \t/*\n>        \n>         ## xdiff/xhistogram.c ##\n>        @@ xdiff/xhistogram.c: redo:\n>       @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n>        -\t * changed[i] should remain 0 or become 1.\n>        +\t * changed[i] should remain false, or become true.\n>         \t */\n>       - \tif (!XDL_CALLOC_ARRAY(matches1, xdf1->nrec + 1)) {\n>       - \t\tstatus = -1;\n>       + \tif (!XDL_CALLOC_ARRAY(action1, xdf1->nrec + 1)) {\n>       + \t\tret = -1;\n>        @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>         \n>         \t/*\n>       @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *\n>         \t */\n>         \tfor (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];\n>         \t     i <= xdf1->dend; i++, recs++) {\n>       - \t\tif (matches1[i] == SOME ||\n>       - \t\t    (matches1[i] == TOO_MANY && !xdl_clean_mmatch(matches1, i, xdf1->dstart, xdf1->dend))) {\n>       + \t\tif (action1[i] == KEEP ||\n>       + \t\t    (action1[i] == INVESTIGATE && !xdl_clean_mmatch(action1, i, xdf1->dstart, xdf1->dend))) {\n>         \t\t\txdf1->rindex[nreff++] = i;\n>       --\t\t\t/* changed[i] remains 0 */\n>       -+\t\t\t/* changed[i] remains false */\n>       +-\t\t\t/* changed[i] remains 0, i.e. keep */\n>       ++\t\t\t/* changed[i] remains false, i.e. keep */\n>         \t\t} else\n>        -\t\t\txdf1->changed[i] = 1;\n>        +\t\t\txdf1->changed[i] = true;\n>       + \t\t\t/* i.e. discard */\n>         \t}\n>         \txdf1->nreff = nreff;\n>       -\n>        @@ xdiff/xprepare.c: static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd\n>       - \t\tif (matches2[i] == SOME ||\n>       - \t\t    (matches2[i] == TOO_MANY && !xdl_clean_mmatch(matches2, i, xdf2->dstart, xdf2->dend))) {\n>       + \t\tif (action2[i] == KEEP ||\n>       + \t\t    (action2[i] == INVESTIGATE && !xdl_clean_mmatch(action2, i, xdf2->dstart, xdf2->dend))) {\n>         \t\t\txdf2->rindex[nreff++] = i;\n>       --\t\t\t/* changed[i] remains 0 */\n>       -+\t\t\t/* changed[i] remains false */\n>       +-\t\t\t/* changed[i] remains 0, i.e. keep */\n>       ++\t\t\t/* changed[i] remains false, i.e. keep */\n>         \t\t} else\n>        -\t\t\txdf2->changed[i] = 1;\n>        +\t\t\txdf2->changed[i] = true;\n>       + \t\t\t/* i.e. discard */\n>         \t}\n>         \txdf2->nreff = nreff;\n>       -\n>        \n>         ## xdiff/xtypes.h ##\n>        @@ xdiff/xtypes.h: typedef struct s_xdfile {\n> \n\n"}]}