Git fork

Merge branch 'ps/rust-balloon'

Dip our toes a bit to (optionally) use Rust implemented helper
called from our C code.

* ps/rust-balloon:
ci: enable Rust for breaking-changes jobs
ci: convert "pedantic" job into full build with breaking changes
BreakingChanges: announce Rust becoming mandatory
varint: reimplement as test balloon for Rust
varint: use explicit width for integers
help: report on whether or not Rust is enabled
Makefile: introduce infrastructure to build internal Rust library
Makefile: reorder sources after includes
meson: add infrastructure to build internal Rust library

+410 -131
+2 -2
.github/workflows/main.yml
··· 379 379 - jobname: linux-breaking-changes 380 380 cc: gcc 381 381 image: ubuntu:rolling 382 + - jobname: fedora-breaking-changes-meson 383 + image: fedora:latest 382 384 - jobname: linux-leaks 383 385 image: ubuntu:rolling 384 386 cc: gcc ··· 396 398 # Supported until 2025-04-02. 397 399 - jobname: linux32 398 400 image: i386/ubuntu:focal 399 - - jobname: pedantic 400 - image: fedora:latest 401 401 # A RHEL 8 compatible distro. Supported until 2029-05-31. 402 402 - jobname: almalinux-8 403 403 image: almalinux:8
+2
.gitignore
··· 1 1 /fuzz_corpora 2 + /target/ 3 + /Cargo.lock 2 4 /GIT-BUILD-DIR 3 5 /GIT-BUILD-OPTIONS 4 6 /GIT-CFLAGS
+2 -2
.gitlab-ci.yml
··· 45 45 - jobname: linux-breaking-changes 46 46 image: ubuntu:20.04 47 47 CC: gcc 48 + - jobname: fedora-breaking-changes-meson 49 + image: fedora:latest 48 50 - jobname: linux-TEST-vars 49 51 image: ubuntu:20.04 50 52 CC: gcc ··· 58 60 - jobname: linux-asan-ubsan 59 61 image: ubuntu:rolling 60 62 CC: clang 61 - - jobname: pedantic 62 - image: fedora:latest 63 63 - jobname: linux-musl-meson 64 64 image: alpine:latest 65 65 - jobname: linux32
+9
Cargo.toml
··· 1 + [package] 2 + name = "gitcore" 3 + version = "0.1.0" 4 + edition = "2018" 5 + 6 + [lib] 7 + crate-type = ["staticlib"] 8 + 9 + [dependencies]
+45
Documentation/BreakingChanges.adoc
··· 171 171 matches the default branch name used in new repositories by many of the 172 172 big Git forges. 173 173 174 + * Git will require Rust as a mandatory part of the build process. While Git 175 + already started to adopt Rust in Git 2.49, all parts written in Rust are 176 + optional for the time being. This includes: 177 + + 178 + ** The Rust wrapper around libgit.a that is part of "contrib/" and which has 179 + been introduced in Git 2.49. 180 + ** Subsystems that have an alternative implementation in Rust to test 181 + interoperability between our C and Rust codebase. 182 + ** Newly written features that are not mission critical for a fully functional 183 + Git client. 184 + + 185 + These changes are meant as test balloons to allow distributors of Git to prepare 186 + for Rust becoming a mandatory part of the build process. There will be multiple 187 + milestones for the introduction of Rust: 188 + + 189 + -- 190 + 1. Initially, with Git 2.52, support for Rust will be auto-detected by Meson and 191 + disabled in our Makefile so that the project can sort out the initial 192 + infrastructure. 193 + 2. In Git 2.53, both build systems will default-enable support for Rust. 194 + Consequently, builds will break by default if Rust is not available on the 195 + build host. The use of Rust can still be explicitly disabled via build 196 + flags. 197 + 3. In Git 3.0, the build options will be removed and support for Rust is 198 + mandatory. 199 + -- 200 + + 201 + You can explicitly ask both Meson and our Makefile-based system to enable Rust 202 + by saying `meson configure -Drust=enabled` and `make WITH_RUST=YesPlease`, 203 + respectively. 204 + + 205 + The Git project will declare the last version before Git 3.0 to be a long-term 206 + support release. This long-term release will receive important bug fixes for at 207 + least four release cycles and security fixes for six release cycles. The Git 208 + project will hand over maintainership of the long-term release to distributors 209 + in case they need to extend the life of that long-term release even further. 210 + Details of how this long-term release will be handed over to the community will 211 + be discussed once the Git project decides to stop officially supporting it. 212 + + 213 + We will evaluate the impact on downstream distributions before making Rust 214 + mandatory in Git 3.0. If we see that the impact on downstream distributions 215 + would be significant, we may decide to defer this change to a subsequent minor 216 + release. This evaluation will also take into account our own experience with 217 + how painful it is to keep Rust an optional component. 218 + 174 219 === Removals 175 220 176 221 * Support for grafting commits has long been superseded by git-replace(1).
+127 -87
Makefile
··· 483 483 # Define LIBPCREDIR=/foo/bar if your PCRE header and library files are 484 484 # in /foo/bar/include and /foo/bar/lib directories. 485 485 # 486 + # == Optional Rust support == 487 + # 488 + # Define WITH_RUST if you want to include features and subsystems written in 489 + # Rust into Git. For now, Rust is still an optional feature of the build 490 + # process. With Git 3.0 though, Rust will always be enabled. 491 + # 492 + # Building Rust code requires Cargo. 493 + # 486 494 # == SHA-1 and SHA-256 defines == 487 495 # 488 496 # === SHA-1 backend === ··· 683 691 OTHER_PROGRAMS = 684 692 PROGRAM_OBJS = 685 693 PROGRAMS = 694 + RUST_SOURCES = 686 695 EXCLUDED_PROGRAMS = 687 696 SCRIPT_PERL = 688 697 SCRIPT_PYTHON = ··· 920 929 LIB_FILE = libgit.a 921 930 XDIFF_LIB = xdiff/lib.a 922 931 REFTABLE_LIB = reftable/libreftable.a 932 + ifdef DEBUG 933 + RUST_LIB = target/debug/libgitcore.a 934 + else 935 + RUST_LIB = target/release/libgitcore.a 936 + endif 937 + 938 + # xdiff and reftable libs may in turn depend on what is in libgit.a 939 + GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE) 940 + EXTLIBS = 941 + 942 + GIT_USER_AGENT = git/$(GIT_VERSION) 943 + 944 + ifeq ($(wildcard sha1collisiondetection/lib/sha1.h),sha1collisiondetection/lib/sha1.h) 945 + DC_SHA1_SUBMODULE = auto 946 + endif 947 + 948 + # Set CFLAGS, LDFLAGS and other *FLAGS variables. These might be 949 + # tweaked by config.* below as well as the command-line, both of 950 + # which'll override these defaults. 951 + # Older versions of GCC may require adding "-std=gnu99" at the end. 952 + CFLAGS = -g -O2 -Wall 953 + LDFLAGS = 954 + CC_LD_DYNPATH = -Wl,-rpath, 955 + BASIC_CFLAGS = -I. 956 + BASIC_LDFLAGS = 957 + 958 + # library flags 959 + ARFLAGS = rcs 960 + PTHREAD_CFLAGS = 961 + 962 + # Rust flags 963 + CARGO_ARGS = 964 + ifndef V 965 + CARGO_ARGS += --quiet 966 + endif 967 + ifndef DEBUG 968 + CARGO_ARGS += --release 969 + endif 970 + 971 + # For the 'sparse' target 972 + SPARSE_FLAGS ?= -std=gnu99 -D__STDC_NO_VLA__ 973 + SP_EXTRA_FLAGS = 974 + 975 + # For informing GIT-BUILD-OPTIONS of the SANITIZE=leak,address targets 976 + SANITIZE_LEAK = 977 + SANITIZE_ADDRESS = 978 + 979 + # For the 'coccicheck' target 980 + SPATCH_INCLUDE_FLAGS = --all-includes 981 + SPATCH_FLAGS = 982 + SPATCH_TEST_FLAGS = 983 + 984 + # If *.o files are present, have "coccicheck" depend on them, with 985 + # COMPUTE_HEADER_DEPENDENCIES this will speed up the common-case of 986 + # only needing to re-generate coccicheck results for the users of a 987 + # given API if it's changed, and not all files in the project. If 988 + # COMPUTE_HEADER_DEPENDENCIES=no this will be unset too. 989 + SPATCH_USE_O_DEPENDENCIES = YesPlease 990 + 991 + # Set SPATCH_CONCAT_COCCI to concatenate the contrib/cocci/*.cocci 992 + # files into a single contrib/cocci/ALL.cocci before running 993 + # "coccicheck". 994 + # 995 + # Pros: 996 + # 997 + # - Speeds up a one-shot run of "make coccicheck", as we won't have to 998 + # parse *.[ch] files N times for the N *.cocci rules 999 + # 1000 + # Cons: 1001 + # 1002 + # - Will make incremental development of *.cocci slower, as 1003 + # e.g. changing strbuf.cocci will re-run all *.cocci. 1004 + # 1005 + # - Makes error and performance analysis harder, as rules will be 1006 + # applied from a monolithic ALL.cocci, rather than 1007 + # e.g. strbuf.cocci. To work around this either undefine this, or 1008 + # generate a specific patch, e.g. this will always use strbuf.cocci, 1009 + # not ALL.cocci: 1010 + # 1011 + # make contrib/coccinelle/strbuf.cocci.patch 1012 + SPATCH_CONCAT_COCCI = YesPlease 1013 + 1014 + # Rebuild 'coccicheck' if $(SPATCH), its flags etc. change 1015 + TRACK_SPATCH_DEFINES = 1016 + TRACK_SPATCH_DEFINES += $(SPATCH) 1017 + TRACK_SPATCH_DEFINES += $(SPATCH_INCLUDE_FLAGS) 1018 + TRACK_SPATCH_DEFINES += $(SPATCH_FLAGS) 1019 + TRACK_SPATCH_DEFINES += $(SPATCH_TEST_FLAGS) 1020 + GIT-SPATCH-DEFINES: FORCE 1021 + @FLAGS='$(TRACK_SPATCH_DEFINES)'; \ 1022 + if test x"$$FLAGS" != x"`cat GIT-SPATCH-DEFINES 2>/dev/null`" ; then \ 1023 + echo >&2 " * new spatch flags"; \ 1024 + echo "$$FLAGS" >GIT-SPATCH-DEFINES; \ 1025 + fi 1026 + 1027 + include config.mak.uname 1028 + -include config.mak.autogen 1029 + -include config.mak 1030 + 1031 + ifdef DEVELOPER 1032 + include config.mak.dev 1033 + endif 923 1034 924 1035 GENERATED_H += command-list.h 925 1036 GENERATED_H += config-list.h ··· 1198 1309 LIB_OBJS += usage.o 1199 1310 LIB_OBJS += userdiff.o 1200 1311 LIB_OBJS += utf8.o 1312 + ifndef WITH_RUST 1201 1313 LIB_OBJS += varint.o 1314 + endif 1202 1315 LIB_OBJS += version.o 1203 1316 LIB_OBJS += versioncmp.o 1204 1317 LIB_OBJS += walker.o ··· 1390 1503 1391 1504 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o 1392 1505 1393 - # xdiff and reftable libs may in turn depend on what is in libgit.a 1394 - GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE) 1395 - EXTLIBS = 1396 - 1397 - GIT_USER_AGENT = git/$(GIT_VERSION) 1398 - 1399 - ifeq ($(wildcard sha1collisiondetection/lib/sha1.h),sha1collisiondetection/lib/sha1.h) 1400 - DC_SHA1_SUBMODULE = auto 1401 - endif 1402 - 1403 - # Set CFLAGS, LDFLAGS and other *FLAGS variables. These might be 1404 - # tweaked by config.* below as well as the command-line, both of 1405 - # which'll override these defaults. 1406 - # Older versions of GCC may require adding "-std=gnu99" at the end. 1407 - CFLAGS = -g -O2 -Wall 1408 - LDFLAGS = 1409 - CC_LD_DYNPATH = -Wl,-rpath, 1410 - BASIC_CFLAGS = -I. 1411 - BASIC_LDFLAGS = 1412 - 1413 - # library flags 1414 - ARFLAGS = rcs 1415 - PTHREAD_CFLAGS = 1416 - 1417 - # For the 'sparse' target 1418 - SPARSE_FLAGS ?= -std=gnu99 -D__STDC_NO_VLA__ 1419 - SP_EXTRA_FLAGS = 1420 - 1421 - # For informing GIT-BUILD-OPTIONS of the SANITIZE=leak,address targets 1422 - SANITIZE_LEAK = 1423 - SANITIZE_ADDRESS = 1424 - 1425 - # For the 'coccicheck' target 1426 - SPATCH_INCLUDE_FLAGS = --all-includes 1427 - SPATCH_FLAGS = 1428 - SPATCH_TEST_FLAGS = 1429 - 1430 - # If *.o files are present, have "coccicheck" depend on them, with 1431 - # COMPUTE_HEADER_DEPENDENCIES this will speed up the common-case of 1432 - # only needing to re-generate coccicheck results for the users of a 1433 - # given API if it's changed, and not all files in the project. If 1434 - # COMPUTE_HEADER_DEPENDENCIES=no this will be unset too. 1435 - SPATCH_USE_O_DEPENDENCIES = YesPlease 1436 - 1437 - # Set SPATCH_CONCAT_COCCI to concatenate the contrib/cocci/*.cocci 1438 - # files into a single contrib/cocci/ALL.cocci before running 1439 - # "coccicheck". 1440 - # 1441 - # Pros: 1442 - # 1443 - # - Speeds up a one-shot run of "make coccicheck", as we won't have to 1444 - # parse *.[ch] files N times for the N *.cocci rules 1445 - # 1446 - # Cons: 1447 - # 1448 - # - Will make incremental development of *.cocci slower, as 1449 - # e.g. changing strbuf.cocci will re-run all *.cocci. 1450 - # 1451 - # - Makes error and performance analysis harder, as rules will be 1452 - # applied from a monolithic ALL.cocci, rather than 1453 - # e.g. strbuf.cocci. To work around this either undefine this, or 1454 - # generate a specific patch, e.g. this will always use strbuf.cocci, 1455 - # not ALL.cocci: 1456 - # 1457 - # make contrib/coccinelle/strbuf.cocci.patch 1458 - SPATCH_CONCAT_COCCI = YesPlease 1459 - 1460 - # Rebuild 'coccicheck' if $(SPATCH), its flags etc. change 1461 - TRACK_SPATCH_DEFINES = 1462 - TRACK_SPATCH_DEFINES += $(SPATCH) 1463 - TRACK_SPATCH_DEFINES += $(SPATCH_INCLUDE_FLAGS) 1464 - TRACK_SPATCH_DEFINES += $(SPATCH_FLAGS) 1465 - TRACK_SPATCH_DEFINES += $(SPATCH_TEST_FLAGS) 1466 - GIT-SPATCH-DEFINES: FORCE 1467 - @FLAGS='$(TRACK_SPATCH_DEFINES)'; \ 1468 - if test x"$$FLAGS" != x"`cat GIT-SPATCH-DEFINES 2>/dev/null`" ; then \ 1469 - echo >&2 " * new spatch flags"; \ 1470 - echo "$$FLAGS" >GIT-SPATCH-DEFINES; \ 1471 - fi 1472 - 1473 - include config.mak.uname 1474 - -include config.mak.autogen 1475 - -include config.mak 1476 - 1477 - ifdef DEVELOPER 1478 - include config.mak.dev 1479 - endif 1506 + RUST_SOURCES += src/lib.rs 1507 + RUST_SOURCES += src/varint.rs 1480 1508 1481 1509 GIT-VERSION-FILE: FORCE 1482 1510 @OLD=$$(cat $@ 2>/dev/null || :) && \ ··· 1506 1534 1507 1535 ALL_CFLAGS = $(DEVELOPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_APPEND) 1508 1536 ALL_LDFLAGS = $(LDFLAGS) $(LDFLAGS_APPEND) 1537 + 1538 + ifdef WITH_RUST 1539 + BASIC_CFLAGS += -DWITH_RUST 1540 + GITLIBS += $(RUST_LIB) 1541 + endif 1509 1542 1510 1543 ifdef SANITIZE 1511 1544 SANITIZERS := $(foreach flag,$(subst $(comma),$(space),$(SANITIZE)),$(flag)) ··· 2921 2954 $(LIB_FILE): $(LIB_OBJS) 2922 2955 $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ 2923 2956 2957 + $(RUST_LIB): Cargo.toml $(RUST_SOURCES) 2958 + $(QUIET_CARGO)cargo build $(CARGO_ARGS) 2959 + 2960 + .PHONY: rust 2961 + rust: $(RUST_LIB) 2962 + 2924 2963 $(XDIFF_LIB): $(XDIFF_OBJS) 2925 2964 $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ 2926 2965 ··· 3771 3810 $(RM) $(FUZZ_PROGRAMS) 3772 3811 $(RM) $(SP_OBJ) 3773 3812 $(RM) $(HCC) 3813 + $(RM) -r Cargo.lock target/ 3774 3814 $(RM) version-def.h 3775 3815 $(RM) -r $(dep_dirs) $(compdb_dir) compile_commands.json 3776 3816 $(RM) $(test_bindir_programs)
+6 -2
ci/install-dependencies.sh
··· 30 30 bash cvs gnupg perl-cgi perl-dbd-sqlite perl-io-tty >/dev/null 31 31 ;; 32 32 fedora-*|almalinux-*) 33 + case "$jobname" in 34 + *-meson) 35 + MESON_DEPS="meson ninja";; 36 + esac 33 37 dnf -yq update >/dev/null && 34 - dnf -yq install shadow-utils sudo make gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null 38 + dnf -yq install shadow-utils sudo make pkg-config gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel $MESON_DEPS cargo >/dev/null 35 39 ;; 36 40 ubuntu-*|i386/ubuntu-*|debian-*) 37 41 # Required so that apt doesn't wait for user input on certain packages. ··· 58 62 make libssl-dev libcurl4-openssl-dev libexpat-dev wget sudo default-jre \ 59 63 tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \ 60 64 libemail-valid-perl libio-pty-perl libio-socket-ssl-perl libnet-smtp-ssl-perl libdbd-sqlite3-perl libcgi-pm-perl \ 61 - libsecret-1-dev libpcre2-dev meson ninja-build pkg-config \ 65 + libsecret-1-dev libpcre2-dev meson ninja-build pkg-config cargo \ 62 66 ${CC_PACKAGE:-${CC:-gcc}} $PYTHON_PACKAGE 63 67 64 68 case "$distro" in
+10 -21
ci/run-build-and-tests.sh
··· 5 5 6 6 . ${0%/*}/lib.sh 7 7 8 - run_tests=t 9 - 10 8 case "$jobname" in 11 - linux-breaking-changes) 9 + fedora-breaking-changes-musl|linux-breaking-changes) 12 10 export WITH_BREAKING_CHANGES=YesPlease 11 + export WITH_RUST=YesPlease 12 + MESONFLAGS="$MESONFLAGS -Dbreaking_changes=true" 13 + MESONFLAGS="$MESONFLAGS -Drust=enabled" 13 14 ;; 14 15 linux-TEST-vars) 15 16 export OPENSSL_SHA1_UNSAFE=YesPlease ··· 35 36 linux-reftable|linux-reftable-leaks|osx-reftable) 36 37 export GIT_TEST_DEFAULT_REF_FORMAT=reftable 37 38 ;; 38 - pedantic) 39 - # Don't run the tests; we only care about whether Git can be 40 - # built. 41 - export DEVOPTS=pedantic 42 - run_tests= 43 - ;; 44 39 esac 45 40 46 41 case "$jobname" in ··· 53 48 -Dtest_output_directory="${TEST_OUTPUT_DIRECTORY:-$(pwd)/t}" \ 54 49 $MESONFLAGS 55 50 group "Build" meson compile -C build -- 56 - if test -n "$run_tests" 57 - then 58 - group "Run tests" meson test -C build --print-errorlogs --test-args="$GIT_TEST_OPTS" || ( 59 - ./t/aggregate-results.sh "${TEST_OUTPUT_DIRECTORY:-t}/test-results" 60 - handle_failed_tests 61 - ) 62 - fi 51 + group "Run tests" meson test -C build --print-errorlogs --test-args="$GIT_TEST_OPTS" || ( 52 + ./t/aggregate-results.sh "${TEST_OUTPUT_DIRECTORY:-t}/test-results" 53 + handle_failed_tests 54 + ) 63 55 ;; 64 56 *) 65 57 group Build make 66 - if test -n "$run_tests" 67 - then 68 - group "Run tests" make test || 69 - handle_failed_tests 70 - fi 58 + group "Run tests" make test || 59 + handle_failed_tests 71 60 ;; 72 61 esac 73 62
+10 -8
dir.c
··· 3579 3579 struct stat_data stat_data; 3580 3580 struct strbuf *out = &wd->out; 3581 3581 unsigned char intbuf[16]; 3582 - unsigned int intlen, value; 3582 + unsigned int value; 3583 + uint8_t intlen; 3583 3584 int i = wd->index++; 3584 3585 3585 3586 /* ··· 3632 3633 struct ondisk_untracked_cache *ouc; 3633 3634 struct write_data wd; 3634 3635 unsigned char varbuf[16]; 3635 - int varint_len; 3636 + uint8_t varint_len; 3636 3637 const unsigned hashsz = the_hash_algo->rawsz; 3637 3638 3638 3639 CALLOC_ARRAY(ouc, 1); ··· 3738 3739 struct untracked_cache_dir ud, *untracked; 3739 3740 const unsigned char *data = rd->data, *end = rd->end; 3740 3741 const unsigned char *eos; 3741 - unsigned int value; 3742 + uint64_t value; 3742 3743 int i; 3743 3744 3744 3745 memset(&ud, 0, sizeof(ud)); ··· 3830 3831 struct read_data rd; 3831 3832 const unsigned char *next = data, *end = (const unsigned char *)data + sz; 3832 3833 const char *ident; 3833 - int ident_len; 3834 + uint64_t ident_len; 3835 + uint64_t varint_len; 3834 3836 ssize_t len; 3835 3837 const char *exclude_per_dir; 3836 3838 const unsigned hashsz = the_hash_algo->rawsz; ··· 3867 3869 if (next >= end) 3868 3870 goto done2; 3869 3871 3870 - len = decode_varint(&next); 3871 - if (next > end || len == 0) 3872 + varint_len = decode_varint(&next); 3873 + if (next > end || varint_len == 0) 3872 3874 goto done2; 3873 3875 3874 3876 rd.valid = ewah_new(); ··· 3877 3879 rd.data = next; 3878 3880 rd.end = end; 3879 3881 rd.index = 0; 3880 - ALLOC_ARRAY(rd.ucd, len); 3882 + ALLOC_ARRAY(rd.ucd, varint_len); 3881 3883 3882 - if (read_one_dir(&uc->root, &rd) || rd.index != len) 3884 + if (read_one_dir(&uc->root, &rd) || rd.index != varint_len) 3883 3885 goto done; 3884 3886 3885 3887 next = rd.data;
+6
help.c
··· 791 791 strbuf_addf(buf, "shell-path: %s\n", SHELL_PATH); 792 792 /* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */ 793 793 794 + #if defined WITH_RUST 795 + strbuf_addstr(buf, "rust: enabled\n"); 796 + #else 797 + strbuf_addstr(buf, "rust: disabled\n"); 798 + #endif 799 + 794 800 if (fsmonitor_ipc__is_supported()) 795 801 strbuf_addstr(buf, "feature: fsmonitor--daemon\n"); 796 802 #if defined LIBCURL_VERSION
+13 -2
meson.build
··· 220 220 # learned to define __STDC_VERSION__ with C11 and later. We thus require 221 221 # GNU C99 and fall back to C11. Meson only learned to handle the fallback 222 222 # with version 1.3.0, so on older versions we use GNU C99 unconditionally. 223 - default_options: meson.version().version_compare('>=1.3.0') ? ['c_std=gnu99,c11'] : ['c_std=gnu99'], 223 + default_options: meson.version().version_compare('>=1.3.0') ? ['rust_std=2018', 'c_std=gnu99,c11'] : ['rust_std=2018', 'c_std=gnu99'], 224 224 ) 225 225 226 226 fs = import('fs') ··· 522 522 'usage.c', 523 523 'userdiff.c', 524 524 'utf8.c', 525 - 'varint.c', 526 525 'version.c', 527 526 'versioncmp.c', 528 527 'walker.c', ··· 1703 1702 ) 1704 1703 libgit_sources += version_def_h 1705 1704 1705 + cargo = find_program('cargo', dirs: program_path, native: true, required: get_option('rust')) 1706 + rust_option = get_option('rust').disable_auto_if(not cargo.found()) 1707 + if rust_option.allowed() 1708 + subdir('src') 1709 + libgit_c_args += '-DWITH_RUST' 1710 + else 1711 + libgit_sources += [ 1712 + 'varint.c', 1713 + ] 1714 + endif 1715 + 1706 1716 libgit = declare_dependency( 1707 1717 link_with: static_library('git', 1708 1718 sources: libgit_sources, ··· 2249 2259 'pcre2': pcre2, 2250 2260 'perl': perl_features_enabled, 2251 2261 'python': target_python.found(), 2262 + 'rust': rust_option.allowed(), 2252 2263 }, section: 'Auto-detected features', bool_yn: true) 2253 2264 2254 2265 summary({
+2
meson_options.txt
··· 71 71 # Build tweaks. 72 72 option('breaking_changes', type: 'boolean', value: false, 73 73 description: 'Enable upcoming breaking changes.') 74 + option('rust', type: 'feature', value: 'auto', 75 + description: 'Enable building with Rust.') 74 76 option('macos_use_homebrew_gettext', type: 'boolean', value: true, 75 77 description: 'Use gettext from Homebrew instead of the slightly-broken system-provided one.') 76 78
+4 -2
read-cache.c
··· 1806 1806 1807 1807 if (expand_name_field) { 1808 1808 const unsigned char *cp = (const unsigned char *)name; 1809 - size_t strip_len, previous_len; 1809 + uint64_t strip_len, previous_len; 1810 1810 1811 1811 /* If we're at the beginning of a block, ignore the previous name */ 1812 1812 strip_len = decode_varint(&cp); ··· 2654 2654 hashwrite(f, ce->name, len); 2655 2655 hashwrite(f, padding, align_padding_size(size, len)); 2656 2656 } else { 2657 - int common, to_remove, prefix_size; 2657 + int common, to_remove; 2658 + uint8_t prefix_size; 2658 2659 unsigned char to_remove_vi[16]; 2660 + 2659 2661 for (common = 0; 2660 2662 (common < previous_name->len && 2661 2663 ce->name[common] &&
+1
shared.mak
··· 56 56 QUIET_MKDIR_P_PARENT = @echo ' ' MKDIR -p $(@D); 57 57 58 58 ## Used in "Makefile" 59 + QUIET_CARGO = @echo ' ' CARGO $@; 59 60 QUIET_CC = @echo ' ' CC $@; 60 61 QUIET_AR = @echo ' ' AR $@; 61 62 QUIET_LINK = @echo ' ' LINK $@;
+32
src/cargo-meson.sh
··· 1 + #!/bin/sh 2 + 3 + if test "$#" -lt 2 4 + then 5 + exit 1 6 + fi 7 + 8 + SOURCE_DIR="$1" 9 + BUILD_DIR="$2" 10 + BUILD_TYPE=debug 11 + 12 + shift 2 13 + 14 + for arg 15 + do 16 + case "$arg" in 17 + --release) 18 + BUILD_TYPE=release;; 19 + esac 20 + done 21 + 22 + cargo build --lib --quiet --manifest-path="$SOURCE_DIR/Cargo.toml" --target-dir="$BUILD_DIR" "$@" 23 + RET=$? 24 + if test $RET -ne 0 25 + then 26 + exit $RET 27 + fi 28 + 29 + if ! cmp "$BUILD_DIR/$BUILD_TYPE/libgitcore.a" "$BUILD_DIR/libgitcore.a" >/dev/null 2>&1 30 + then 31 + cp "$BUILD_DIR/$BUILD_TYPE/libgitcore.a" "$BUILD_DIR/libgitcore.a" 32 + fi
+1
src/lib.rs
··· 1 + pub mod varint;
+41
src/meson.build
··· 1 + libgit_rs_sources = [ 2 + 'lib.rs', 3 + 'varint.rs', 4 + ] 5 + 6 + # Unfortunately we must use a wrapper command to move the output file into the 7 + # current build directory. This can fixed once `cargo build --artifact-dir` 8 + # stabilizes. See https://github.com/rust-lang/cargo/issues/6790 for that 9 + # effort. 10 + cargo_command = [ 11 + shell, 12 + meson.current_source_dir() / 'cargo-meson.sh', 13 + meson.project_source_root(), 14 + meson.current_build_dir(), 15 + ] 16 + if get_option('buildtype') == 'release' 17 + cargo_command += '--release' 18 + endif 19 + 20 + libgit_rs = custom_target('git_rs', 21 + input: libgit_rs_sources + [ 22 + meson.project_source_root() / 'Cargo.toml', 23 + ], 24 + output: 'libgitcore.a', 25 + command: cargo_command, 26 + ) 27 + libgit_dependencies += declare_dependency(link_with: libgit_rs) 28 + 29 + if get_option('tests') 30 + test('rust', cargo, 31 + args: [ 32 + 'test', 33 + '--manifest-path', 34 + meson.project_source_root() / 'Cargo.toml', 35 + '--target-dir', 36 + meson.current_build_dir() / 'target', 37 + ], 38 + timeout: 0, 39 + protocol: 'rust', 40 + ) 41 + endif
+92
src/varint.rs
··· 1 + #[no_mangle] 2 + pub unsafe extern "C" fn decode_varint(bufp: *mut *const u8) -> u64 { 3 + let mut buf = *bufp; 4 + let mut c = *buf; 5 + let mut val = u64::from(c & 127); 6 + 7 + buf = buf.add(1); 8 + 9 + while (c & 128) != 0 { 10 + val = val.wrapping_add(1); 11 + if val == 0 || val.leading_zeros() < 7 { 12 + return 0; // overflow 13 + } 14 + 15 + c = *buf; 16 + buf = buf.add(1); 17 + 18 + val = (val << 7) + u64::from(c & 127); 19 + } 20 + 21 + *bufp = buf; 22 + val 23 + } 24 + 25 + #[no_mangle] 26 + pub unsafe extern "C" fn encode_varint(value: u64, buf: *mut u8) -> u8 { 27 + let mut varint: [u8; 16] = [0; 16]; 28 + let mut pos = varint.len() - 1; 29 + 30 + varint[pos] = (value & 127) as u8; 31 + 32 + let mut value = value >> 7; 33 + while value != 0 { 34 + pos -= 1; 35 + value -= 1; 36 + varint[pos] = 128 | (value & 127) as u8; 37 + value >>= 7; 38 + } 39 + 40 + if !buf.is_null() { 41 + std::ptr::copy_nonoverlapping(varint.as_ptr().add(pos), buf, varint.len() - pos); 42 + } 43 + 44 + (varint.len() - pos) as u8 45 + } 46 + 47 + #[cfg(test)] 48 + mod tests { 49 + use super::*; 50 + 51 + #[test] 52 + fn test_decode_varint() { 53 + unsafe { 54 + assert_eq!(decode_varint(&mut [0x00].as_slice().as_ptr()), 0); 55 + assert_eq!(decode_varint(&mut [0x01].as_slice().as_ptr()), 1); 56 + assert_eq!(decode_varint(&mut [0x7f].as_slice().as_ptr()), 127); 57 + assert_eq!(decode_varint(&mut [0x80, 0x00].as_slice().as_ptr()), 128); 58 + assert_eq!(decode_varint(&mut [0x80, 0x01].as_slice().as_ptr()), 129); 59 + assert_eq!(decode_varint(&mut [0x80, 0x7f].as_slice().as_ptr()), 255); 60 + 61 + // Overflows are expected to return 0. 62 + assert_eq!(decode_varint(&mut [0x88; 16].as_slice().as_ptr()), 0); 63 + } 64 + } 65 + 66 + #[test] 67 + fn test_encode_varint() { 68 + unsafe { 69 + let mut varint: [u8; 16] = [0; 16]; 70 + 71 + assert_eq!(encode_varint(0, std::ptr::null_mut()), 1); 72 + 73 + assert_eq!(encode_varint(0, varint.as_mut_slice().as_mut_ptr()), 1); 74 + assert_eq!(varint, [0; 16]); 75 + 76 + assert_eq!(encode_varint(10, varint.as_mut_slice().as_mut_ptr()), 1); 77 + assert_eq!(varint, [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); 78 + 79 + assert_eq!(encode_varint(127, varint.as_mut_slice().as_mut_ptr()), 1); 80 + assert_eq!(varint, [127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); 81 + 82 + assert_eq!(encode_varint(128, varint.as_mut_slice().as_mut_ptr()), 2); 83 + assert_eq!(varint, [128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); 84 + 85 + assert_eq!(encode_varint(129, varint.as_mut_slice().as_mut_ptr()), 2); 86 + assert_eq!(varint, [128, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); 87 + 88 + assert_eq!(encode_varint(255, varint.as_mut_slice().as_mut_ptr()), 2); 89 + assert_eq!(varint, [128, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); 90 + } 91 + } 92 + }
+3 -3
varint.c
··· 1 1 #include "git-compat-util.h" 2 2 #include "varint.h" 3 3 4 - uintmax_t decode_varint(const unsigned char **bufp) 4 + uint64_t decode_varint(const unsigned char **bufp) 5 5 { 6 6 const unsigned char *buf = *bufp; 7 7 unsigned char c = *buf++; 8 - uintmax_t val = c & 127; 8 + uint64_t val = c & 127; 9 9 while (c & 128) { 10 10 val += 1; 11 11 if (!val || MSB(val, 7)) ··· 17 17 return val; 18 18 } 19 19 20 - int encode_varint(uintmax_t value, unsigned char *buf) 20 + uint8_t encode_varint(uint64_t value, unsigned char *buf) 21 21 { 22 22 unsigned char varint[16]; 23 23 unsigned pos = sizeof(varint) - 1;
+2 -2
varint.h
··· 1 1 #ifndef VARINT_H 2 2 #define VARINT_H 3 3 4 - int encode_varint(uintmax_t, unsigned char *); 5 - uintmax_t decode_varint(const unsigned char **); 4 + uint8_t encode_varint(uint64_t, unsigned char *); 5 + uint64_t decode_varint(const unsigned char **); 6 6 7 7 #endif /* VARINT_H */