mirror of
https://github.com/ostreedev/ostree.git
synced 2025-01-03 05:18:24 +03:00
oxidation: Add implementation of bupsplit in Rust
This is an initial drop of "oxidation", or adding implementation of components in Rust. The bupsplit code is a good target - no dependencies, just computation. Translation into Rust had a few twists - - The C code relies a lot on overflowing unsigned ints, and also on the C promotion rules for e.g. `uint8_t -> int32_t` - There were some odd loops that I introduced bugs in while translating...in particular, the function always returns `len`, but I mistakenly translated to `len+1`, resulting in an OOB read on the C side, which was hard to debug. On the plus side, an off-by-one array indexing in the Rust code paniced nicely. In practice, we'll need a lot more build infrastructure to make this work, such as using `cargo vendor` when producing build artifacts for example. Also, Cargo is yet another thing we need to cache. Where do we go with this? Well, I think we should merge this, it's not a lot of code. We can just have it be an alternative CI target. Should we do a lot more right now? Probably not immediately, but I find the medium/long term prospects pretty exciting! Closes: #656 Approved by: jlebon
This commit is contained in:
parent
7803fe1d60
commit
d894f609db
@ -46,3 +46,30 @@ env:
|
|||||||
|
|
||||||
tests:
|
tests:
|
||||||
artifacts:
|
artifacts:
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
inherit: true
|
||||||
|
|
||||||
|
context: f25-rust
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- cargo
|
||||||
|
|
||||||
|
build:
|
||||||
|
config-opts: >
|
||||||
|
--prefix=/usr
|
||||||
|
--libdir=/usr/lib64
|
||||||
|
--enable-installed-tests
|
||||||
|
--enable-gtk-doc
|
||||||
|
--enable-rust
|
||||||
|
|
||||||
|
env:
|
||||||
|
CC: 'gcc'
|
||||||
|
|
||||||
|
tests:
|
||||||
|
- make check TESTS=tests/test-rollsum
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- test-suite.log
|
||||||
|
@ -19,7 +19,19 @@
|
|||||||
|
|
||||||
include Makefile-libostree-defines.am
|
include Makefile-libostree-defines.am
|
||||||
|
|
||||||
noinst_LTLIBRARIES += libostree-kernel-args.la libbupsplit.la
|
noinst_LTLIBRARIES += libostree-kernel-args.la
|
||||||
|
|
||||||
|
|
||||||
|
if ENABLE_RUST
|
||||||
|
bupsplitpath = @abs_top_builddir@/target/@RUST_TARGET_SUBDIR@/libbupsplit_rs.a
|
||||||
|
.PHONY: $(bupsplitpath)
|
||||||
|
$(bupsplitpath): Makefile rust/src/bupsplit.rs
|
||||||
|
cd $(top_srcdir)/rust && CARGO_TARGET_DIR=@abs_top_builddir@/target cargo build --verbose $(CARGO_RELEASE_ARGS)
|
||||||
|
else
|
||||||
|
bupsplitpath = libbupsplit.la
|
||||||
|
noinst_LTLIBRARIES += libbupsplit.la
|
||||||
|
libbupsplit_la_SOURCES = src/libostree/bupsplit.h src/libostree/bupsplit.c
|
||||||
|
endif # ENABLE_RUST
|
||||||
|
|
||||||
libostree_kernel_args_la_SOURCES = \
|
libostree_kernel_args_la_SOURCES = \
|
||||||
src/libostree/ostree-kernel-args.h \
|
src/libostree/ostree-kernel-args.h \
|
||||||
@ -56,11 +68,6 @@ BUILT_SOURCES += $(nodist_libostree_1_la_SOURCES)
|
|||||||
|
|
||||||
CLEANFILES += $(BUILT_SOURCES)
|
CLEANFILES += $(BUILT_SOURCES)
|
||||||
|
|
||||||
libbupsplit_la_SOURCES = \
|
|
||||||
src/libostree/bupsplit.h \
|
|
||||||
src/libostree/bupsplit.c \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
libostree_1_la_SOURCES = \
|
libostree_1_la_SOURCES = \
|
||||||
src/libostree/ostree-async-progress.c \
|
src/libostree/ostree-async-progress.c \
|
||||||
src/libostree/ostree-cmdprivate.h \
|
src/libostree/ostree-cmdprivate.h \
|
||||||
@ -142,7 +149,8 @@ libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/bsdiff -I$(srcdir)/libglnx -I$(
|
|||||||
$(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_LZMA_CFLAGS) $(OT_DEP_ZLIB_CFLAGS) \
|
$(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_LZMA_CFLAGS) $(OT_DEP_ZLIB_CFLAGS) \
|
||||||
-fvisibility=hidden '-D_OSTREE_PUBLIC=__attribute__((visibility("default"))) extern'
|
-fvisibility=hidden '-D_OSTREE_PUBLIC=__attribute__((visibility("default"))) extern'
|
||||||
libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions -Wl,--version-script=$(top_srcdir)/src/libostree/libostree.sym
|
libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions -Wl,--version-script=$(top_srcdir)/src/libostree/libostree.sym
|
||||||
libostree_1_la_LIBADD = libotutil.la libbupsplit.la libglnx.la libbsdiff.la libostree-kernel-args.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) $(OT_DEP_LZMA_LIBS) $(OT_DEP_ZLIB_LIBS)
|
libostree_1_la_LIBADD = libotutil.la libglnx.la libbsdiff.la libostree-kernel-args.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) $(OT_DEP_LZMA_LIBS) $(OT_DEP_ZLIB_LIBS)
|
||||||
|
libostree_1_la_LIBADD += $(bupsplitpath)
|
||||||
EXTRA_libostree_1_la_DEPENDENCIES = $(top_srcdir)/src/libostree/libostree.sym
|
EXTRA_libostree_1_la_DEPENDENCIES = $(top_srcdir)/src/libostree/libostree.sym
|
||||||
|
|
||||||
EXTRA_DIST += src/libostree/libostree.sym
|
EXTRA_DIST += src/libostree/libostree.sym
|
||||||
|
@ -200,11 +200,11 @@ TESTS_LDADD = $(common_tests_ldadd) libostreetest.la
|
|||||||
|
|
||||||
tests_test_rollsum_cli_SOURCES = src/libostree/ostree-rollsum.c tests/test-rollsum-cli.c
|
tests_test_rollsum_cli_SOURCES = src/libostree/ostree-rollsum.c tests/test-rollsum-cli.c
|
||||||
tests_test_rollsum_cli_CFLAGS = $(TESTS_CFLAGS) $(OT_DEP_ZLIB_CFLAGS)
|
tests_test_rollsum_cli_CFLAGS = $(TESTS_CFLAGS) $(OT_DEP_ZLIB_CFLAGS)
|
||||||
tests_test_rollsum_cli_LDADD = libbupsplit.la $(TESTS_LDADD) $(OT_DEP_ZLIB_LIBS)
|
tests_test_rollsum_cli_LDADD = $(bupsplitpath) $(TESTS_LDADD) $(OT_DEP_ZLIB_LIBS)
|
||||||
|
|
||||||
tests_test_rollsum_SOURCES = src/libostree/ostree-rollsum.c tests/test-rollsum.c
|
tests_test_rollsum_SOURCES = src/libostree/ostree-rollsum.c tests/test-rollsum.c
|
||||||
tests_test_rollsum_CFLAGS = $(TESTS_CFLAGS) $(OT_DEP_ZLIB_CFLAGS)
|
tests_test_rollsum_CFLAGS = $(TESTS_CFLAGS) $(OT_DEP_ZLIB_CFLAGS)
|
||||||
tests_test_rollsum_LDADD = libbupsplit.la $(TESTS_LDADD) $(OT_DEP_ZLIB_LIBS)
|
tests_test_rollsum_LDADD = $(bupsplitpath) $(TESTS_LDADD) $(OT_DEP_ZLIB_LIBS)
|
||||||
|
|
||||||
tests_test_mutable_tree_CFLAGS = $(TESTS_CFLAGS)
|
tests_test_mutable_tree_CFLAGS = $(TESTS_CFLAGS)
|
||||||
tests_test_mutable_tree_LDADD = $(TESTS_LDADD)
|
tests_test_mutable_tree_LDADD = $(TESTS_LDADD)
|
||||||
|
15
Makefile.am
15
Makefile.am
@ -60,6 +60,21 @@ GIRS =
|
|||||||
TYPELIBS = $(GIRS:.gir=.typelib)
|
TYPELIBS = $(GIRS:.gir=.typelib)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# These bits based on gnome:librsvg/Makefile.am
|
||||||
|
if ENABLE_RUST
|
||||||
|
if RUST_DEBUG
|
||||||
|
CARGO_RELEASE_ARGS=
|
||||||
|
else
|
||||||
|
CARGO_RELEASE_ARGS=--release
|
||||||
|
endif
|
||||||
|
|
||||||
|
check-local:
|
||||||
|
cd $(srcdir)/rust && CARGO_TARGET_DIR=$(abs_top_builddir)/target cargo test
|
||||||
|
|
||||||
|
clean-local:
|
||||||
|
cd $(srcdir)/rust && CARGO_TARGET_DIR=$(abs_top_builddir)/target cargo clean
|
||||||
|
endif # end ENABLE_RUST
|
||||||
|
|
||||||
libglnx_srcpath := $(srcdir)/libglnx
|
libglnx_srcpath := $(srcdir)/libglnx
|
||||||
libglnx_cflags := $(OT_DEP_GIO_UNIX_CFLAGS) "-I$(libglnx_srcpath)"
|
libglnx_cflags := $(OT_DEP_GIO_UNIX_CFLAGS) "-I$(libglnx_srcpath)"
|
||||||
libglnx_libs := $(OT_DEP_GIO_UNIX_LIBS)
|
libglnx_libs := $(OT_DEP_GIO_UNIX_LIBS)
|
||||||
|
34
configure.ac
34
configure.ac
@ -166,6 +166,39 @@ AS_IF([test "$enable_man" != no], [
|
|||||||
])
|
])
|
||||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(rust,
|
||||||
|
[AS_HELP_STRING([--enable-rust],
|
||||||
|
[Compile Rust code instead of C [default=no]])],,
|
||||||
|
[enable_rust=no; rust_debug_release=no])
|
||||||
|
|
||||||
|
AS_IF([test "$enable_rust" = yes], [
|
||||||
|
AC_PATH_PROG([cargo], [cargo])
|
||||||
|
AS_IF([test -z "$cargo"], [AC_MSG_ERROR([cargo is required for --enable-rust])])
|
||||||
|
AC_PATH_PROG([rustc], [rustc])
|
||||||
|
AS_IF([test -z "$rustc"], [AC_MSG_ERROR([rustc is required for --enable-rust])])
|
||||||
|
|
||||||
|
dnl These bits based on gnome:librsvg/configure.ac
|
||||||
|
|
||||||
|
dnl By default, we build in public release mode.
|
||||||
|
AC_ARG_ENABLE(rust-debug,
|
||||||
|
AC_HELP_STRING([--enable-rust-debug],
|
||||||
|
[Build Rust code with debugging information [default=no]]),
|
||||||
|
[rust_debug_release=$enableval],
|
||||||
|
[rust_debug_release=release])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING(whether to build Rust code with debugging information)
|
||||||
|
if test "x$rust_debug_release" = "xyes" ; then
|
||||||
|
rust_debug_release=debug
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
RUST_TARGET_SUBDIR=${rust_debug_release}
|
||||||
|
AC_SUBST([RUST_TARGET_SUBDIR])
|
||||||
|
])
|
||||||
|
AM_CONDITIONAL(RUST_DEBUG, [test "x$rust_debug_release" = "xdebug"])
|
||||||
|
AM_CONDITIONAL(ENABLE_RUST, [test "$enable_rust" != no])
|
||||||
|
|
||||||
AC_ARG_WITH(libarchive,
|
AC_ARG_WITH(libarchive,
|
||||||
AS_HELP_STRING([--without-libarchive], [Do not use libarchive]),
|
AS_HELP_STRING([--without-libarchive], [Do not use libarchive]),
|
||||||
:, with_libarchive=maybe)
|
:, with_libarchive=maybe)
|
||||||
@ -339,6 +372,7 @@ echo "
|
|||||||
|
|
||||||
|
|
||||||
introspection: $found_introspection
|
introspection: $found_introspection
|
||||||
|
Rust (internal oxidation): $rust_debug_release
|
||||||
rofiles-fuse: $enable_rofiles_fuse
|
rofiles-fuse: $enable_rofiles_fuse
|
||||||
libsoup (retrieve remote HTTP repositories): $with_soup
|
libsoup (retrieve remote HTTP repositories): $with_soup
|
||||||
libsoup TLS client certs: $have_libsoup_client_certs
|
libsoup TLS client certs: $have_libsoup_client_certs
|
||||||
|
16
rust/Cargo.toml
Normal file
16
rust/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "bupsplit"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Colin Walters <walters@verbum.org>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "bupsplit_rs"
|
||||||
|
path = "src/bupsplit.rs"
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
lto = true
|
129
rust/src/bupsplit.rs
Normal file
129
rust/src/bupsplit.rs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2017 Colin Walters <walters@verbum.org>
|
||||||
|
* Based on original bupsplit.c:
|
||||||
|
* Copyright 2011 Avery Pennarun. All rights reserved.
|
||||||
|
*
|
||||||
|
* (This license applies to bupsplit.c and bupsplit.h only.)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY AVERY PENNARUN ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
// According to librsync/rollsum.h:
|
||||||
|
// "We should make this something other than zero to improve the
|
||||||
|
// checksum algorithm: tridge suggests a prime number."
|
||||||
|
// apenwarr: I unscientifically tried 0 and 7919, and they both ended up
|
||||||
|
// slightly worse than the librsync value of 31 for my arbitrary test data.
|
||||||
|
const ROLLSUM_CHAR_OFFSET: u32 = 31;
|
||||||
|
|
||||||
|
// Previously in the header file
|
||||||
|
const BUP_BLOBBITS: u32= 13;
|
||||||
|
const BUP_BLOBSIZE: u32 = (1<<BUP_BLOBBITS);
|
||||||
|
const BUP_WINDOWBITS: u32 = 7;
|
||||||
|
const BUP_WINDOWSIZE: u32 = (1<<(BUP_WINDOWBITS-1));
|
||||||
|
|
||||||
|
struct Rollsum {
|
||||||
|
s1: u32,
|
||||||
|
s2: u32,
|
||||||
|
window: [u8; BUP_WINDOWSIZE as usize],
|
||||||
|
wofs: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rollsum {
|
||||||
|
pub fn new() -> Rollsum {
|
||||||
|
Rollsum { s1: BUP_WINDOWSIZE * ROLLSUM_CHAR_OFFSET,
|
||||||
|
s2: BUP_WINDOWSIZE * (BUP_WINDOWSIZE-1) * ROLLSUM_CHAR_OFFSET,
|
||||||
|
window: [0; 64],
|
||||||
|
wofs: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These formulas are based on rollsum.h in the librsync project.
|
||||||
|
pub fn add(&mut self, drop: u8, add: u8) -> () {
|
||||||
|
let drop_expanded = drop as u32;
|
||||||
|
let add_expanded = add as u32;
|
||||||
|
self.s1 = self.s1.wrapping_add(add_expanded.wrapping_sub(drop_expanded));
|
||||||
|
self.s2 = self.s2.wrapping_add(self.s1.wrapping_sub(BUP_WINDOWSIZE * (drop_expanded + ROLLSUM_CHAR_OFFSET)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn roll(&mut self, ch: u8) -> () {
|
||||||
|
let wofs = self.wofs as usize;
|
||||||
|
let dval = self.window[wofs];
|
||||||
|
self.add(dval, ch);
|
||||||
|
self.window[wofs] = ch;
|
||||||
|
self.wofs = (self.wofs + 1) % (BUP_WINDOWSIZE as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn digest(&self) -> u32 {
|
||||||
|
(self.s1 << 16) | (self.s2 & 0xFFFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bupsplit_sum(buf: *const u8, ofs: libc::size_t, len: libc::size_t) -> u32 {
|
||||||
|
let sbuf = unsafe {
|
||||||
|
assert!(!buf.is_null());
|
||||||
|
slice::from_raw_parts(buf.offset(ofs as isize), (len - ofs) as usize)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut r = Rollsum::new();
|
||||||
|
for x in sbuf {
|
||||||
|
r.roll(*x);
|
||||||
|
}
|
||||||
|
r.digest()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bupsplit_find_ofs(buf: *const u8, len: libc::size_t,
|
||||||
|
bits: *mut libc::c_int) -> libc::c_int
|
||||||
|
{
|
||||||
|
let sbuf = unsafe {
|
||||||
|
assert!(!buf.is_null());
|
||||||
|
slice::from_raw_parts(buf, len as usize)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut r = Rollsum::new();
|
||||||
|
for x in sbuf {
|
||||||
|
r.roll(*x);
|
||||||
|
if (r.s2 & (BUP_BLOBSIZE-1)) == ((u32::max_value()) & (BUP_BLOBSIZE-1)) {
|
||||||
|
if !bits.is_null() {
|
||||||
|
let mut sum = r.digest() >> BUP_BLOBBITS;
|
||||||
|
let mut rbits : libc::c_int = BUP_BLOBBITS as i32;
|
||||||
|
while sum & 1 != 0 {
|
||||||
|
sum = sum >> 1;
|
||||||
|
rbits = rbits + 1;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
*bits = rbits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user