git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v3 08/18] rerere: explain the rerere I/O abstraction

From
Junio C Hamano <gitster@pobox.com>
Date
Jul 24, 2015, 20:42 UTC
Message-ID
<xmqqio99l22p.fsf@gitster.dls.corp.google.com>
In-Reply-To
<1437171880-21590-9-git-send-email-gitster@pobox.com>
Junio C Hamano <gitster@pobox.com> writes:
Show 6 quoted lines
> +/*
> + * Write a conflict marker to io->output (if defined).
> + */
>  static void rerere_io_putconflict(int ch, int size, struct rerere_io *io)
>  {
>  	char buf[64];

This is unrelated to the topic of this step, but this function seems to be very poorly put together with duct tape.

Show 10 quoted lines
> static void rerere_io_putconflict(int ch, int size, struct rerere_io *io)
> {
> 	char buf[64];
> 
> 	while (size) {
> 		if (size < sizeof(buf) - 2) {
> 			memset(buf, ch, size);
> 			buf[size] = '\n';
> 			buf[size + 1] = '\0';
> 			size = 0;

The if() guarding this block has an off-by-one error in the benign direction. When size is 62, the marker with terminating LF and NUL should still fit within the buf[], so it should use "<=", not "<", to compare.

Show 7 quoted lines
> 		} else {
> 			int sz = sizeof(buf) - 1;
> 			if (size <= sz)
> 				sz -= (sz - size) + 1;
> 			memset(buf, ch, sz);
> 			buf[sz] = '\0';
> 			size -= sz;

This is an even more awkward construct. sz is what we have to work with the substring that we cannot emit with a single call (because buf[] is too small), so naturally I would expect it to be more like

	int to_emit = size;
        if (sz <= size)
        	to_emit = sz;
	memset(buf, ch, to_emit);
        buf[to_emit] = '\0';
	size -= to_emit;
which makes the "if (size <= sz)" in the code look very suspicious.

But rewriting it to the "more natural" version would make it buggy. At a right boundary condition, i.e. size == 63, we may find that the conflict marker is too long with LF and NUL to fit in buf[] and come here, and then fill the full conflict marker with NUL just fine in buf[], decrementing size to 0, emit that 63-byte long marker. The next iteration will exit the loop without emitting the LF.

The unnatural is what is saving us from such a bug.
	if (size <= sz)
		sz -= (sz - size) + 1;

It ensures that subtraction of sz (i.e. "to_emit") from size before the next iteration will never brings size down to zero by reducing sz by one too much, forcing another iteration, which will then have size smaller than "sizeof(buf) - 2" and have us emit the LF.

Not buggy, but ugly.
> 		}
> 		rerere_io_putstr(buf, io);
> 	}
> }
Previous: Junio C HamanoNext: Junio C Hamano
Message 28 of 39 in “"rerere" minor clean-up”
  1. 00/13 "rerere" minor clean-upJunio C Hamano, Jul 1, 2015
  2. 01/13 rerere: fix an off-by-one non-bugJunio C Hamano, Jul 1, 2015
  3. 02/13 rerere: plug conflict ID leaksJunio C Hamano, Jul 1, 2015
  4. 03/13 rerere: lift PATH_MAX limitationJunio C Hamano, Jul 1, 2015
  5. 04/13 rerere: write out each record of MERGE_RR in one goJunio C Hamano, Jul 1, 2015
  6. 05/13 rerere: report autoupdated paths only after actually updating themJunio C Hamano, Jul 1, 2015
  7. 06/13 rerere: drop want_sp parameter from is_cmarker()Junio C Hamano, Jul 1, 2015
  8. 07/13 rerere: stop looping unnecessarilyJunio C Hamano, Jul 1, 2015
  9. 08/13 rerere: explain the rerere I/O abstractionJunio C Hamano, Jul 1, 2015
  10. 09/13 rerere: explain MERGE_RR management helpersJunio C Hamano, Jul 1, 2015
  11. 10/13 rerere: explain the primary codepathJunio C Hamano, Jul 1, 2015
  12. 11/13 rerere: explain "rerere forget" codepathJunio C Hamano, Jul 1, 2015
  13. 12/13 rerere: explain the remainderJunio C Hamano, Jul 1, 2015
  14. 13/13 rerere: refactor "replay" part of do_plain_rerere()Junio C Hamano, Jul 1, 2015
  15. 00/18 "rerere" preparatory clean-upJunio C Hamano, Jul 17, 2015
  16. 01/18 rerere: fix an off-by-one non-bugJunio C Hamano, Jul 17, 2015
  17. Junio C HamanoJul 24, 2015
  18. 02/18 rerere: plug conflict ID leaksJunio C Hamano, Jul 17, 2015
  19. 03/18 rerere: lift PATH_MAX limitationJunio C Hamano, Jul 17, 2015
  20. 04/18 rerere: write out each record of MERGE_RR in one goJunio C Hamano, Jul 17, 2015
  21. 05/18 rerere: report autoupdated paths only after actually updating themJunio C Hamano, Jul 17, 2015
  22. 06/18 rerere: drop want_sp parameter from is_cmarker()Junio C Hamano, Jul 17, 2015
  23. Philip OakleyJul 18, 2015
  24. Eric SunshineJul 18, 2015
  25. 07/18 rerere: stop looping unnecessarilyJunio C Hamano, Jul 17, 2015
  26. Junio C HamanoJul 24, 2015
  27. 08/18 rerere: explain the rerere I/O abstractionJunio C Hamano, Jul 17, 2015
  28. Junio C HamanoJul 24, 2015
  29. 09/18 rerere: explain MERGE_RR management helpersJunio C Hamano, Jul 17, 2015
  30. 10/18 rerere: explain the primary codepathJunio C Hamano, Jul 17, 2015
  31. 11/18 rerere: explain "rerere forget" codepathJunio C Hamano, Jul 17, 2015
  32. 12/18 rerere: explain the remainderJunio C Hamano, Jul 17, 2015
  33. 13/18 rerere: refactor "replay" part of do_plain_rerere()Junio C Hamano, Jul 17, 2015
  34. 14/18 rerere: further de-dent do_plain_rerere()Junio C Hamano, Jul 17, 2015
  35. 15/18 rerere: further clarify do_rerere_one_path()Junio C Hamano, Jul 17, 2015
  36. 16/18 rerere: call conflict-ids IDsJunio C Hamano, Jul 17, 2015
  37. 17/18 rerere: use "struct rerere_id" instead of "char *" for conflict IDJunio C Hamano, Jul 17, 2015
  38. Eric SunshineJul 18, 2015
  39. 18/18 rerere: un-nest merge() furtherJunio C Hamano, Jul 17, 2015

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.