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

Re: [PATCH] http: Add Accept-Language header if possible

From
Junio C Hamano <gitster@pobox.com>
Date
Jan 27, 2015, 23:34 UTC
Message-ID
<xmqqwq47iyrn.fsf@gitster.dls.corp.google.com>
In-Reply-To
<1422373918-14132-2-git-send-email-eungjun.yi@navercorp.com>
Yi EungJun <semtlenori@gmail.com> writes:
Show 44 quoted lines
> +static void write_accept_language(struct strbuf *buf)
> +{
> +	/*
> +	 * MAX_DECIMAL_PLACES must not be larger than 3. If it is larger than
> +	 * that, q-value will be smaller than 0.001, the minimum q-value the
> +	 * HTTP specification allows. See
> +	 * http://tools.ietf.org/html/rfc7231#section-5.3.1 for q-value.
> +	 */
> +	const int MAX_DECIMAL_PLACES = 3;
> +	const int MAX_LANGUAGE_TAGS = 1000;
> +	const int MAX_ACCEPT_LANGUAGE_HEADER_SIZE = 4000;
> +	struct strbuf *language_tags = NULL;
> +	int num_langs = 0;
> +	const char *s = get_preferred_languages();
> +	int i;
> +	struct strbuf tag = STRBUF_INIT;
> +
> +	/* Don't add Accept-Language header if no language is preferred. */
> +	if (!s)
> +		return;
> +
> +	/*
> +	 * Split the colon-separated string of preferred languages into
> +	 * language_tags array.
> +	 */
> +	do {
> +		/* collect language tag */
> +		for (; *s && (isalnum(*s) || *s == '_'); s++)
> +			strbuf_addch(&tag, *s == '_' ? '-' : *s);
> +
> +		/* skip .codeset, @modifier and any other unnecessary parts */
> +		while (*s && *s != ':')
> +			s++;
> +
> +		if (tag.len) {
> +			num_langs++;
> +			REALLOC_ARRAY(language_tags, num_langs);
> +			strbuf_init(&language_tags[num_langs - 1], 0);
> +			strbuf_swap(&tag, &language_tags[num_langs - 1]);
> +
> +			if (num_langs >= MAX_LANGUAGE_TAGS - 1) /* -1 for '*' */
> +				break;
> +		}
> +	} while (*s++);

The structure of this function is much easier to understand than any of the previous rounds. You collect up to the max you are going to support, and then you format up to the max you are going to send.

Very straight-forward and simple.
> +	/* write Accept-Language header into buf */
> +	if (num_langs >= 1) {
micronit: should be OK to just say "if (num_langs)".
Show 15 quoted lines
> +		int last_buf_len = 0;
> +		int max_q;
> +		int decimal_places;
> +		char q_format[32];
> +
> +		/* add '*' */
> +		REALLOC_ARRAY(language_tags, num_langs + 1);
> +		strbuf_init(&language_tags[num_langs], 0);
> +		strbuf_addstr(&language_tags[num_langs++], "*");
> +
> +		/* compute decimal_places */
> +		for (max_q = 1, decimal_places = 0;
> +				max_q < num_langs && decimal_places <= MAX_DECIMAL_PLACES;
> +				decimal_places++, max_q *= 10)
> +			;

micronit: the second and the third line are indented too deeply and made me wonder if this has an overlong first line (i.e. the set-up part to enter the for-loop) split into multiple lines.

Show 10 quoted lines
> +
> +		sprintf(q_format, ";q=0.%%0%dd", decimal_places);
> +
> +		strbuf_addstr(buf, "Accept-Language: ");
> +
> +		for(i = 0; i < num_langs; i++) {
> +			if (i > 0)
> +				strbuf_addstr(buf, ", ");
> +
> +			strbuf_addstr(buf, strbuf_detach(&language_tags[i], NULL));
This is not wrong per-se, but it looks somewhat convoluted to me.

You can just peek language_tags[i].buf here without detaching, as you will free the strbufs after you exit the loop anyway. Your version is not wrong and it does not result in double-freeing, because detach clears the strbuf, but at the same time, makes the responsibility to free language_tags[] strbuf split into here (for elements up to the ones that are used to fill buf) and the cleanup loop (for elements that are not used in this loop).

Show 18 quoted lines
> +			if (i > 0)
> +				strbuf_addf(buf, q_format, max_q - i);
> +
> +			if (buf->len > MAX_ACCEPT_LANGUAGE_HEADER_SIZE) {
> +				strbuf_remove(buf, last_buf_len, buf->len - last_buf_len);
> +				break;
> +			}
> +
> +			last_buf_len = buf->len;
> +		}
> +	}
> +
> +	/* free language tags */
> +	for(i = 0; i < num_langs; i++) {
> +		strbuf_release(&language_tags[i]);
> +	}
> +	free(language_tags);
> +}

I am wondering if using strbuf for each of the language_tags[] is even necessary. How about doing it this way instead?

 http.c | 22 +++++++++-------------
 1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/http.c b/http.c
index 6111c6a..db591b3 100644
--- a/http.c
+++ b/http.c
@@ -1027,7 +1027,7 @@ static void write_accept_language(struct strbuf *buf)
 	const int MAX_DECIMAL_PLACES = 3;
 	const int MAX_LANGUAGE_TAGS = 1000;
 	const int MAX_ACCEPT_LANGUAGE_HEADER_SIZE = 4000;
-	struct strbuf *language_tags = NULL;
+	char **language_tags = NULL;
 	int num_langs = 0;
 	const char *s = get_preferred_languages();
 	int i;
@@ -1053,9 +1053,7 @@ static void write_accept_language(struct strbuf *buf)
 		if (tag.len) {
 			num_langs++;
 			REALLOC_ARRAY(language_tags, num_langs);
-			strbuf_init(&language_tags[num_langs - 1], 0);
-			strbuf_swap(&tag, &language_tags[num_langs - 1]);
-
+			language_tags[num_langs - 1] = strbuf_detach(&tag, NULL);
 			if (num_langs >= MAX_LANGUAGE_TAGS - 1) /* -1 for '*' */
 				break;
 		}
@@ -1070,13 +1068,12 @@ static void write_accept_language(struct strbuf *buf)
 
 		/* add '*' */
 		REALLOC_ARRAY(language_tags, num_langs + 1);
-		strbuf_init(&language_tags[num_langs], 0);
-		strbuf_addstr(&language_tags[num_langs++], "*");
+		language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
 
 		/* compute decimal_places */
 		for (max_q = 1, decimal_places = 0;
-				max_q < num_langs && decimal_places <= MAX_DECIMAL_PLACES;
-				decimal_places++, max_q *= 10)
+		     max_q < num_langs && decimal_places <= MAX_DECIMAL_PLACES;
+		     decimal_places++, max_q *= 10)
 			;
 
 		sprintf(q_format, ";q=0.%%0%dd", decimal_places);
@@ -1087,7 +1084,7 @@ static void write_accept_language(struct strbuf *buf)
 			if (i > 0)
 				strbuf_addstr(buf, ", ");
 
-			strbuf_addstr(buf, strbuf_detach(&language_tags[i], NULL));
+			strbuf_addstr(buf, language_tags[i]);
 
 			if (i > 0)
 				strbuf_addf(buf, q_format, max_q - i);
@@ -1101,10 +1098,9 @@ static void write_accept_language(struct strbuf *buf)
 		}
 	}
 
-	/* free language tags */
-	for(i = 0; i < num_langs; i++) {
-		strbuf_release(&language_tags[i]);
-	}
+	/* free language tags -- last one is a static '*' */
+	for(i = 0; i < num_langs - 1; i++)
+		free(language_tags[i]);
 	free(language_tags);
 }
 
Previous: Yi EungJunNext: Junio C Hamano
Message 24 of 46 in “http: Add Accept-Language header if possible”
  1. 0/1 http: Add Accept-Language header if possibleYi EungJun, Jul 19, 2014
  2. 1/1 http: Add Accept-Language header if possibleYi EungJun, Jul 19, 2014
  3. Junio C HamanoJul 21, 2014
  4. Yi, EungJunAug 3, 2014
  5. 0/1 http: Add Accept-Language header if possibleYi EungJun, Dec 2, 2014
  6. 1/1 http: Add Accept-Language header if possibleYi EungJun, Dec 2, 2014
  7. Junio C HamanoDec 3, 2014
  8. Eric SunshineDec 3, 2014
  9. Junio C HamanoDec 3, 2014
  10. Michael BlumeDec 3, 2014
  11. Michael BlumeDec 3, 2014
  12. 0/1 http: Add Accept-Language header if possibleYi EungJun, Dec 22, 2014
  13. 1/1 http: Add Accept-Language header if possibleYi EungJun, Dec 22, 2014
  14. Junio C HamanoDec 22, 2014
  15. Eric SunshineDec 24, 2014
  16. Junio C HamanoDec 29, 2014
  17. 0/1 http: Add Accept-Language header if possibleYi EungJun, Jan 18, 2015
  18. 1/1 http: Add Accept-Language header if possibleYi EungJun, Jan 18, 2015
  19. Torsten BögershausenJan 18, 2015
  20. Eric SunshineJan 19, 2015
  21. Junio C HamanoJan 22, 2015
  22. 0/1 http: Add Accept-Language header if possibleYi EungJun, Jan 27, 2015
  23. http: Add Accept-Language header if possibleYi EungJun, Jan 27, 2015
  24. Junio C HamanoJan 27, 2015
  25. Junio C HamanoJan 28, 2015
  26. Yi, EungJunJan 28, 2015
  27. 0/1 http: Add Accept-Language header if possibleYi EungJun, Jan 28, 2015
  28. 1/1 http: Add Accept-Language header if possibleYi EungJun, Jan 28, 2015
  29. Junio C HamanoFeb 25, 2015
  30. Jeff KingFeb 26, 2015
  31. Jeff KingFeb 26, 2015
  32. Junio C HamanoFeb 26, 2015
  33. Jeff KingFeb 26, 2015
  34. Junio C HamanoFeb 26, 2015
  35. Stefan BellerFeb 26, 2015
  36. Jeff KingFeb 26, 2015
  37. Jeff KingFeb 26, 2015
  38. Stefan BellerFeb 26, 2015
  39. Jeff KingFeb 26, 2015
  40. Jeff KingFeb 26, 2015
  41. Junio C HamanoFeb 26, 2015
  42. Junio C HamanoFeb 26, 2015
  43. Junio C HamanoJan 29, 2015
  44. Yi, EungJunJan 30, 2015
  45. http: Include locale.h when using setlocale()Ævar Arnfjörð Bjarmason, Mar 6, 2015
  46. Junio C HamanoMar 6, 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.