Commit 3658e81e by Russell Belfer

Move crlf conversion into buf_text

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.
parent 050ab995
......@@ -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)
{
......
......@@ -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;
......
......@@ -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
......
......@@ -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 */
......
......@@ -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);
}
......@@ -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)
{
......
......@@ -9,7 +9,6 @@
#include "common.h"
#include "buffer.h"
#include "buf_text.h"
#include "git2/odb.h"
#include "git2/repository.h"
......
......@@ -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);
}
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment