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

[PATCH v9 2/6] convert.c: stream and early out

From
tboegi@web.de <tboegi@web.de>
Date
May 7, 2016, 06:11 UTC
Message-ID
<1462601460-23543-1-git-send-email-tboegi@web.de>
In-Reply-To
<xmqqegblor2l.fsf@gitster.mtv.corp.google.com>
From: Torsten Bögershausen <tboegi@web.de>
When statistics are done for the autocrlf handling, the search in
the content can be stopped, if e.g
- a search for binary is done, and a NUL character is found
- a search for CRLF is done, and the first CRLF is found.

Similar when statistics for binary vs non-binary are gathered: Whenever a lone CR or NUL is found, the search can be aborted.

When checking out files in "auto" mode, any file that has a "lone CR" or a CRLF will not be converted, so the search can be aborted early.

Add the new bit, CONVERT_STAT_BITS_ANY_CR, which is set for either lone CR or CRLF.

Many binary files have a NUL very early (within the first few bytes, latest within the first 1..2K). It is often not necessary to load the whole content of a file or blob into memory.

Use a streaming handling for blobs and files in the worktree.
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
---
 convert.c | 159 ++++++++++++++++++++++++++++++++++++++++----------------------
 1 file changed, 103 insertions(+), 56 deletions(-)
diff --git a/convert.c b/convert.c
index b1614bf..24ab095 100644
--- a/convert.c
+++ b/convert.c
@@ -3,6 +3,7 @@
 #include "run-command.h"
 #include "quote.h"
 #include "sigchain.h"
+#include "streaming.h"
 
 /*
  * convert.c - convert a file when checking it out and checking it in.
@@ -13,10 +14,10 @@
  * translation when the "text" attribute or "auto_crlf" option is set.
  */
 
-/* Stat bits: When BIN is set, the txt bits are unset */
 #define CONVERT_STAT_BITS_TXT_LF    0x1
 #define CONVERT_STAT_BITS_TXT_CRLF  0x2
 #define CONVERT_STAT_BITS_BIN       0x4
+#define CONVERT_STAT_BITS_ANY_CR    0x8
 
 enum crlf_action {
 	CRLF_UNDEFINED,
@@ -31,30 +32,36 @@ enum crlf_action {
 
 struct text_stat {
 	/* NUL, CR, LF and CRLF counts */
-	unsigned nul, lonecr, lonelf, crlf;
+	unsigned stat_bits, lonecr, lonelf, crlf;
 
 	/* These are just approximations! */
 	unsigned printable, nonprintable;
 };
 
-static void gather_stats(const char *buf, unsigned long size, struct text_stat *stats)
+static void do_gather_stats(const char *buf, unsigned long size,
+			    struct text_stat *stats, unsigned earlyout)
 {
 	unsigned long i;
 
-	memset(stats, 0, sizeof(*stats));
-
+	if (!buf || !size)
+		return;
 	for (i = 0; i < size; i++) {
 		unsigned char c = buf[i];
 		if (c == '\r') {
+			stats->stat_bits |= CONVERT_STAT_BITS_ANY_CR;
 			if (i+1 < size && buf[i+1] == '\n') {
 				stats->crlf++;
 				i++;
-			} else
+				stats->stat_bits |= CONVERT_STAT_BITS_TXT_CRLF;
+			} else {
 				stats->lonecr++;
+				stats->stat_bits |= CONVERT_STAT_BITS_BIN;
+			}
 			continue;
 		}
 		if (c == '\n') {
 			stats->lonelf++;
+			stats->stat_bits |= CONVERT_STAT_BITS_TXT_LF;
 			continue;
 		}
 		if (c == 127)
@@ -67,7 +74,7 @@ static void gather_stats(const char *buf, unsigned long size, struct text_stat *
 				stats->printable++;
 				break;
 			case 0:
-				stats->nul++;
+				stats->stat_bits |= CONVERT_STAT_BITS_BIN;
 				/* fall through */
 			default:
 				stats->nonprintable++;
@@ -75,6 +82,8 @@ static void gather_stats(const char *buf, unsigned long size, struct text_stat *
 		}
 		else
 			stats->printable++;
+		if (stats->stat_bits & earlyout)
+			break; /* We found what we have been searching for */
 	}
 
 	/* If file ends with EOF then don't count this EOF as non-printable. */
@@ -86,41 +95,62 @@ static void gather_stats(const char *buf, unsigned long size, struct text_stat *
  * The same heuristics as diff.c::mmfile_is_binary()
  * We treat files with bare CR as binary
  */
-static int convert_is_binary(unsigned long size, const struct text_stat *stats)
+static void convert_nonprintable(struct text_stat *stats)
 {
-	if (stats->lonecr)
-		return 1;
-	if (stats->nul)
-		return 1;
 	if ((stats->printable >> 7) < stats->nonprintable)
-		return 1;
-	return 0;
+		stats->stat_bits |= CONVERT_STAT_BITS_BIN;
+}
+
+static void gather_stats(const char *buf, unsigned long size,
+			 struct text_stat *stats, unsigned earlyout)
+{
+	memset(stats, 0, sizeof(*stats));
+	do_gather_stats(buf, size, stats, earlyout);
+	convert_nonprintable(stats);
 }
 
-static unsigned int gather_convert_stats(const char *data, unsigned long size)
+
+static unsigned get_convert_stats_sha1(unsigned const char *sha1,
+				       unsigned earlyout)
 {
+	struct git_istream *st;
 	struct text_stat stats;
-	int ret = 0;
-	if (!data || !size)
-		return 0;
-	gather_stats(data, size, &stats);
-	if (convert_is_binary(size, &stats))
-		ret |= CONVERT_STAT_BITS_BIN;
-	if (stats.crlf)
-		ret |= CONVERT_STAT_BITS_TXT_CRLF;
-	if (stats.lonelf)
-		ret |=  CONVERT_STAT_BITS_TXT_LF;
+	enum object_type type;
+	unsigned long sz;
 
-	return ret;
+	if (!sha1)
+		return 0;
+	memset(&stats, 0, sizeof(stats));
+	st = open_istream(sha1, &type, &sz, NULL);
+	if (!st) {
+		return 0;
+	}
+	if (type != OBJ_BLOB)
+		goto close_and_exit_i;
+	for (;;) {
+		char buf[1024];
+		ssize_t readlen = read_istream(st, buf, sizeof(buf));
+		if (readlen < 0)
+			break;
+		if (!readlen)
+			break;
+		do_gather_stats(buf, (unsigned long)readlen, &stats, earlyout);
+		if (stats.stat_bits & earlyout)
+			break; /* We found what we have been searching for */
+	}
+close_and_exit_i:
+	close_istream(st);
+	convert_nonprintable(&stats);
+	return stats.stat_bits;
 }
 
-static const char *gather_convert_stats_ascii(const char *data, unsigned long size)
+static const char *convert_stats_ascii(unsigned convert_stats)
 {
-	unsigned int convert_stats = gather_convert_stats(data, size);
-
+	unsigned mask = CONVERT_STAT_BITS_TXT_LF |
+		CONVERT_STAT_BITS_TXT_CRLF;
 	if (convert_stats & CONVERT_STAT_BITS_BIN)
 		return "-text";
-	switch (convert_stats) {
+	switch (convert_stats & mask) {
 	case CONVERT_STAT_BITS_TXT_LF:
 		return "lf";
 	case CONVERT_STAT_BITS_TXT_CRLF:
@@ -132,24 +162,45 @@ static const char *gather_convert_stats_ascii(const char *data, unsigned long si
 	}
 }
 
+static unsigned get_convert_stats_wt(const char *path)
+{
+	struct text_stat stats;
+	unsigned earlyout = CONVERT_STAT_BITS_BIN;
+	int fd;
+	memset(&stats, 0, sizeof(stats));
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return 0;
+	for (;;) {
+		char buf[1024];
+		ssize_t readlen = read(fd, buf, sizeof(buf));
+		if (readlen < 0)
+			break;
+		if (!readlen)
+			break;
+		do_gather_stats(buf, (unsigned long)readlen, &stats, earlyout);
+		if (stats.stat_bits & earlyout)
+			break; /* We found what we have been searching for */
+	}
+	close(fd);
+	convert_nonprintable(&stats);
+	return stats.stat_bits;
+}
+
 const char *get_cached_convert_stats_ascii(const char *path)
 {
-	const char *ret;
-	unsigned long sz;
-	void *data = read_blob_data_from_cache(path, &sz);
-	ret = gather_convert_stats_ascii(data, sz);
-	free(data);
-	return ret;
+	unsigned convert_stats;
+	unsigned earlyout = CONVERT_STAT_BITS_BIN;
+	convert_stats = get_convert_stats_sha1(get_sha1_from_cache(path),
+					       earlyout);
+	return convert_stats_ascii(convert_stats);
 }
 
 const char *get_wt_convert_stats_ascii(const char *path)
 {
-	const char *ret = "";
-	struct strbuf sb = STRBUF_INIT;
-	if (strbuf_read_file(&sb, path, 0) >= 0)
-		ret = gather_convert_stats_ascii(sb.buf, sb.len);
-	strbuf_release(&sb);
-	return ret;
+	unsigned convert_stats;
+	convert_stats = get_convert_stats_wt(path);
+	return convert_stats_ascii(convert_stats);
 }
 
 static int text_eol_is_crlf(void)
@@ -219,16 +270,10 @@ static void check_safe_crlf(const char *path, enum crlf_action crlf_action,
 
 static int has_cr_in_index(const char *path)
 {
-	unsigned long sz;
-	void *data;
-	int has_cr;
-
-	data = read_blob_data_from_cache(path, &sz);
-	if (!data)
-		return 0;
-	has_cr = memchr(data, '\r', sz) != NULL;
-	free(data);
-	return has_cr;
+	unsigned convert_stats;
+	convert_stats = get_convert_stats_sha1(get_sha1_from_cache(path),
+					       CONVERT_STAT_BITS_ANY_CR);
+	return convert_stats & CONVERT_STAT_BITS_ANY_CR;
 }
 
 static int crlf_to_git(const char *path, const char *src, size_t len,
@@ -249,10 +294,10 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
 	if (!buf && !src)
 		return 1;
 
-	gather_stats(src, len, &stats);
+	gather_stats(src, len, &stats, CONVERT_STAT_BITS_BIN);
 
 	if (crlf_action == CRLF_AUTO || crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
-		if (convert_is_binary(len, &stats))
+		if (stats.stat_bits & CONVERT_STAT_BITS_BIN)
 			return 0;
 
 		if (crlf_action == CRLF_AUTO_INPUT || crlf_action == CRLF_AUTO_CRLF) {
@@ -309,11 +354,13 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
 {
 	char *to_free = NULL;
 	struct text_stat stats;
+	unsigned earlyout = CONVERT_STAT_BITS_TXT_CRLF | CONVERT_STAT_BITS_BIN;
+
 
 	if (!len || output_eol(crlf_action) != EOL_CRLF)
 		return 0;
 
-	gather_stats(src, len, &stats);
+	gather_stats(src, len, &stats, earlyout);
 
 	/* No "naked" LF? Nothing to convert, regardless. */
 	if (!stats.lonelf)
@@ -327,7 +374,7 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
 				return 0;
 		}
 
-		if (convert_is_binary(len, &stats))
+		if (stats.stat_bits & CONVERT_STAT_BITS_BIN)
 			return 0;
 	}
 
-- 
2.0.0.rc1.6318.g0c2c796
Previous: Junio C HamanoNext: Junio C Hamano
Message 118 of 126 in “git reset --hard gives clean working tree”
  1. 1/3 git reset --hard gives clean working treetboegi@web.de, Feb 11, 2016
  2. Junio C HamanoFeb 11, 2016
  3. Torsten BögershausenMar 5, 2016
  4. Junio C HamanoMar 5, 2016
  5. Torsten BögershausenMar 5, 2016
  6. Junio C HamanoMar 5, 2016
  7. Junio C HamanoMar 7, 2016
  8. Junio C HamanoMar 7, 2016
  9. Torsten BögershausenMar 7, 2016
  10. Junio C HamanoMar 7, 2016
  11. 1/7 Make it possible to get sha1 for a path from the indextboegi@web.de, Mar 29, 2016
  12. Duy NguyenMar 29, 2016
  13. Duy NguyenMar 29, 2016
  14. Torsten BögershausenMar 29, 2016
  15. Eric SunshineMar 29, 2016
  16. 2/7 convert.c: stream and early outtboegi@web.de, Mar 29, 2016
  17. 3/7 Allow core.autocrlf=input and core.eol=crlftboegi@web.de, Mar 29, 2016
  18. 4/7 t0027: TC for combined attributestboegi@web.de, Mar 29, 2016
  19. 5/7 CRLF: unify the "auto" handlingtboegi@web.de, Mar 29, 2016
  20. Eric SunshineMar 29, 2016
  21. 6/7 correct blame for files commited with CRLFtboegi@web.de, Mar 29, 2016
  22. Junio C HamanoMar 29, 2016
  23. Torsten BögershausenMar 29, 2016
  24. Junio C HamanoMar 29, 2016
  25. Junio C HamanoMar 29, 2016
  26. Junio C HamanoMar 29, 2016
  27. Junio C HamanoMar 29, 2016
  28. Torsten BögershausenMar 30, 2016
  29. 7/7 convert.c: more safer crlf handling with text attributetboegi@web.de, Mar 29, 2016
  30. Junio C HamanoMar 29, 2016
  31. 1/7 Make it possible to get sha1 for a path from the indextboegi@web.de, Apr 1, 2016
  32. 2/7 convert.c: stream and early outtboegi@web.de, Apr 1, 2016
  33. 3/7 Allow core.autocrlf=input and core.eol=crlftboegi@web.de, Apr 1, 2016
  34. Junio C HamanoApr 1, 2016
  35. 4/7 t0027: TC for combined attributestboegi@web.de, Apr 1, 2016
  36. Junio C HamanoApr 1, 2016
  37. 5/7 CRLF: unify the "auto" handlingtboegi@web.de, Apr 1, 2016
  38. Junio C HamanoApr 1, 2016
  39. 6/7 correct blame for files commited with CRLFtboegi@web.de, Apr 1, 2016
  40. Junio C HamanoApr 1, 2016
  41. Torsten BögershausenApr 3, 2016
  42. 7/7 convert.c: more safer crlf handling with text attributetboegi@web.de, Apr 1, 2016
  43. correct blame for files commited with CRLFtboegi@web.de, Apr 5, 2016
  44. Junio C HamanoApr 5, 2016
  45. Junio C HamanoApr 5, 2016
  46. Torsten BögershausenApr 6, 2016
  47. 1/4 t0027: Make more reliabletboegi@web.de, Apr 19, 2016
  48. 2/4 convert: allow core.autocrlf=input and core.eol=crlftboegi@web.de, Apr 19, 2016
  49. 3/4 t0027: test cases for combined attributestboegi@web.de, Apr 19, 2016
  50. Junio C HamanoApr 19, 2016
  51. Torsten BögershausenApr 20, 2016
  52. 4/4 convert.c: ident + core.autocrlf didn't worktboegi@web.de, Apr 19, 2016
  53. Junio C HamanoApr 20, 2016
  54. 01/10 t0027: Make more reliabletboegi@web.de, Apr 22, 2016
  55. Junio C HamanoApr 22, 2016
  56. Torsten BögershausenApr 24, 2016
  57. 02/10 convert: allow core.autocrlf=input and core.eol=crlftboegi@web.de, Apr 22, 2016
  58. 03/10 t0027: test cases for combined attributestboegi@web.de, Apr 22, 2016
  59. 04/10 convert.c: ident + core.autocrlf didn't worktboegi@web.de, Apr 22, 2016
  60. 05/10 read-cache: factor out get_sha1_from_index() helpertboegi@web.de, Apr 22, 2016
  61. 06/10 convert.c: stream and early outtboegi@web.de, Apr 22, 2016
  62. 07/10 convert: unify the "auto" handling of CRLFtboegi@web.de, Apr 22, 2016
  63. 08/10 convert.c: more safer crlf handling with text attributetboegi@web.de, Apr 22, 2016
  64. 09/10 t6038; use crlf on all platformstboegi@web.de, Apr 22, 2016
  65. 10/10 ce_compare_data() did not respect conversiontboegi@web.de, Apr 22, 2016
  66. 01/10 t0027: Make commit_chk_wrnNNO() reliabletboegi@web.de, Apr 24, 2016
  67. 02/10 convert: allow core.autocrlf=input and core.eol=crlftboegi@web.de, Apr 24, 2016
  68. 03/10 t0027: test cases for combined attributestboegi@web.de, Apr 24, 2016
  69. 04/10 convert.c: ident + core.autocrlf didn't worktboegi@web.de, Apr 24, 2016
  70. 05/10 read-cache: factor out get_sha1_from_index() helpertboegi@web.de, Apr 24, 2016
  71. 06/10 convert.c: stream and early outtboegi@web.de, Apr 24, 2016
  72. 07/10 convert: unify the "auto" handling of CRLFtboegi@web.de, Apr 24, 2016
  73. 08/10 convert.c: more safer crlf handling with text attributetboegi@web.de, Apr 24, 2016
  74. 09/10 t6038; use crlf on all platformstboegi@web.de, Apr 24, 2016
  75. 10/10 ce_compare_data() did not respect conversiontboegi@web.de, Apr 24, 2016
  76. 01/10 t0027: Make commit_chk_wrnNNO() reliabletboegi@web.de, Apr 25, 2016
  77. Junio C HamanoApr 25, 2016
  78. 02/10 convert: allow core.autocrlf=input and core.eol=crlftboegi@web.de, Apr 25, 2016
  79. 03/10 t0027: test cases for combined attributestboegi@web.de, Apr 25, 2016
  80. 04/10 convert.c: ident + core.autocrlf didn't worktboegi@web.de, Apr 25, 2016
  81. 05/10 read-cache: factor out get_sha1_from_index() helpertboegi@web.de, Apr 25, 2016
  82. 06/10 convert.c: stream and early outtboegi@web.de, Apr 25, 2016
  83. 07/10 convert: unify the "auto" handling of CRLFtboegi@web.de, Apr 25, 2016
  84. Junio C HamanoApr 25, 2016
  85. Torsten BögershausenApr 26, 2016
  86. Junio C HamanoApr 26, 2016
  87. 08/10 convert.c: more safer crlf handling with text attributetboegi@web.de, Apr 25, 2016
  88. 09/10 t6038; use crlf on all platformstboegi@web.de, Apr 25, 2016
  89. 10/10 ce_compare_data() did not respect conversiontboegi@web.de, Apr 25, 2016
  90. 01/10 t0027: make commit_chk_wrnNNO() reliabletboegi@web.de, Apr 29, 2016
  91. 02/10 convert: allow core.autocrlf=input and core.eol=crlftboegi@web.de, Apr 29, 2016
  92. 03/10 t0027: test cases for combined attributestboegi@web.de, Apr 29, 2016
  93. 04/10 convert.c: ident + core.autocrlf didn't worktboegi@web.de, Apr 29, 2016
  94. 05/10 read-cache: factor out get_sha1_from_index() helpertboegi@web.de, Apr 29, 2016
  95. 06/10 convert.c: stream and early outtboegi@web.de, Apr 29, 2016
  96. 07/10 convert: unify the "auto" handling of CRLFtboegi@web.de, Apr 29, 2016
  97. Torsten BögershausenNov 25, 2016
  98. 1/1 New way to normalize the line endingstboegi@web.de, Nov 27, 2016
  99. Junio C HamanoNov 29, 2016
  100. 1/1 Document how to normalize the line endingstboegi@web.de, Apr 12, 2017
  101. 08/10 convert.c: more safer crlf handling with text attributetboegi@web.de, Apr 29, 2016
  102. 09/10 t6038; use crlf on all platformstboegi@web.de, Apr 29, 2016
  103. 10/10 ce_compare_data() did not respect conversiontboegi@web.de, Apr 29, 2016
  104. Junio C HamanoApr 29, 2016
  105. Junio C HamanoApr 29, 2016
  106. Torsten BögershausenMay 1, 2016
  107. Junio C HamanoMay 2, 2016
  108. Junio C HamanoMay 2, 2016
  109. Torsten BögershausenMay 3, 2016
  110. Junio C HamanoMay 3, 2016
  111. Torsten BögershausenMay 4, 2016
  112. Junio C HamanoMay 4, 2016
  113. Torsten BögershausenMay 6, 2016
  114. Junio C HamanoMay 6, 2016
  115. 0/6 convert-eol-autocrlf, old 5..10 now 1..6tboegi@web.de, May 7, 2016
  116. 1/6 read-cache: factor out get_sha1_from_index() helpertboegi@web.de, May 7, 2016
  117. Junio C HamanoMay 9, 2016
  118. 2/6 convert.c: stream and early outtboegi@web.de, May 7, 2016
  119. Junio C HamanoMay 9, 2016
  120. Torsten BögershausenMay 11, 2016
  121. 3/6 convert: unify the "auto" handling of CRLFtboegi@web.de, May 7, 2016
  122. 4/6 convert.c: more safer crlf handling with text attributetboegi@web.de, May 7, 2016
  123. 5/6 t6038; use crlf on all platformstboegi@web.de, May 7, 2016
  124. 6/6 convert: ce_compare_data() checks for a sha1 of a pathtboegi@web.de, May 7, 2016
  125. 2/3 Factor out convert_cmp_checkout() into convert.ctboegi@web.de, Feb 11, 2016
  126. 3/3 convert.c: Optimize convert_cmp_checkout() for changed file lentboegi@web.de, Feb 11, 2016

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.