summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-03-25 14:20:07 -0700
committerRussell Belfer <rb@github.com>2013-03-25 14:20:07 -0700
commit3658e81e3499f874dc9f323d4d9127df7e219e12 (patch)
tree468b0ba5739118b56d1961063655ffbbf95d8a12
parent050ab9950d089c55e874b63e5ab9c8d0b948cf46 (diff)
downloadlibgit2-autocrlf-fixes.tar.gz
Move crlf conversion into buf_textautocrlf-fixes
This adds crlf/lf conversion functions into buf_text with more efficient implementations that bypass the high level buffer functions. They attempt to minimize the number of reallocations done and they directly write the buffer data as needed if they know that there is enough memory allocated to memcpy data. Tests are added for these new functions. The crlf.c code is updated to use the new functions. Removed the include of buf_text.h from filter.h and just include it more narrowly in the places that need it.
-rw-r--r--src/blob.c1
-rw-r--r--src/buf_text.c77
-rw-r--r--src/buf_text.h14
-rw-r--r--src/checkout.c1
-rw-r--r--src/crlf.c80
-rw-r--r--src/diff_output.c1
-rw-r--r--src/filter.h1
-rw-r--r--tests-clar/core/buffer.c82
-rw-r--r--tests-clar/object/blob/filter.c1
9 files changed, 194 insertions, 64 deletions
diff --git a/src/blob.c b/src/blob.c
index 3ff141c7f..c0514fc13 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -12,6 +12,7 @@
#include "common.h"
#include "blob.h"
#include "filter.h"
+#include "buf_text.h"
const void *git_blob_rawcontent(const git_blob *blob)
{
diff --git a/src/buf_text.c b/src/buf_text.c
index 3a8f442b4..443454b5f 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -60,6 +60,83 @@ void git_buf_text_unescape(git_buf *buf)
buf->size = git__unescape(buf->ptr);
}
+int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
+{
+ const char *scan = src->ptr;
+ const char *scan_end = src->ptr + src->size;
+ const char *next = memchr(scan, '\r', src->size);
+ char *out;
+
+ assert(tgt != src);
+
+ if (!next)
+ return GIT_ENOTFOUND;
+
+ /* reduce reallocs while in the loop */
+ if (git_buf_grow(tgt, src->size) < 0)
+ return -1;
+ out = tgt->ptr;
+ tgt->size = 0;
+
+ /* Find the next \r and copy whole chunk up to there to tgt */
+ for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
+ if (next > scan) {
+ size_t copylen = next - scan;
+ memcpy(out, scan, copylen);
+ out += copylen;
+ }
+
+ /* Do not drop \r unless it is followed by \n */
+ if (next[1] != '\n')
+ *out++ = '\r';
+ }
+
+ /* Copy remaining input into dest */
+ memcpy(out, scan, scan_end - scan + 1); /* +1 for NUL byte */
+ out += (scan_end - scan);
+ tgt->size = out - tgt->ptr;
+
+ return 0;
+}
+
+int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
+{
+ const char *start = src->ptr;
+ const char *end = start + src->size;
+ const char *scan = start;
+ const char *next = memchr(scan, '\n', src->size);
+
+ assert(tgt != src);
+
+ if (!next)
+ return GIT_ENOTFOUND;
+
+ /* attempt to reduce reallocs while in the loop */
+ if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
+ return -1;
+ tgt->size = 0;
+
+ for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
+ size_t copylen = next - scan;
+ /* don't convert existing \r\n to \r\r\n */
+ size_t extralen = (next > start && next[-1] == '\r') ? 1 : 2;
+ size_t needsize = tgt->size + copylen + extralen + 1;
+
+ if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0)
+ return -1;
+
+ if (next > scan) {
+ memcpy(tgt->ptr + tgt->size, scan, copylen);
+ tgt->size += copylen;
+ }
+ if (extralen == 2)
+ tgt->ptr[tgt->size++] = '\r';
+ tgt->ptr[tgt->size++] = '\n';
+ }
+
+ return git_buf_put(tgt, scan, end - scan);
+}
+
int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings)
{
size_t i;
diff --git a/src/buf_text.h b/src/buf_text.h
index 458ee33c9..58e4e26a7 100644
--- a/src/buf_text.h
+++ b/src/buf_text.h
@@ -56,6 +56,20 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
extern void git_buf_text_unescape(git_buf *buf);
/**
+ * Replace all \r\n with \n (or do nothing if no \r\n are found)
+ *
+ * @return 0 on success, GIT_ENOTFOUND if no \r\n, -1 on memory error
+ */
+extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
+
+/**
+ * Replace all \n with \r\n (or do nothing if no \n are found)
+ *
+ * @return 0 on success, GIT_ENOTFOUND if no \n, -1 on memory error
+ */
+extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
+
+/**
* Fill buffer with the common prefix of a array of strings
*
* Buffer will be set to empty if there is no common prefix
diff --git a/src/checkout.c b/src/checkout.c
index e52649aec..5a2698e41 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -23,6 +23,7 @@
#include "blob.h"
#include "diff.h"
#include "pathspec.h"
+#include "buf_text.h"
/* See docs/checkout-internals.md for more information */
diff --git a/src/crlf.c b/src/crlf.c
index cd6d9825f..81268da83 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -9,6 +9,7 @@
#include "fileops.h"
#include "hash.h"
#include "filter.h"
+#include "buf_text.h"
#include "repository.h"
#include "git2/attr.h"
#include "git2/blob.h"
@@ -105,35 +106,6 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con
return -1;
}
-static int drop_crlf(git_buf *dest, const git_buf *source)
-{
- const char *scan = source->ptr, *next;
- const char *scan_end = git_buf_cstr(source) + git_buf_len(source);
-
- /* Main scan loop. Find the next carriage return and copy the
- * whole chunk up to that point to the destination buffer.
- */
- while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
- /* copy input up to \r */
- if (next > scan)
- git_buf_put(dest, scan, next - scan);
-
- /* Do not drop \r unless it is followed by \n */
- if (*(next + 1) != '\n')
- git_buf_putc(dest, '\r');
-
- scan = next + 1;
- }
-
- /* If there was no \r, then tell the library to skip this filter */
- if (scan == source->ptr)
- return -1;
-
- /* Copy remaining input into dest */
- git_buf_put(dest, scan, scan_end - scan);
- return 0;
-}
-
static int has_cr_in_index(git_filter *self)
{
struct crlf_filter *filter = (struct crlf_filter *)self;
@@ -217,29 +189,7 @@ static int crlf_apply_to_odb(
}
/* Actually drop the carriage returns */
- return drop_crlf(dest, source);
-}
-
-static int convert_line_endings(git_buf *dest, const git_buf *source, const char *ending)
-{
- const char *scan = git_buf_cstr(source),
- *next,
- *line_end,
- *scan_end = git_buf_cstr(source) + git_buf_len(source);
-
- while ((next = memchr(scan, '\n', scan_end - scan)) != NULL) {
- if (next > scan) {
- line_end = *(next - 1) == '\r' ? next - 1 : next;
- git_buf_put(dest, scan, line_end - scan);
- scan = next + 1;
- }
-
- git_buf_puts(dest, ending);
- scan = next + 1;
- }
-
- git_buf_put(dest, scan, scan_end - scan);
- return 0;
+ return git_buf_text_crlf_to_lf(dest, source);
}
static const char *line_ending(struct crlf_filter *filter)
@@ -282,26 +232,28 @@ line_ending_error:
return NULL;
}
-static int crlf_apply_to_workdir(git_filter *self, git_buf *dest, const git_buf *source)
+static int crlf_apply_to_workdir(
+ git_filter *self, git_buf *dest, const git_buf *source)
{
struct crlf_filter *filter = (struct crlf_filter *)self;
const char *workdir_ending = NULL;
- assert (self && dest && source);
+ assert(self && dest && source);
/* Empty file? Nothing to do. */
if (git_buf_len(source) == 0)
- return 0;
+ return -1;
/* Determine proper line ending */
workdir_ending = line_ending(filter);
- if (!workdir_ending) return -1;
-
- /* If the line ending is '\n', just copy the input */
- if (!strcmp(workdir_ending, "\n"))
- return git_buf_puts(dest, git_buf_cstr(source));
+ if (!workdir_ending)
+ return -1;
+ if (!strcmp("\n", workdir_ending)) /* do nothing for \n ending */
+ return -1;
- return convert_line_endings(dest, source, workdir_ending);
+ /* for now, only lf->crlf conversion is supported here */
+ assert(!strcmp("\r\n", workdir_ending));
+ return git_buf_text_lf_to_crlf(dest, source);
}
static int find_and_add_filter(
@@ -351,12 +303,14 @@ static int find_and_add_filter(
return git_vector_insert(filters, filter);
}
-int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
+int git_filter_add__crlf_to_odb(
+ git_vector *filters, git_repository *repo, const char *path)
{
return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb);
}
-int git_filter_add__crlf_to_workdir(git_vector *filters, git_repository *repo, const char *path)
+int git_filter_add__crlf_to_workdir(
+ git_vector *filters, git_repository *repo, const char *path)
{
return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir);
}
diff --git a/src/diff_output.c b/src/diff_output.c
index fba6129b7..e8dd5b317 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -12,6 +12,7 @@
#include <ctype.h>
#include "fileops.h"
#include "filter.h"
+#include "buf_text.h"
static int read_next_int(const char **str, int *value)
{
diff --git a/src/filter.h b/src/filter.h
index 0ca71656b..42a44ebdb 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -9,7 +9,6 @@
#include "common.h"
#include "buffer.h"
-#include "buf_text.h"
#include "git2/odb.h"
#include "git2/repository.h"
diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c
index cb5f0161b..3d8221e04 100644
--- a/tests-clar/core/buffer.c
+++ b/tests-clar/core/buffer.c
@@ -904,3 +904,85 @@ void test_core_buffer__similarity_metric_whitespace(void)
git_buf_free(&buf);
}
+
+#define check_buf(expected,buf) do { \
+ cl_assert_equal_s(expected, buf.ptr); \
+ cl_assert_equal_sz(strlen(expected), buf.size); } while (0)
+
+void test_core_buffer__lf_and_crlf_conversions(void)
+{
+ git_buf src = GIT_BUF_INIT, tgt = GIT_BUF_INIT;
+
+ /* LF source */
+
+ git_buf_sets(&src, "lf\nlf\nlf\nlf\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src));
+ /* no conversion needed if all LFs already */
+
+ git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src));
+ /* no conversion needed if all LFs already */
+
+ /* CRLF source */
+
+ git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
+ check_buf(src.ptr, tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
+
+ git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
+ check_buf(src.ptr, tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
+
+ /* CRLF in LF text */
+
+ git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
+
+ /* LF in CRLF text */
+
+ git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
+
+ /* bare CR test */
+
+ git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
+
+ git_buf_sets(&src, "\rcr\r");
+ cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_lf_to_crlf(&tgt, &src));
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\rcr\r", tgt);
+
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+}
diff --git a/tests-clar/object/blob/filter.c b/tests-clar/object/blob/filter.c
index b9bbfff0c..042bddab7 100644
--- a/tests-clar/object/blob/filter.c
+++ b/tests-clar/object/blob/filter.c
@@ -2,6 +2,7 @@
#include "posix.h"
#include "blob.h"
#include "filter.h"
+#include "buf_text.h"
static git_repository *g_repo = NULL;
#define NUM_TEST_OBJECTS 8