Hash :
585b5dac
Author :
Date :
2017-11-18T15:43:11
refcount: make refcounting conform to aliasing rules Strict aliasing rules dictate that for most data types, you are not allowed to cast them to another data type and then access the casted pointers. While this works just fine for most compilers, technically we end up in undefined behaviour when we hurt that rule. Our current refcounting code makes heavy use of casting and thus violates that rule. While we didn't have any problems with that code, Travis started spitting out a lot of warnings due to a change in their toolchain. In the refcounting case, the code is also easy to fix: as all refcounting-statements are actually macros, we can just access the `rc` field directly instead of casting. There are two outliers in our code where that doesn't work. Both the `git_diff` and `git_patch` structures have specializations for generated and parsed diffs/patches, which directly inherit from them. Because of that, the refcounting code is only part of the base structure and not of the children themselves. We can help that by instead passing their base into `GIT_REFCOUNT_INC`, though.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "diff_parse.h"
#include "diff.h"
#include "patch.h"
#include "patch_parse.h"
static void diff_parsed_free(git_diff *d)
{
git_diff_parsed *diff = (git_diff_parsed *)d;
git_patch *patch;
size_t i;
git_vector_foreach(&diff->patches, i, patch)
git_patch_free(patch);
git_vector_free(&diff->patches);
git_vector_free(&diff->base.deltas);
git_pool_clear(&diff->base.pool);
git__memzero(diff, sizeof(*diff));
git__free(diff);
}
static git_diff_parsed *diff_parsed_alloc(void)
{
git_diff_parsed *diff;
if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL)
return NULL;
GIT_REFCOUNT_INC(&diff->base);
diff->base.type = GIT_DIFF_TYPE_PARSED;
diff->base.strcomp = git__strcmp;
diff->base.strncomp = git__strncmp;
diff->base.pfxcomp = git__prefixcmp;
diff->base.entrycomp = git_diff__entry_cmp;
diff->base.patch_fn = git_patch_parsed_from_diff;
diff->base.free_fn = diff_parsed_free;
if (git_diff_init_options(&diff->base.opts, GIT_DIFF_OPTIONS_VERSION) < 0) {
git__free(diff);
return NULL;
}
diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
git_pool_init(&diff->base.pool, 1);
if (git_vector_init(&diff->patches, 0, NULL) < 0 ||
git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
git_diff_free(&diff->base);
return NULL;
}
git_vector_set_cmp(&diff->base.deltas, git_diff_delta__cmp);
return diff;
}
int git_diff_from_buffer(
git_diff **out,
const char *content,
size_t content_len)
{
git_diff_parsed *diff;
git_patch *patch;
git_patch_parse_ctx *ctx = NULL;
int error = 0;
*out = NULL;
diff = diff_parsed_alloc();
GITERR_CHECK_ALLOC(diff);
ctx = git_patch_parse_ctx_init(content, content_len, NULL);
GITERR_CHECK_ALLOC(ctx);
while (ctx->parse_ctx.remain_len) {
if ((error = git_patch_parse(&patch, ctx)) < 0)
break;
git_vector_insert(&diff->patches, patch);
git_vector_insert(&diff->base.deltas, patch->delta);
}
if (error == GIT_ENOTFOUND && git_vector_length(&diff->patches) > 0) {
giterr_clear();
error = 0;
}
git_patch_parse_ctx_free(ctx);
if (error < 0)
git_diff_free(&diff->base);
else
*out = &diff->base;
return error;
}