Skip to content

Commit

Permalink
[rust] Conversions from base <-> Rust types.
Browse files Browse the repository at this point in the history
Most early Rust experiments will involve passing unstructured data
into some Rust decoder or parser, then receiving structured data back.
The unstructured data will typically be a base::span (containing
binary data) or a base::StringPiece (containing textual data).
Add conversions from these Chromium base types into the Rust
equivalents, by means of the cxx types exposed that correspond
to the Rust types.

This CL does nothing in the case that Rust is disabled.

If Rust is enabled, this will cause base_unittests to depend on
the Rust standard library, because real Rust types are constructed
by some of these C++ representations.

Bug: 1069271
Change-Id: I1029bd435efd28a2c589b2560ea59357cb1ecd12
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2277259
Commit-Queue: Adrian Taylor <adetaylor@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/main@{#942443}
  • Loading branch information
adetaylor authored and Chromium LUCI CQ committed Nov 17, 2021
1 parent ea18a10 commit bbfcdab
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 0 deletions.
22 changes: 22 additions & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import("//build/config/dcheck_always_on.gni")
import("//build/config/logging.gni")
import("//build/config/nacl/config.gni")
import("//build/config/profiling/profiling.gni")
import("//build/config/rust.gni")
import("//build/config/sysroot.gni")
import("//build/config/ui.gni")
import("//build/nocompile.gni")
Expand Down Expand Up @@ -1456,6 +1457,19 @@ component("base") {
}
}

if (enable_rust && toolchain_has_rust) {
# TODO(adetaylor): we include conversions between base and cxx's Rust types.
# We assume that any //base client who has flipped 'enable_rust' likely wants
# these, but it's conceivable that some clients might want to enable Rust
# without adding these cxx conversions. If so we would want to add an
# additional gn conditional e.g. enable_rust_binding_in_base
sources += [
"containers/span_rust.h",
"strings/string_piece_rust.h",
]
deps += [ "//build/rust:cxx_cppdeps" ]
}

if (use_clang_profiling) {
# Call-sites use this conditional on the CLANG_PROFILING macro, for clarity.
sources += [
Expand Down Expand Up @@ -3738,6 +3752,14 @@ test("base_unittests") {
]
}

if (enable_rust && toolchain_has_rust) {
sources += [
"containers/span_rust_unittest.cc",
"strings/string_piece_rust_unittest.cc",
]
deps += [ "//build/rust:cxx_cppdeps" ]
}

configs += [ ":memory_tagging" ]
}

Expand Down
5 changes: 5 additions & 0 deletions base/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ include_rules = [
"+third_party/modp_b64",
"+third_party/perfetto/include",
"+third_party/perfetto/protos/perfetto",
# Conversions between base and Rust types (e.g. base::span <-> rust::Slice)
# require the cxx.h header from cxx. This is only used if Rust is enabled
# in the gn build; see //base/BUILD.gn's conditional dependency on
# //build/rust:cxx_cppdeps.
"+third_party/rust/cxx",
"+third_party/tcmalloc",
"+third_party/test_fonts",

Expand Down
22 changes: 22 additions & 0 deletions base/containers/span_rust.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_CONTAINERS_SPAN_RUST_H_
#define BASE_CONTAINERS_SPAN_RUST_H_

#include <stdint.h>

#include "base/containers/span.h"
#include "third_party/rust/cxx/v1/crate/include/cxx.h"

namespace base {

// Create a Rust slice from a base::span.
inline rust::Slice<const uint8_t> SpanToRustSlice(span<const uint8_t> span) {
return rust::Slice<const uint8_t>(span.data(), span.size());
}

} // namespace base

#endif // BASE_CONTAINERS_SPAN_RUST_H_
21 changes: 21 additions & 0 deletions base/containers/span_rust_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/containers/span_rust.h"

#include "testing/gtest/include/gtest/gtest.h"

namespace base {
namespace {

TEST(BaseSpanRustTest, SliceConstruct) {
uint8_t data[] = {0, 1, 2, 3, 4};
span<const uint8_t> data_span(data, 2);
rust::Slice<const uint8_t> rust_slice = SpanToRustSlice(data_span);
EXPECT_EQ(2ul, rust_slice.length());
EXPECT_EQ(1, rust_slice[1]);
}

} // namespace
} // namespace base
38 changes: 38 additions & 0 deletions base/strings/string_piece_rust.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_STRINGS_STRING_PIECE_RUST_H_
#define BASE_STRINGS_STRING_PIECE_RUST_H_

#include <stdint.h>

#include "base/strings/string_piece.h"
#include "third_party/rust/cxx/v1/crate/include/cxx.h"

namespace base {

// Create a Rust str from a base::BasigStringPiece. This will call std::abort
// if there is any invalid UTF8. If you're concerned about this, then
// instead use StringPieceToRustSlice and convert the data to a string on
// the Rust side (or pass in a std::string).
inline rust::Str StringPieceToRustStrUTF8(StringPiece string_piece) {
return rust::Str(string_piece.data(), string_piece.size());
}

// Create a Rust slice from a StringPiece. No UTF8 check is performed.
inline rust::Slice<const uint8_t> StringPieceToRustSlice(
StringPiece string_piece) {
return rust::Slice<const uint8_t>(
reinterpret_cast<const uint8_t*>(string_piece.data()),
string_piece.length() * sizeof(StringPiece::value_type));
}

// Create a StringPiece from a Rust str.
inline StringPiece RustStrToStringPiece(rust::Str str) {
return StringPiece(str.data(), str.size());
}

} // namespace base

#endif // BASE_STRINGS_STRING_PIECE_RUST_H_
30 changes: 30 additions & 0 deletions base/strings/string_piece_rust_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/strings/string_piece_rust.h"

#include "testing/gtest/include/gtest/gtest.h"

namespace base {
namespace {

TEST(BaseStringPieceRustTest, StrRoundTrip) {
std::string data = "hello";
StringPiece data_piece(data);
rust::Str rust_str = StringPieceToRustStrUTF8(data_piece);
EXPECT_EQ(5ul, rust_str.length());
StringPiece data_piece2 = RustStrToStringPiece(rust_str);
EXPECT_EQ(data_piece, data_piece2);
}

TEST(BaseStringPieceRustTest, StrToSlice) {
std::string data = "hello";
StringPiece data_piece(data);
rust::Slice<const uint8_t> rust_slice = StringPieceToRustSlice(data_piece);
EXPECT_EQ(5ul, rust_slice.length());
EXPECT_EQ('e', rust_slice[1]);
}

} // namespace
} // namespace base
12 changes: 12 additions & 0 deletions build/rust/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ source_set("cxx_cppdeps") {
"//third_party/rust/cxx/v1/crate/include/cxx.h",
"//third_party/rust/cxx/v1/crate/src/cxx.cc",
]

# Depending on the C++ bindings side of cxx then requires also depending
# on the Rust bindings, since one calls the other. And the Rust bindings
# require the Rust standard library.
# Normally the Rust stdlib is brought in as a dependency by depending
# on any first-party Rust target. But in this case, it's conceivable
# that pure-C++ targets will not depend on any 1p Rust code so we'll add
# the Rust stdlib explicitly.
deps = [
":cxx_rustdeps",
"//build/rust/std",
]
public_configs = [ ":cxx_cppconfig" ]
}

Expand Down

0 comments on commit bbfcdab

Please sign in to comment.