diff --git a/BUILD b/BUILD
index 072de8454b1..023b1cede4b 100644
--- a/BUILD
+++ b/BUILD
@@ -1573,6 +1573,7 @@ grpc_cc_library(
hdrs = [
"src/core/lib/slice/slice_refcount.h",
"src/core/lib/slice/slice_refcount_base.h",
+ "src/core/lib/slice/slice_utils.h",
],
public_hdrs = [
"include/grpc/slice.h",
@@ -1886,6 +1887,7 @@ grpc_cc_library(
"src/core/lib/slice/percent_encoding.cc",
"src/core/lib/slice/slice_api.cc",
"src/core/lib/slice/slice_buffer.cc",
+ "src/core/lib/slice/slice_intern.cc",
"src/core/lib/slice/slice_split.cc",
"src/core/lib/surface/api_trace.cc",
"src/core/lib/surface/builtins.cc",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 693e06ba9a6..d5f00c92555 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -735,6 +735,7 @@ if(gRPC_BUILD_TESTS)
endif()
add_dependencies(buildtests_c server_test)
add_dependencies(buildtests_c slice_buffer_test)
+ add_dependencies(buildtests_c slice_intern_test)
add_dependencies(buildtests_c slice_split_test)
add_dependencies(buildtests_c slice_string_helpers_test)
add_dependencies(buildtests_c sockaddr_resolver_test)
@@ -2081,6 +2082,7 @@ add_library(grpc
src/core/lib/slice/slice.cc
src/core/lib/slice/slice_api.cc
src/core/lib/slice/slice_buffer.cc
+ src/core/lib/slice/slice_intern.cc
src/core/lib/slice/slice_refcount.cc
src/core/lib/slice/slice_split.cc
src/core/lib/slice/slice_string_helpers.cc
@@ -2665,6 +2667,7 @@ add_library(grpc_unsecure
src/core/lib/slice/slice.cc
src/core/lib/slice/slice_api.cc
src/core/lib/slice/slice_buffer.cc
+ src/core/lib/slice/slice_intern.cc
src/core/lib/slice/slice_refcount.cc
src/core/lib/slice/slice_split.cc
src/core/lib/slice/slice_string_helpers.cc
@@ -6703,6 +6706,33 @@ target_link_libraries(slice_buffer_test
)
+endif()
+if(gRPC_BUILD_TESTS)
+
+add_executable(slice_intern_test
+ test/core/slice/slice_intern_test.cc
+)
+
+target_include_directories(slice_intern_test
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+ ${_gRPC_RE2_INCLUDE_DIR}
+ ${_gRPC_SSL_INCLUDE_DIR}
+ ${_gRPC_UPB_GENERATED_DIR}
+ ${_gRPC_UPB_GRPC_GENERATED_DIR}
+ ${_gRPC_UPB_INCLUDE_DIR}
+ ${_gRPC_XXHASH_INCLUDE_DIR}
+ ${_gRPC_ZLIB_INCLUDE_DIR}
+)
+
+target_link_libraries(slice_intern_test
+ ${_gRPC_ALLTARGETS_LIBRARIES}
+ grpc_test_util
+)
+
+
endif()
if(gRPC_BUILD_TESTS)
diff --git a/Makefile b/Makefile
index 46b9da7cf0d..52e7383c83d 100644
--- a/Makefile
+++ b/Makefile
@@ -1566,6 +1566,7 @@ LIBGRPC_SRC = \
src/core/lib/slice/slice.cc \
src/core/lib/slice/slice_api.cc \
src/core/lib/slice/slice_buffer.cc \
+ src/core/lib/slice/slice_intern.cc \
src/core/lib/slice/slice_refcount.cc \
src/core/lib/slice/slice_split.cc \
src/core/lib/slice/slice_string_helpers.cc \
@@ -1997,6 +1998,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/slice/slice.cc \
src/core/lib/slice/slice_api.cc \
src/core/lib/slice/slice_buffer.cc \
+ src/core/lib/slice/slice_intern.cc \
src/core/lib/slice/slice_refcount.cc \
src/core/lib/slice/slice_split.cc \
src/core/lib/slice/slice_string_helpers.cc \
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 079e2e84515..70f07694113 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -932,6 +932,7 @@ libs:
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_split.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
- src/core/lib/surface/api_trace.h
- src/core/lib/surface/builtins.h
- src/core/lib/surface/call.h
@@ -1532,6 +1533,7 @@ libs:
- src/core/lib/slice/slice.cc
- src/core/lib/slice/slice_api.cc
- src/core/lib/slice/slice_buffer.cc
+ - src/core/lib/slice/slice_intern.cc
- src/core/lib/slice/slice_refcount.cc
- src/core/lib/slice/slice_split.cc
- src/core/lib/slice/slice_string_helpers.cc
@@ -1987,6 +1989,7 @@ libs:
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_split.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
- src/core/lib/surface/api_trace.h
- src/core/lib/surface/builtins.h
- src/core/lib/surface/call.h
@@ -2263,6 +2266,7 @@ libs:
- src/core/lib/slice/slice.cc
- src/core/lib/slice/slice_api.cc
- src/core/lib/slice/slice_buffer.cc
+ - src/core/lib/slice/slice_intern.cc
- src/core/lib/slice/slice_refcount.cc
- src/core/lib/slice/slice_split.cc
- src/core/lib/slice/slice_string_helpers.cc
@@ -3764,6 +3768,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
src:
- src/core/lib/debug/trace.cc
- src/core/lib/event_engine/memory_allocator.cc
@@ -4014,6 +4019,15 @@ targets:
deps:
- grpc_test_util
uses_polling: false
+- name: slice_intern_test
+ build: test
+ language: c
+ headers: []
+ src:
+ - test/core/slice/slice_intern_test.cc
+ deps:
+ - grpc_test_util
+ uses_polling: false
- name: slice_split_test
build: test
language: c
@@ -4036,6 +4050,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
src:
- src/core/lib/debug/trace.cc
- src/core/lib/slice/slice.cc
@@ -4513,6 +4528,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
src:
- src/core/lib/debug/trace.cc
- src/core/lib/event_engine/memory_allocator.cc
@@ -5005,6 +5021,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
src:
- src/core/lib/debug/trace.cc
- src/core/lib/event_engine/memory_allocator.cc
@@ -5637,6 +5654,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
src:
- src/core/lib/debug/trace.cc
- src/core/lib/iomgr/combiner.cc
@@ -5846,6 +5864,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
- test/core/promise/test_wakeup_schedulers.h
src:
- src/core/lib/debug/trace.cc
@@ -6610,6 +6629,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
- test/core/resource_quota/call_checker.h
src:
- src/core/lib/debug/trace.cc
@@ -6915,6 +6935,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
- test/core/promise/test_wakeup_schedulers.h
src:
- src/core/lib/debug/trace.cc
@@ -7267,6 +7288,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
src:
- src/core/lib/debug/trace.cc
- src/core/lib/event_engine/memory_allocator.cc
@@ -7703,6 +7725,7 @@ targets:
- src/core/lib/slice/slice_refcount.h
- src/core/lib/slice/slice_refcount_base.h
- src/core/lib/slice/slice_string_helpers.h
+ - src/core/lib/slice/slice_utils.h
- test/core/util/build.h
src:
- src/core/lib/debug/trace.cc
diff --git a/config.m4 b/config.m4
index fde499331f5..e15fa572fe1 100644
--- a/config.m4
+++ b/config.m4
@@ -628,6 +628,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/slice/slice.cc \
src/core/lib/slice/slice_api.cc \
src/core/lib/slice/slice_buffer.cc \
+ src/core/lib/slice/slice_intern.cc \
src/core/lib/slice/slice_refcount.cc \
src/core/lib/slice/slice_split.cc \
src/core/lib/slice/slice_string_helpers.cc \
diff --git a/config.w32 b/config.w32
index 7a7b2bae78e..0c88a5e6fe8 100644
--- a/config.w32
+++ b/config.w32
@@ -594,6 +594,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\slice\\slice.cc " +
"src\\core\\lib\\slice\\slice_api.cc " +
"src\\core\\lib\\slice\\slice_buffer.cc " +
+ "src\\core\\lib\\slice\\slice_intern.cc " +
"src\\core\\lib\\slice\\slice_refcount.cc " +
"src\\core\\lib\\slice\\slice_split.cc " +
"src\\core\\lib\\slice\\slice_string_helpers.cc " +
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index 2c5a88da891..91a954408b3 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -805,6 +805,7 @@ Pod::Spec.new do |s|
'src/core/lib/slice/slice_refcount_base.h',
'src/core/lib/slice/slice_split.h',
'src/core/lib/slice/slice_string_helpers.h',
+ 'src/core/lib/slice/slice_utils.h',
'src/core/lib/surface/api_trace.h',
'src/core/lib/surface/builtins.h',
'src/core/lib/surface/call.h',
@@ -1537,6 +1538,7 @@ Pod::Spec.new do |s|
'src/core/lib/slice/slice_refcount_base.h',
'src/core/lib/slice/slice_split.h',
'src/core/lib/slice/slice_string_helpers.h',
+ 'src/core/lib/slice/slice_utils.h',
'src/core/lib/surface/api_trace.h',
'src/core/lib/surface/builtins.h',
'src/core/lib/surface/call.h',
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index cd7e15ff575..4c2980404a2 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -1327,6 +1327,7 @@ Pod::Spec.new do |s|
'src/core/lib/slice/slice.h',
'src/core/lib/slice/slice_api.cc',
'src/core/lib/slice/slice_buffer.cc',
+ 'src/core/lib/slice/slice_intern.cc',
'src/core/lib/slice/slice_internal.h',
'src/core/lib/slice/slice_refcount.cc',
'src/core/lib/slice/slice_refcount.h',
@@ -1335,6 +1336,7 @@ Pod::Spec.new do |s|
'src/core/lib/slice/slice_split.h',
'src/core/lib/slice/slice_string_helpers.cc',
'src/core/lib/slice/slice_string_helpers.h',
+ 'src/core/lib/slice/slice_utils.h',
'src/core/lib/surface/api_trace.cc',
'src/core/lib/surface/api_trace.h',
'src/core/lib/surface/builtins.cc',
@@ -2078,6 +2080,7 @@ Pod::Spec.new do |s|
'src/core/lib/slice/slice_refcount_base.h',
'src/core/lib/slice/slice_split.h',
'src/core/lib/slice/slice_string_helpers.h',
+ 'src/core/lib/slice/slice_utils.h',
'src/core/lib/surface/api_trace.h',
'src/core/lib/surface/builtins.h',
'src/core/lib/surface/call.h',
diff --git a/grpc.def b/grpc.def
index a6959b94fb3..58979d814b9 100644
--- a/grpc.def
+++ b/grpc.def
@@ -189,6 +189,7 @@ EXPORTS
grpc_slice_new_with_len
grpc_slice_malloc
grpc_slice_malloc_large
+ grpc_slice_intern
grpc_slice_from_copied_string
grpc_slice_from_copied_buffer
grpc_slice_from_static_string
@@ -199,6 +200,8 @@ EXPORTS
grpc_slice_split_tail_maybe_ref
grpc_slice_split_head
grpc_empty_slice
+ grpc_slice_default_hash_impl
+ grpc_slice_default_eq_impl
grpc_slice_eq
grpc_slice_cmp
grpc_slice_str_cmp
@@ -206,6 +209,7 @@ EXPORTS
grpc_slice_rchr
grpc_slice_chr
grpc_slice_slice
+ grpc_slice_hash
grpc_slice_is_equivalent
grpc_slice_dup
grpc_slice_to_c_string
diff --git a/grpc.gemspec b/grpc.gemspec
index ec09d055143..154971f7d8e 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -1246,6 +1246,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/slice/slice.h )
s.files += %w( src/core/lib/slice/slice_api.cc )
s.files += %w( src/core/lib/slice/slice_buffer.cc )
+ s.files += %w( src/core/lib/slice/slice_intern.cc )
s.files += %w( src/core/lib/slice/slice_internal.h )
s.files += %w( src/core/lib/slice/slice_refcount.cc )
s.files += %w( src/core/lib/slice/slice_refcount.h )
@@ -1254,6 +1255,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/slice/slice_split.h )
s.files += %w( src/core/lib/slice/slice_string_helpers.cc )
s.files += %w( src/core/lib/slice/slice_string_helpers.h )
+ s.files += %w( src/core/lib/slice/slice_utils.h )
s.files += %w( src/core/lib/surface/api_trace.cc )
s.files += %w( src/core/lib/surface/api_trace.h )
s.files += %w( src/core/lib/surface/builtins.cc )
diff --git a/grpc.gyp b/grpc.gyp
index bd9440dc29d..abd8ed7d8e2 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -1014,6 +1014,7 @@
'src/core/lib/slice/slice.cc',
'src/core/lib/slice/slice_api.cc',
'src/core/lib/slice/slice_buffer.cc',
+ 'src/core/lib/slice/slice_intern.cc',
'src/core/lib/slice/slice_refcount.cc',
'src/core/lib/slice/slice_split.cc',
'src/core/lib/slice/slice_string_helpers.cc',
@@ -1417,6 +1418,7 @@
'src/core/lib/slice/slice.cc',
'src/core/lib/slice/slice_api.cc',
'src/core/lib/slice/slice_buffer.cc',
+ 'src/core/lib/slice/slice_intern.cc',
'src/core/lib/slice/slice_refcount.cc',
'src/core/lib/slice/slice_split.cc',
'src/core/lib/slice/slice_string_helpers.cc',
diff --git a/include/grpc/impl/codegen/slice.h b/include/grpc/impl/codegen/slice.h
index 130e5efffd2..4412058104e 100644
--- a/include/grpc/impl/codegen/slice.h
+++ b/include/grpc/impl/codegen/slice.h
@@ -58,10 +58,7 @@ struct grpc_slice_refcount;
Multiple grpc_slice values may share a ref count.
If the slice does not have a refcount, it represents an inlined small piece
- of data that is copied by value.
-
- As a special case, a slice can be given refcount == uintptr_t(1), meaning
- that the slice represents external data that is not refcounted. */
+ of data that is copied by value. */
struct grpc_slice {
struct grpc_slice_refcount* refcount;
union grpc_slice_data {
diff --git a/include/grpc/slice.h b/include/grpc/slice.h
index 15978fe54c5..65d20878831 100644
--- a/include/grpc/slice.h
+++ b/include/grpc/slice.h
@@ -69,6 +69,12 @@ GPRAPI grpc_slice grpc_slice_malloc_large(size_t length);
#define GRPC_SLICE_MALLOC(len) grpc_slice_malloc(len)
+/** Intern a slice:
+
+ The return value for two invocations of this function with the same sequence
+ of bytes is a slice which points to the same memory. */
+GPRAPI grpc_slice grpc_slice_intern(grpc_slice slice);
+
/** Create a slice by copying a string.
Does not preserve null terminators.
Equivalent to:
@@ -123,6 +129,9 @@ GPRAPI grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split);
GPRAPI grpc_slice grpc_empty_slice(void);
+GPRAPI uint32_t grpc_slice_default_hash_impl(grpc_slice s);
+GPRAPI int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b);
+
GPRAPI int grpc_slice_eq(grpc_slice a, grpc_slice b);
/** Returns <0 if a < b, ==0 if a == b, >0 if a > b
@@ -142,6 +151,8 @@ GPRAPI int grpc_slice_chr(grpc_slice s, char c);
if it's not found */
GPRAPI int grpc_slice_slice(grpc_slice haystack, grpc_slice needle);
+GPRAPI uint32_t grpc_slice_hash(grpc_slice s);
+
/** Do two slices point at the same memory, with the same length
If a or b is inlined, actually compares data */
GPRAPI int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b);
diff --git a/package.xml b/package.xml
index 8da5915d5b1..9090ab25a1a 100644
--- a/package.xml
+++ b/package.xml
@@ -1226,6 +1226,7 @@
+
@@ -1234,6 +1235,7 @@
+
diff --git a/src/core/ext/transport/binder/transport/binder_transport.cc b/src/core/ext/transport/binder/transport/binder_transport.cc
index 3206bec8e7f..964d6bef881 100644
--- a/src/core/ext/transport/binder/transport/binder_transport.cc
+++ b/src/core/ext/transport/binder/transport/binder_transport.cc
@@ -36,7 +36,7 @@
#include "src/core/ext/transport/binder/wire_format/wire_reader_impl.h"
#include "src/core/ext/transport/binder/wire_format/wire_writer.h"
#include "src/core/lib/iomgr/exec_ctx.h"
-#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
#include "src/core/lib/transport/byte_stream.h"
#include "src/core/lib/transport/error_utils.h"
#include "src/core/lib/transport/metadata_batch.h"
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
index cb17759bba5..18abbdacde7 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
@@ -647,6 +647,17 @@ class HPackParser::Input {
// Helper to parse a string and turn it into a slice with appropriate memory
// management characteristics
class HPackParser::String {
+ public:
+ // Helper to specify a string should be internalized
+ struct Intern {};
+ // Helper to specify a string should be externalized
+ struct Extern {};
+
+ private:
+ // Forward declare take functions... we'll need them in the public interface
+ Slice Take(Extern);
+ Slice Take(Intern);
+
public:
String(const String&) = delete;
String& operator=(const String&) = delete;
@@ -660,7 +671,11 @@ class HPackParser::String {
}
// Take the value and leave this empty
- Slice Take();
+ // Use Intern/Extern to choose memory management
+ template
+ auto Take() -> decltype(this->Take(T())) {
+ return Take(T());
+ }
// Return a reference to the value as a string view
absl::string_view string_view() const {
@@ -947,11 +962,13 @@ class HPackParser::Parser {
case 1:
switch (cur & 0xf) {
case 0: // literal key
- return FinishHeaderOmitFromTable(ParseLiteralKey());
+ return FinishHeaderOmitFromTable(ParseLiteralKey());
case 0xf: // varint encoded key index
- return FinishHeaderOmitFromTable(ParseVarIdxKey(0xf));
+ return FinishHeaderOmitFromTable(
+ ParseVarIdxKey(0xf));
default: // inline encoded key index
- return FinishHeaderOmitFromTable(ParseIdxKey(cur & 0xf));
+ return FinishHeaderOmitFromTable(
+ ParseIdxKey(cur & 0xf));
}
// Update max table size.
// First byte format: 001xxxxx
@@ -978,20 +995,23 @@ class HPackParser::Parser {
case 4:
if (cur == 0x40) {
// literal key
- return FinishHeaderAndAddToTable(ParseLiteralKey());
+ return FinishHeaderAndAddToTable(ParseLiteralKey());
}
ABSL_FALLTHROUGH_INTENDED;
case 5:
case 6:
// inline encoded key index
- return FinishHeaderAndAddToTable(ParseIdxKey(cur & 0x3f));
+ return FinishHeaderAndAddToTable(
+ ParseIdxKey(cur & 0x3f));
case 7:
if (cur == 0x7f) {
// varint encoded key index
- return FinishHeaderAndAddToTable(ParseVarIdxKey(0x3f));
+ return FinishHeaderAndAddToTable(
+ ParseVarIdxKey(0x3f));
} else {
// inline encoded key index
- return FinishHeaderAndAddToTable(ParseIdxKey(cur & 0x3f));
+ return FinishHeaderAndAddToTable(
+ ParseIdxKey(cur & 0x3f));
}
// Indexed Header Field Representation
// First byte format: 1xxxxxxx
@@ -1093,6 +1113,7 @@ class HPackParser::Parser {
}
// Parse a string encoded key and a string encoded value
+ template
absl::optional ParseLiteralKey() {
auto key = String::Parse(input_);
if (!key.has_value()) return {};
@@ -1101,7 +1122,7 @@ class HPackParser::Parser {
return {};
}
auto key_string = key->string_view();
- auto value_slice = value->Take();
+ auto value_slice = value->Take();
const auto transport_size = key_string.size() + value_slice.size() +
hpack_constants::kEntryOverhead;
return grpc_metadata_batch::Parse(
@@ -1112,6 +1133,7 @@ class HPackParser::Parser {
}
// Parse an index encoded key and a string encoded value
+ template
absl::optional ParseIdxKey(uint32_t index) {
const auto* elem = table_->Lookup(index);
if (GPR_UNLIKELY(elem == nullptr)) {
@@ -1120,17 +1142,19 @@ class HPackParser::Parser {
}
auto value = ParseValueString(elem->is_binary_header());
if (GPR_UNLIKELY(!value.has_value())) return {};
- return elem->WithNewValue(
- value->Take(), [=](absl::string_view error, const Slice& value) {
- ReportMetadataParseError(elem->key(), error, value.as_string_view());
- });
+ return elem->WithNewValue(value->Take(),
+ [=](absl::string_view error, const Slice& value) {
+ ReportMetadataParseError(
+ elem->key(), error, value.as_string_view());
+ });
}
// Parse a varint index encoded key and a string encoded value
+ template
absl::optional ParseVarIdxKey(uint32_t offset) {
auto index = input_->ParseVarint(offset);
if (GPR_UNLIKELY(!index.has_value())) return {};
- return ParseIdxKey(*index);
+ return ParseIdxKey(*index);
}
// Parse a string, figuring out if it's binary or not by the key name.
@@ -1226,7 +1250,7 @@ class HPackParser::Parser {
const LogInfo log_info_;
};
-Slice HPackParser::String::Take() {
+Slice HPackParser::String::Take(Extern) {
if (auto* p = absl::get_if(&value_)) {
return p->Copy();
} else if (auto* p = absl::get_if>(&value_)) {
@@ -1237,6 +1261,18 @@ Slice HPackParser::String::Take() {
GPR_UNREACHABLE_CODE(return Slice());
}
+Slice HPackParser::String::Take(Intern) {
+ ManagedMemorySlice m;
+ if (auto* p = absl::get_if(&value_)) {
+ m = ManagedMemorySlice(&p->c_slice());
+ } else if (auto* p = absl::get_if>(&value_)) {
+ m = ManagedMemorySlice(reinterpret_cast(p->data()), p->size());
+ } else if (auto* p = absl::get_if>(&value_)) {
+ m = ManagedMemorySlice(reinterpret_cast(p->data()), p->size());
+ }
+ return Slice(m);
+}
+
/* PUBLIC INTERFACE */
HPackParser::HPackParser() = default;
diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc
index d05f27b98c5..63dbd8e5824 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.cc
+++ b/src/core/ext/transport/chttp2/transport/parsing.cc
@@ -28,8 +28,8 @@
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/lib/profiling/timers.h"
-#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/slice/slice_utils.h"
#include "src/core/lib/transport/http2_errors.h"
#include "src/core/lib/transport/status_conversion.h"
#include "src/core/lib/transport/timeout_encoding.h"
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.cc b/src/core/ext/transport/cronet/transport/cronet_transport.cc
index f25d8e0f5a7..e82217e0d44 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.cc
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.cc
@@ -410,10 +410,11 @@ static void convert_cronet_array_to_metadata(
grpc_slice value;
if (absl::EndsWith(header_array->headers[i].key, "-bin")) {
value = grpc_slice_from_static_string(header_array->headers[i].value);
- value = grpc_chttp2_base64_decode_with_length(
- value, grpc_chttp2_base64_infer_length_after_decode(value));
+ value = grpc_slice_intern(grpc_chttp2_base64_decode_with_length(
+ value, grpc_chttp2_base64_infer_length_after_decode(value)));
} else {
- value = grpc_slice_from_static_string(header_array->headers[i].value);
+ value = grpc_slice_intern(
+ grpc_slice_from_static_string(header_array->headers[i].value));
}
mds->Append(header_array->headers[i].key, grpc_core::Slice(value),
[&](absl::string_view error, const grpc_core::Slice& value) {
diff --git a/src/core/ext/xds/xds_api.cc b/src/core/ext/xds/xds_api.cc
index d9cc5b8828a..6f885e18b4e 100644
--- a/src/core/ext/xds/xds_api.cc
+++ b/src/core/ext/xds/xds_api.cc
@@ -57,7 +57,7 @@
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/sockaddr.h"
#include "src/core/lib/iomgr/socket_utils.h"
-#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
#include "src/core/lib/uri/uri_parser.h"
namespace grpc_core {
diff --git a/src/core/ext/xds/xds_server_config_fetcher.cc b/src/core/ext/xds/xds_server_config_fetcher.cc
index e78c35e4a24..699ba1d2549 100644
--- a/src/core/ext/xds/xds_server_config_fetcher.cc
+++ b/src/core/ext/xds/xds_server_config_fetcher.cc
@@ -37,7 +37,7 @@
#include "src/core/lib/iomgr/sockaddr.h"
#include "src/core/lib/iomgr/socket_utils.h"
#include "src/core/lib/security/credentials/xds/xds_credentials.h"
-#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/server.h"
#include "src/core/lib/transport/error_utils.h"
diff --git a/src/core/lib/compression/compression.cc b/src/core/lib/compression/compression.cc
index ce39d7e8343..ba86dbd48ac 100644
--- a/src/core/lib/compression/compression.cc
+++ b/src/core/lib/compression/compression.cc
@@ -25,7 +25,7 @@
#include "src/core/lib/compression/compression_internal.h"
#include "src/core/lib/gpr/useful.h"
-#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
#include "src/core/lib/surface/api_trace.h"
int grpc_compression_algorithm_is_message(grpc_compression_algorithm) {
diff --git a/src/core/lib/compression/compression_internal.cc b/src/core/lib/compression/compression_internal.cc
index 01dd6c75218..3ca36af31b8 100644
--- a/src/core/lib/compression/compression_internal.cc
+++ b/src/core/lib/compression/compression_internal.cc
@@ -33,7 +33,7 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gpr/useful.h"
-#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
#include "src/core/lib/surface/api_trace.h"
namespace grpc_core {
diff --git a/src/core/lib/event_engine/memory_allocator.cc b/src/core/lib/event_engine/memory_allocator.cc
index afb056ecd7b..8b6994e76d8 100644
--- a/src/core/lib/event_engine/memory_allocator.cc
+++ b/src/core/lib/event_engine/memory_allocator.cc
@@ -26,24 +26,28 @@ namespace {
// Reference count for a slice allocated by MemoryAllocator::MakeSlice.
// Takes care of releasing memory back when the slice is destroyed.
-class SliceRefCount : public grpc_slice_refcount {
+class SliceRefCount {
public:
+ static void Destroy(void* p) {
+ auto* rc = static_cast(p);
+ rc->~SliceRefCount();
+ gpr_free(rc);
+ }
SliceRefCount(std::shared_ptr allocator,
size_t size)
- : grpc_slice_refcount(Destroy),
+ : base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this,
+ &base_),
allocator_(std::move(allocator)),
size_(size) {
// Nothing to do here.
}
~SliceRefCount() { allocator_->Release(size_); }
- private:
- static void Destroy(grpc_slice_refcount* p) {
- auto* rc = static_cast(p);
- rc->~SliceRefCount();
- gpr_free(rc);
- }
+ grpc_slice_refcount* base_refcount() { return &base_; }
+ private:
+ grpc_slice_refcount base_;
+ std::atomic refs_{1};
std::shared_ptr allocator_;
size_t size_;
};
@@ -55,7 +59,7 @@ grpc_slice MemoryAllocator::MakeSlice(MemoryRequest request) {
void* p = gpr_malloc(size);
new (p) SliceRefCount(allocator_, size);
grpc_slice slice;
- slice.refcount = static_cast(p);
+ slice.refcount = static_cast(p)->base_refcount();
slice.data.refcounted.bytes =
static_cast(p) + sizeof(SliceRefCount);
slice.data.refcounted.length = size - sizeof(SliceRefCount);
diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc
index a442f88f62e..f5147c62aac 100644
--- a/src/core/lib/iomgr/error.cc
+++ b/src/core/lib/iomgr/error.cc
@@ -35,6 +35,7 @@
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/iomgr/error_internal.h"
#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount(false,
"error_refcount");
diff --git a/src/core/lib/security/authorization/evaluate_args.cc b/src/core/lib/security/authorization/evaluate_args.cc
index 1571d8be55a..8d84f45c513 100644
--- a/src/core/lib/security/authorization/evaluate_args.cc
+++ b/src/core/lib/security/authorization/evaluate_args.cc
@@ -22,7 +22,7 @@
#include "src/core/lib/address_utils/sockaddr_utils.h"
#include "src/core/lib/gprpp/host_port.h"
#include "src/core/lib/security/credentials/tls/tls_utils.h"
-#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
namespace grpc_core {
diff --git a/src/core/lib/security/credentials/external/file_external_account_credentials.cc b/src/core/lib/security/credentials/external/file_external_account_credentials.cc
index a959e8c871f..d596d29414c 100644
--- a/src/core/lib/security/credentials/external/file_external_account_credentials.cc
+++ b/src/core/lib/security/credentials/external/file_external_account_credentials.cc
@@ -21,6 +21,7 @@
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
namespace grpc_core {
diff --git a/src/core/lib/slice/slice.cc b/src/core/lib/slice/slice.cc
index ce556a25702..e0c4cb038a0 100644
--- a/src/core/lib/slice/slice.cc
+++ b/src/core/lib/slice/slice.cc
@@ -18,8 +18,6 @@
#include
-#include "src/core/lib/slice/slice.h"
-
#include
#include
@@ -29,7 +27,6 @@
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/slice/slice_internal.h"
-#include "src/core/lib/slice/slice_refcount_base.h"
char* grpc_slice_to_c_string(grpc_slice slice) {
char* out = static_cast(gpr_malloc(GRPC_SLICE_LENGTH(slice) + 1));
@@ -38,9 +35,7 @@ char* grpc_slice_to_c_string(grpc_slice slice) {
return out;
}
-grpc_slice grpc_empty_slice(void) {
- return grpc_core::slice_detail::EmptySlice();
-}
+grpc_slice grpc_empty_slice(void) { return grpc_core::UnmanagedMemorySlice(); }
grpc_slice grpc_slice_copy(grpc_slice s) {
grpc_slice out = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(s));
@@ -51,21 +46,30 @@ grpc_slice grpc_slice_copy(grpc_slice s) {
namespace grpc_core {
+/* grpc_slice_from_static_string support structure - a refcount that does
+ nothing */
+grpc_slice_refcount kNoopRefcount(grpc_slice_refcount::Type::NOP);
+static_assert(std::is_trivially_destructible::value,
+ "kNoopRefcount must be trivially destructible.");
+
/* grpc_slice_new support structures - we create a refcount object extended
with the user provided data pointer & destroy function */
-class NewSliceRefcount : public grpc_slice_refcount {
+class NewSliceRefcount {
public:
+ static void Destroy(void* arg) { delete static_cast(arg); }
+
NewSliceRefcount(void (*destroy)(void*), void* user_data)
- : grpc_slice_refcount(Destroy),
+ : base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this,
+ &base_),
user_destroy_(destroy),
user_data_(user_data) {}
~NewSliceRefcount() { user_destroy_(user_data_); }
- private:
- static void Destroy(grpc_slice_refcount* arg) {
- delete static_cast(arg);
- }
+ grpc_slice_refcount* base_refcount() { return &base_; }
+ private:
+ grpc_slice_refcount base_;
+ std::atomic refs_{1};
void (*user_destroy_)(void*);
void* user_data_;
};
@@ -73,8 +77,7 @@ class NewSliceRefcount : public grpc_slice_refcount {
} // namespace grpc_core
size_t grpc_slice_memory_usage(grpc_slice s) {
- if (s.refcount == nullptr ||
- s.refcount == grpc_slice_refcount::NoopRefcount()) {
+ if (s.refcount == nullptr || s.refcount == &grpc_core::kNoopRefcount) {
return 0;
} else {
return s.data.refcounted.length;
@@ -82,18 +85,19 @@ size_t grpc_slice_memory_usage(grpc_slice s) {
}
grpc_slice grpc_slice_from_static_buffer(const void* s, size_t len) {
- return grpc_core::StaticSlice::FromStaticBuffer(s, len).TakeCSlice();
+ return grpc_core::ExternallyManagedSlice(s, len);
}
grpc_slice grpc_slice_from_static_string(const char* s) {
- return grpc_core::StaticSlice::FromStaticString(s).TakeCSlice();
+ return grpc_core::ExternallyManagedSlice(s, strlen(s));
}
grpc_slice grpc_slice_new_with_user_data(void* p, size_t len,
void (*destroy)(void*),
void* user_data) {
grpc_slice slice;
- slice.refcount = new grpc_core::NewSliceRefcount(destroy, user_data);
+ slice.refcount =
+ (new grpc_core::NewSliceRefcount(destroy, user_data))->base_refcount();
slice.data.refcounted.bytes = static_cast(p);
slice.data.refcounted.length = len;
return slice;
@@ -107,51 +111,68 @@ grpc_slice grpc_slice_new(void* p, size_t len, void (*destroy)(void*)) {
namespace grpc_core {
/* grpc_slice_new_with_len support structures - we create a refcount object
extended with the user provided data pointer & destroy function */
-class NewWithLenSliceRefcount : public grpc_slice_refcount {
+class NewWithLenSliceRefcount {
public:
+ static void Destroy(void* arg) {
+ delete static_cast(arg);
+ }
+
NewWithLenSliceRefcount(void (*destroy)(void*, size_t), void* user_data,
size_t user_length)
- : grpc_slice_refcount(Destroy),
+ : base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this,
+ &base_),
user_data_(user_data),
user_length_(user_length),
user_destroy_(destroy) {}
~NewWithLenSliceRefcount() { user_destroy_(user_data_, user_length_); }
- private:
- static void Destroy(grpc_slice_refcount* arg) {
- delete static_cast(arg);
- }
+ grpc_slice_refcount* base_refcount() { return &base_; }
+ private:
+ grpc_slice_refcount base_;
+ std::atomic refs_{1};
void* user_data_;
size_t user_length_;
void (*user_destroy_)(void*, size_t);
};
/** grpc_slice_from_moved_(string|buffer) ref count .*/
-class MovedStringSliceRefCount : public grpc_slice_refcount {
+class MovedStringSliceRefCount {
public:
explicit MovedStringSliceRefCount(UniquePtr&& str)
- : grpc_slice_refcount(Destroy), str_(std::move(str)) {}
+ : base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this,
+ &base_),
+ str_(std::move(str)) {}
+
+ grpc_slice_refcount* base_refcount() { return &base_; }
private:
- static void Destroy(grpc_slice_refcount* arg) {
+ static void Destroy(void* arg) {
delete static_cast(arg);
}
+ grpc_slice_refcount base_;
+ std::atomic refs_{1};
UniquePtr str_;
};
// grpc_slice_from_cpp_string() ref count.
-class MovedCppStringSliceRefCount : public grpc_slice_refcount {
+class MovedCppStringSliceRefCount {
public:
explicit MovedCppStringSliceRefCount(std::string&& str)
- : grpc_slice_refcount(Destroy), str_(std::move(str)) {}
+ : base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this,
+ &base_),
+ str_(std::move(str)) {}
+
+ grpc_slice_refcount* base_refcount() { return &base_; }
private:
- static void Destroy(grpc_slice_refcount* arg) {
+ static void Destroy(void* arg) {
delete static_cast(arg);
}
+ grpc_slice_refcount base_;
+ std::atomic refs_{1};
std::string str_;
};
@@ -160,17 +181,44 @@ class MovedCppStringSliceRefCount : public grpc_slice_refcount {
grpc_slice grpc_slice_new_with_len(void* p, size_t len,
void (*destroy)(void*, size_t)) {
grpc_slice slice;
- slice.refcount = new grpc_core::NewWithLenSliceRefcount(destroy, p, len);
+ slice.refcount = (new grpc_core::NewWithLenSliceRefcount(destroy, p, len))
+ ->base_refcount();
slice.data.refcounted.bytes = static_cast(p);
slice.data.refcounted.length = len;
return slice;
}
-grpc_slice grpc_slice_from_copied_buffer(const char* source, size_t len) {
- if (len == 0) return grpc_empty_slice();
- grpc_slice out = grpc_slice_malloc(len);
- memcpy(GRPC_SLICE_START_PTR(out), source, len);
- return out;
+grpc_core::UnmanagedMemorySlice::UnmanagedMemorySlice(const char* source,
+ size_t length) {
+ if (length <= sizeof(data.inlined.bytes)) {
+ refcount = nullptr;
+ data.inlined.length = static_cast(length);
+ } else {
+ HeapInit(length);
+ }
+ if (length > 0) {
+ memcpy(GRPC_SLICE_START_PTR(*this), source, length);
+ }
+}
+
+grpc_core::UnmanagedMemorySlice::UnmanagedMemorySlice(const char* source)
+ : grpc_core::UnmanagedMemorySlice::UnmanagedMemorySlice(source,
+ strlen(source)) {}
+
+grpc_slice grpc_slice_from_copied_buffer(const char* source, size_t length) {
+ grpc_slice slice;
+ if (length <= sizeof(slice.data.inlined.bytes)) {
+ slice.refcount = nullptr;
+ slice.data.inlined.length = length;
+ } else {
+ // Create a ref-counted slice.
+ slice = grpc_core::UnmanagedMemorySlice(
+ length, grpc_core::UnmanagedMemorySlice::ForceHeapAllocation());
+ }
+ if (length > 0) {
+ memcpy(GRPC_SLICE_START_PTR(slice), source, length);
+ }
+ return slice;
}
grpc_slice grpc_slice_from_copied_string(const char* source) {
@@ -186,7 +234,8 @@ grpc_slice grpc_slice_from_moved_buffer(grpc_core::UniquePtr p,
slice.data.inlined.length = len;
memcpy(GRPC_SLICE_START_PTR(slice), ptr, len);
} else {
- slice.refcount = new grpc_core::MovedStringSliceRefCount(std::move(p));
+ slice.refcount = (new grpc_core::MovedStringSliceRefCount(std::move(p)))
+ ->base_refcount();
slice.data.refcounted.bytes = ptr;
slice.data.refcounted.length = len;
}
@@ -208,44 +257,94 @@ grpc_slice grpc_slice_from_cpp_string(std::string str) {
slice.data.refcounted.bytes =
reinterpret_cast(const_cast(str.data()));
slice.data.refcounted.length = str.size();
- slice.refcount = new grpc_core::MovedCppStringSliceRefCount(std::move(str));
+ slice.refcount =
+ (new grpc_core::MovedCppStringSliceRefCount(std::move(str)))
+ ->base_refcount();
}
return slice;
}
+namespace {
+
+class MallocRefCount {
+ public:
+ static void Destroy(void* arg) {
+ MallocRefCount* r = static_cast(arg);
+ r->~MallocRefCount();
+ gpr_free(r);
+ }
+
+ MallocRefCount()
+ : base_(grpc_slice_refcount::Type::REGULAR, &refs_, Destroy, this,
+ &base_) {}
+ ~MallocRefCount() = default;
+
+ grpc_slice_refcount* base_refcount() { return &base_; }
+
+ private:
+ grpc_slice_refcount base_;
+ std::atomic refs_{1};
+};
+
+} // namespace
+
grpc_slice grpc_slice_malloc_large(size_t length) {
- grpc_slice slice;
- uint8_t* memory = new uint8_t[sizeof(grpc_slice_refcount) + length];
- slice.refcount = new (memory) grpc_slice_refcount(
- [](grpc_slice_refcount* p) { delete[] reinterpret_cast(p); });
- slice.data.refcounted.bytes = memory + sizeof(grpc_slice_refcount);
- slice.data.refcounted.length = length;
- return slice;
+ return grpc_core::UnmanagedMemorySlice(
+ length, grpc_core::UnmanagedMemorySlice::ForceHeapAllocation());
+}
+
+void grpc_core::UnmanagedMemorySlice::HeapInit(size_t length) {
+ /* Memory layout used by the slice created here:
+
+ +-----------+----------------------------------------------------------+
+ | refcount | bytes |
+ +-----------+----------------------------------------------------------+
+
+ refcount is a malloc_refcount
+ bytes is an array of bytes of the requested length
+ Both parts are placed in the same allocation returned from gpr_malloc */
+ auto* rc =
+ static_cast(gpr_malloc(sizeof(MallocRefCount) + length));
+
+ /* Initial refcount on rc is 1 - and it's up to the caller to release
+ this reference. */
+ new (rc) MallocRefCount();
+
+ /* Build up the slice to be returned. */
+ /* The slices refcount points back to the allocated block. */
+ refcount = rc->base_refcount();
+ /* The data bytes are placed immediately after the refcount struct */
+ data.refcounted.bytes = reinterpret_cast(rc + 1);
+ /* And the length of the block is set to the requested length */
+ data.refcounted.length = length;
}
grpc_slice grpc_slice_malloc(size_t length) {
- if (length <= GRPC_SLICE_INLINED_SIZE) {
- grpc_slice slice;
- slice.refcount = nullptr;
- slice.data.inlined.length = length;
- return slice;
+ return grpc_core::UnmanagedMemorySlice(length);
+}
+
+grpc_core::UnmanagedMemorySlice::UnmanagedMemorySlice(size_t length) {
+ if (length > sizeof(data.inlined.bytes)) {
+ HeapInit(length);
} else {
- return grpc_slice_malloc_large(length);
+ /* small slice: just inline the data */
+ refcount = nullptr;
+ data.inlined.length = static_cast(length);
}
}
-static grpc_slice sub_no_ref(const grpc_slice& source, size_t begin,
- size_t end) {
- grpc_slice subset;
+template
+static Slice sub_no_ref(const Slice& source, size_t begin, size_t end) {
+ Slice subset;
GPR_ASSERT(end >= begin);
- if (source.refcount != nullptr) {
+ if (source.refcount) {
/* Enforce preconditions */
GPR_ASSERT(source.data.refcounted.length >= end);
/* Build the result */
- subset.refcount = source.refcount;
+ subset.refcount = source.refcount->sub_refcount();
/* Point into the source array */
subset.data.refcounted.bytes = source.data.refcounted.bytes + begin;
subset.data.refcounted.length = end - begin;
@@ -264,6 +363,11 @@ grpc_slice grpc_slice_sub_no_ref(grpc_slice source, size_t begin, size_t end) {
return sub_no_ref(source, begin, end);
}
+grpc_core::UnmanagedMemorySlice grpc_slice_sub_no_ref(
+ const grpc_core::UnmanagedMemorySlice& source, size_t begin, size_t end) {
+ return sub_no_ref(source, begin, end);
+}
+
grpc_slice grpc_slice_sub(grpc_slice source, size_t begin, size_t end) {
grpc_slice subset;
@@ -293,12 +397,6 @@ grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice* source, size_t split,
memcpy(tail.data.inlined.bytes, source->data.inlined.bytes + split,
tail.data.inlined.length);
source->data.inlined.length = static_cast(split);
- } else if (source->refcount == grpc_slice_refcount::NoopRefcount()) {
- /* refcount == NoopRefcount(), so we can just split in-place */
- tail.refcount = grpc_slice_refcount::NoopRefcount();
- tail.data.refcounted.bytes = source->data.refcounted.bytes + split;
- tail.data.refcounted.length = source->data.refcounted.length - split;
- source->data.refcounted.length = split;
} else {
size_t tail_length = source->data.refcounted.length - split;
GPR_ASSERT(source->data.refcounted.length >= split);
@@ -309,18 +407,21 @@ grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice* source, size_t split,
tail.data.inlined.length = static_cast(tail_length);
memcpy(tail.data.inlined.bytes, source->data.refcounted.bytes + split,
tail_length);
+ source->refcount = source->refcount->sub_refcount();
} else {
/* Build the result */
switch (ref_whom) {
case GRPC_SLICE_REF_TAIL:
- tail.refcount = source->refcount;
- source->refcount = grpc_slice_refcount::NoopRefcount();
+ tail.refcount = source->refcount->sub_refcount();
+ source->refcount = &grpc_core::kNoopRefcount;
break;
case GRPC_SLICE_REF_HEAD:
- tail.refcount = grpc_slice_refcount::NoopRefcount();
+ tail.refcount = &grpc_core::kNoopRefcount;
+ source->refcount = source->refcount->sub_refcount();
break;
case GRPC_SLICE_REF_BOTH:
- tail.refcount = source->refcount;
+ tail.refcount = source->refcount->sub_refcount();
+ source->refcount = source->refcount->sub_refcount();
/* Bump the refcount */
tail.refcount->Ref();
break;
@@ -358,18 +459,20 @@ grpc_slice grpc_slice_split_head(grpc_slice* source, size_t split) {
head.refcount = nullptr;
head.data.inlined.length = static_cast(split);
memcpy(head.data.inlined.bytes, source->data.refcounted.bytes, split);
+ source->refcount = source->refcount->sub_refcount();
source->data.refcounted.bytes += split;
source->data.refcounted.length -= split;
} else {
GPR_ASSERT(source->data.refcounted.length >= split);
/* Build the result */
- head.refcount = source->refcount;
+ head.refcount = source->refcount->sub_refcount();
/* Bump the refcount */
head.refcount->Ref();
/* Point into the source array */
head.data.refcounted.bytes = source->data.refcounted.bytes;
head.data.refcounted.length = split;
+ source->refcount = source->refcount->sub_refcount();
source->data.refcounted.bytes += split;
source->data.refcounted.length -= split;
}
@@ -377,13 +480,21 @@ grpc_slice grpc_slice_split_head(grpc_slice* source, size_t split) {
return head;
}
-int grpc_slice_eq(grpc_slice a, grpc_slice b) {
+int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b) {
if (GRPC_SLICE_LENGTH(a) != GRPC_SLICE_LENGTH(b)) return false;
if (GRPC_SLICE_LENGTH(a) == 0) return true;
return 0 == memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b),
GRPC_SLICE_LENGTH(a));
}
+int grpc_slice_eq(grpc_slice a, grpc_slice b) {
+ if (a.refcount && b.refcount &&
+ a.refcount->GetType() == b.refcount->GetType()) {
+ return a.refcount->Eq(a, b);
+ }
+ return grpc_slice_default_eq_impl(a, b);
+}
+
int grpc_slice_differs_refcounted(const grpc_slice& a,
const grpc_slice& b_not_inline) {
size_t a_len;
diff --git a/src/core/lib/slice/slice.h b/src/core/lib/slice/slice.h
index c4c9284bec6..357ea708ccc 100644
--- a/src/core/lib/slice/slice.h
+++ b/src/core/lib/slice/slice.h
@@ -25,8 +25,6 @@
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/slice/slice_internal.h"
-#include "src/core/lib/slice/slice_refcount.h"
-#include "src/core/lib/slice/slice_refcount_base.h"
// Herein lies grpc_core::Slice and its team of thin wrappers around grpc_slice.
// They aim to keep you safe by providing strong guarantees around lifetime and
@@ -105,8 +103,6 @@ class BaseSlice {
return grpc_slice_is_equivalent(slice_, other.slice_);
}
- uint32_t Hash() const { return grpc_slice_hash_internal(slice_); }
-
protected:
BaseSlice() : slice_(EmptySlice()) {}
explicit BaseSlice(const grpc_slice& slice) : slice_(slice) {}
@@ -166,7 +162,7 @@ inline bool operator!=(const grpc_slice& a, const BaseSlice& b) {
template
struct CopyConstructors {
static Out FromCopiedString(const char* s) {
- return FromCopiedBuffer(s, strlen(s));
+ return Out(grpc_slice_from_copied_string(s));
}
static Out FromCopiedString(absl::string_view s) {
return FromCopiedBuffer(s.data(), s.size());
@@ -175,7 +171,7 @@ struct CopyConstructors {
return Out(grpc_slice_from_cpp_string(std::move(s)));
}
static Out FromCopiedBuffer(const char* p, size_t len) {
- return Out(grpc_slice_from_copied_buffer(p, len));
+ return Out(UnmanagedMemorySlice(p, len));
}
template
@@ -194,20 +190,11 @@ struct CopyConstructors {
template
struct StaticConstructors {
static Out FromStaticString(const char* s) {
- return FromStaticBuffer(s, strlen(s));
+ return Out(grpc_slice_from_static_string(s));
}
static Out FromStaticString(absl::string_view s) {
- return FromStaticBuffer(s.data(), s.size());
- }
-
- static Out FromStaticBuffer(const void* s, size_t len) {
- grpc_slice slice;
- slice.refcount = grpc_slice_refcount::NoopRefcount();
- slice.data.refcounted.bytes =
- const_cast(static_cast(s));
- slice.data.refcounted.length = len;
- return Out(slice);
+ return Out(ExternallyManagedSlice(s.data(), s.size()));
}
};
@@ -219,8 +206,11 @@ class StaticSlice : public slice_detail::BaseSlice,
StaticSlice() = default;
explicit StaticSlice(const grpc_slice& slice)
: slice_detail::BaseSlice(slice) {
- GPR_DEBUG_ASSERT(slice.refcount == grpc_slice_refcount::NoopRefcount());
+ GPR_DEBUG_ASSERT(slice.refcount->GetType() ==
+ grpc_slice_refcount::Type::NOP);
}
+ explicit StaticSlice(const StaticMetadataSlice& slice)
+ : slice_detail::BaseSlice(slice) {}
StaticSlice(const StaticSlice& other)
: slice_detail::BaseSlice(other.c_slice()) {}
@@ -242,7 +232,8 @@ class MutableSlice : public slice_detail::BaseSlice,
MutableSlice() = default;
explicit MutableSlice(const grpc_slice& slice)
: slice_detail::BaseSlice(slice) {
- GPR_DEBUG_ASSERT(slice.refcount == nullptr || slice.refcount->IsUnique());
+ GPR_DEBUG_ASSERT(slice.refcount == nullptr ||
+ slice.refcount->IsRegularUnique());
}
~MutableSlice() { grpc_slice_unref_internal(c_slice()); }
@@ -306,7 +297,7 @@ class Slice : public slice_detail::BaseSlice,
if (c_slice().refcount == nullptr) {
return Slice(c_slice());
}
- if (c_slice().refcount == grpc_slice_refcount::NoopRefcount()) {
+ if (c_slice().refcount->GetType() == grpc_slice_refcount::Type::NOP) {
return Slice(grpc_slice_copy(c_slice()));
}
return Slice(TakeCSlice());
@@ -318,7 +309,7 @@ class Slice : public slice_detail::BaseSlice,
if (c_slice().refcount == nullptr) {
return Slice(c_slice());
}
- if (c_slice().refcount == grpc_slice_refcount::NoopRefcount()) {
+ if (c_slice().refcount->GetType() == grpc_slice_refcount::Type::NOP) {
return Slice(grpc_slice_copy(c_slice()));
}
return Slice(grpc_slice_ref_internal(c_slice()));
@@ -336,8 +327,8 @@ class Slice : public slice_detail::BaseSlice,
if (c_slice().refcount == nullptr) {
return MutableSlice(c_slice());
}
- if (c_slice().refcount != grpc_slice_refcount::NoopRefcount() &&
- c_slice().refcount->IsUnique()) {
+ if (c_slice().refcount->GetType() == grpc_slice_refcount::Type::REGULAR &&
+ c_slice().refcount->IsRegularUnique()) {
return MutableSlice(TakeCSlice());
}
return MutableSlice(grpc_slice_copy(c_slice()));
@@ -368,15 +359,11 @@ class Slice : public slice_detail::BaseSlice,
const uint8_t* begin, const uint8_t* end) {
grpc_slice out;
out.refcount = r;
- if (r != grpc_slice_refcount::NoopRefcount()) r->Ref();
+ r->Ref();
out.data.refcounted.bytes = const_cast(begin);
out.data.refcounted.length = end - begin;
return Slice(out);
}
-
- static Slice FromExternalString(absl::string_view str) {
- return FromStaticString(str);
- }
};
} // namespace grpc_core
diff --git a/src/core/lib/slice/slice_intern.cc b/src/core/lib/slice/slice_intern.cc
new file mode 100644
index 00000000000..2ab13d1458b
--- /dev/null
+++ b/src/core/lib/slice/slice_intern.cc
@@ -0,0 +1,269 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include "src/core/lib/gpr/murmur_hash.h"
+#include "src/core/lib/gprpp/sync.h"
+#include "src/core/lib/iomgr/iomgr_internal.h" /* for iomgr_abort_on_leaks() */
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/slice/slice_utils.h"
+
+#define LOG2_SHARD_COUNT 5
+#define SHARD_COUNT (1 << LOG2_SHARD_COUNT)
+#define INITIAL_SHARD_CAPACITY 8
+
+#define TABLE_IDX(hash, capacity) (((hash) >> LOG2_SHARD_COUNT) % (capacity))
+#define SHARD_IDX(hash) ((hash) & ((1 << LOG2_SHARD_COUNT) - 1))
+
+using grpc_core::InternedSliceRefcount;
+
+typedef struct slice_shard {
+ grpc_core::Mutex mu;
+ InternedSliceRefcount** strs;
+ size_t count;
+ size_t capacity;
+} slice_shard;
+
+static slice_shard* g_shards;
+
+namespace grpc_core {
+
+/* hash seed: decided at initialization time */
+uint32_t g_hash_seed;
+static bool g_forced_hash_seed = false;
+
+InternedSliceRefcount::~InternedSliceRefcount() {
+ slice_shard* shard = &g_shards[SHARD_IDX(this->hash)];
+ MutexLock lock(&shard->mu);
+ InternedSliceRefcount** prev_next;
+ InternedSliceRefcount* cur;
+ for (prev_next = &shard->strs[TABLE_IDX(this->hash, shard->capacity)],
+ cur = *prev_next;
+ cur != this; prev_next = &cur->bucket_next, cur = cur->bucket_next) {
+ }
+ *prev_next = cur->bucket_next;
+ shard->count--;
+}
+
+} // namespace grpc_core
+
+static void grow_shard(slice_shard* shard) {
+ GPR_TIMER_SCOPE("grow_strtab", 0);
+
+ size_t capacity = shard->capacity * 2;
+ size_t i;
+ InternedSliceRefcount** strtab;
+ InternedSliceRefcount *s, *next;
+
+ strtab = static_cast(
+ gpr_zalloc(sizeof(InternedSliceRefcount*) * capacity));
+
+ for (i = 0; i < shard->capacity; i++) {
+ for (s = shard->strs[i]; s; s = next) {
+ size_t idx = TABLE_IDX(s->hash, capacity);
+ next = s->bucket_next;
+ s->bucket_next = strtab[idx];
+ strtab[idx] = s;
+ }
+ }
+ gpr_free(shard->strs);
+ shard->strs = strtab;
+ shard->capacity = capacity;
+}
+
+grpc_core::InternedSlice::InternedSlice(InternedSliceRefcount* s) {
+ refcount = &s->base;
+ data.refcounted.bytes = reinterpret_cast(s + 1);
+ data.refcounted.length = s->length;
+}
+
+uint32_t grpc_slice_default_hash_impl(grpc_slice s) {
+ return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s),
+ grpc_core::g_hash_seed);
+}
+
+uint32_t grpc_slice_hash(grpc_slice s) { return grpc_slice_hash_internal(s); }
+
+grpc_slice grpc_slice_intern(grpc_slice slice) {
+ /* TODO(arjunroy): At present, this is capable of returning either a static or
+ an interned slice. This yields weirdness like the constructor for
+ ManagedMemorySlice instantiating itself as an instance of a derived type
+ (StaticMetadataSlice or InternedSlice). Should reexamine. */
+ return grpc_core::ManagedMemorySlice(&slice);
+}
+
+// Helper methods to enable us to select appropriately overloaded slice methods
+// whether we're dealing with a slice, or a buffer with length, when interning
+// strings. Helpers for FindOrCreateInternedSlice().
+static const char* GetBuffer(const std::pair& buflen) {
+ return buflen.first;
+}
+static size_t GetLength(const std::pair& buflen) {
+ return buflen.second;
+}
+static const void* GetBuffer(const grpc_slice& slice) {
+ return GRPC_SLICE_START_PTR(slice);
+}
+static size_t GetLength(const grpc_slice& slice) {
+ return GRPC_SLICE_LENGTH(slice);
+}
+
+// Creates an interned slice for a string that does not currently exist in the
+// intern table. SliceArgs is either a const grpc_slice& or a const
+// pair&. Hash is the pre-computed hash value. We must
+// already hold the shard lock. Helper for FindOrCreateInternedSlice().
+//
+// Returns: a newly interned slice.
+template
+static InternedSliceRefcount* InternNewStringLocked(slice_shard* shard,
+ size_t shard_idx,
+ uint32_t hash,
+ const SliceArgs& args) {
+ /* string data goes after the internal_string header */
+ size_t len = GetLength(args);
+ const void* buffer = GetBuffer(args);
+ InternedSliceRefcount* s =
+ static_cast(gpr_malloc(sizeof(*s) + len));
+ new (s) grpc_core::InternedSliceRefcount(len, hash, shard->strs[shard_idx]);
+ // TODO(arjunroy): Investigate why hpack tried to intern the nullptr string.
+ // https://github.com/grpc/grpc/pull/20110#issuecomment-526729282
+ if (len > 0) {
+ memcpy(reinterpret_cast(s + 1), buffer, len);
+ }
+ shard->strs[shard_idx] = s;
+ shard->count++;
+ if (shard->count > shard->capacity * 2) {
+ grow_shard(shard);
+ }
+ return s;
+}
+
+// Attempt to see if the provided slice or string matches an existing interned
+// slice. SliceArgs... is either a const grpc_slice& or a string and length. In
+// either case, hash is the pre-computed hash value. We must already hold the
+// shard lock. Helper for FindOrCreateInternedSlice().
+//
+// Returns: a pre-existing matching static slice, or null.
+template
+static InternedSliceRefcount* MatchInternedSliceLocked(uint32_t hash,
+ size_t idx,
+ const SliceArgs& args) {
+ InternedSliceRefcount* s;
+ slice_shard* shard = &g_shards[SHARD_IDX(hash)];
+ /* search for an existing string */
+ for (s = shard->strs[idx]; s; s = s->bucket_next) {
+ if (s->hash == hash && grpc_core::InternedSlice(s) == args) {
+ if (grpc_core::IncrementIfNonzero(&s->refcnt)) {
+ return s;
+ }
+ }
+ }
+ return nullptr;
+}
+
+// Attempt to see if the provided slice or string matches an existing interned
+// slice, and failing that, create an interned slice with its contents. Returns
+// either the existing matching interned slice or the newly created one.
+// SliceArgs is either a const grpc_slice& or const pair&.
+// In either case, hash is the pre-computed hash value. We do not hold the
+// shard lock here, but do take it.
+//
+// Returns: an interned slice, either pre-existing/matched or newly created.
+template
+static InternedSliceRefcount* FindOrCreateInternedSlice(uint32_t hash,
+ const SliceArgs& args) {
+ slice_shard* shard = &g_shards[SHARD_IDX(hash)];
+ grpc_core::MutexLock lock(&shard->mu);
+ const size_t idx = TABLE_IDX(hash, shard->capacity);
+ InternedSliceRefcount* s = MatchInternedSliceLocked(hash, idx, args);
+ if (s == nullptr) {
+ s = InternNewStringLocked(shard, idx, hash, args);
+ }
+ return s;
+}
+
+grpc_core::ManagedMemorySlice::ManagedMemorySlice(const char* string)
+ : grpc_core::ManagedMemorySlice::ManagedMemorySlice(string,
+ strlen(string)) {}
+
+grpc_core::ManagedMemorySlice::ManagedMemorySlice(const char* buf, size_t len) {
+ GPR_TIMER_SCOPE("grpc_slice_intern", 0);
+ const uint32_t hash = gpr_murmur_hash3(buf, len, g_hash_seed);
+ *this = grpc_core::InternedSlice(FindOrCreateInternedSlice(
+ hash, std::pair(buf, len)));
+}
+
+grpc_core::ManagedMemorySlice::ManagedMemorySlice(const grpc_slice* slice_ptr) {
+ GPR_TIMER_SCOPE("grpc_slice_intern", 0);
+ const grpc_slice& slice = *slice_ptr;
+ const uint32_t hash = grpc_slice_hash_internal(slice);
+ *this = grpc_core::InternedSlice(FindOrCreateInternedSlice(hash, slice));
+}
+
+void grpc_test_only_set_slice_hash_seed(uint32_t seed) {
+ grpc_core::g_hash_seed = seed;
+ grpc_core::g_forced_hash_seed = true;
+}
+
+void grpc_slice_intern_init(void) {
+ if (!grpc_core::g_forced_hash_seed) {
+ grpc_core::g_hash_seed =
+ static_cast(gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
+ }
+ g_shards = new slice_shard[SHARD_COUNT];
+ for (size_t i = 0; i < SHARD_COUNT; i++) {
+ slice_shard* shard = &g_shards[i];
+ shard->count = 0;
+ shard->capacity = INITIAL_SHARD_CAPACITY;
+ shard->strs = static_cast(
+ gpr_zalloc(sizeof(*shard->strs) * shard->capacity));
+ }
+}
+
+void grpc_slice_intern_shutdown(void) {
+ for (size_t i = 0; i < SHARD_COUNT; i++) {
+ slice_shard* shard = &g_shards[i];
+ /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
+ if (shard->count != 0) {
+ gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata strings were leaked",
+ shard->count);
+ for (size_t j = 0; j < shard->capacity; j++) {
+ for (InternedSliceRefcount* s = shard->strs[j]; s; s = s->bucket_next) {
+ char* text = grpc_dump_slice(grpc_core::InternedSlice(s),
+ GPR_DUMP_HEX | GPR_DUMP_ASCII);
+ gpr_log(GPR_DEBUG, "LEAKED: %s", text);
+ gpr_free(text);
+ }
+ }
+ if (grpc_iomgr_abort_on_leaks()) {
+ abort();
+ }
+ }
+ gpr_free(shard->strs);
+ }
+ delete[] g_shards;
+}
diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h
index d4f22bbfec0..ae664edc6ce 100644
--- a/src/core/lib/slice/slice_internal.h
+++ b/src/core/lib/slice/slice_internal.h
@@ -23,8 +23,6 @@
#include
-#include "absl/strings/string_view.h"
-
#include
#include
#include
@@ -33,6 +31,7 @@
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/slice/slice_refcount.h"
+#include "src/core/lib/slice/slice_utils.h"
void grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer* sb);
void grpc_slice_buffer_partial_unref_internal(grpc_slice_buffer* sb,
@@ -53,6 +52,21 @@ void grpc_slice_buffer_remove_first(grpc_slice_buffer* sb);
void grpc_slice_buffer_sub_first(grpc_slice_buffer* sb, size_t begin,
size_t end);
+/* Check if a slice is interned */
+bool grpc_slice_is_interned(const grpc_slice& slice);
+inline bool grpc_slice_is_interned(const grpc_slice& slice) {
+ return (slice.refcount &&
+ (slice.refcount->GetType() == grpc_slice_refcount::Type::INTERNED));
+}
+
+inline bool grpc_slice_static_interned_equal(const grpc_slice& a,
+ const grpc_slice& b) {
+ GPR_DEBUG_ASSERT(grpc_slice_is_interned(a) && grpc_slice_is_interned(b));
+ return a.refcount == b.refcount;
+}
+
+void grpc_slice_intern_init(void);
+void grpc_slice_intern_shutdown(void);
void grpc_test_only_set_slice_hash_seed(uint32_t seed);
// if slice matches a static slice, returns the static slice
// otherwise returns the passed in slice (without reffing it)
@@ -63,11 +77,21 @@ grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice,
uint32_t grpc_static_slice_hash(grpc_slice s);
int grpc_static_slice_eq(grpc_slice a, grpc_slice b);
-inline uint32_t grpc_slice_hash_internal(const grpc_slice& s) {
+inline uint32_t grpc_slice_hash_refcounted(const grpc_slice& s) {
+ GPR_DEBUG_ASSERT(s.refcount != nullptr);
+ return s.refcount->Hash(s);
+}
+
+inline uint32_t grpc_slice_default_hash_internal(const grpc_slice& s) {
return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s),
grpc_core::g_hash_seed);
}
+inline uint32_t grpc_slice_hash_internal(const grpc_slice& s) {
+ return s.refcount == nullptr ? grpc_slice_default_hash_internal(s)
+ : grpc_slice_hash_refcounted(s);
+}
+
grpc_slice grpc_slice_from_moved_buffer(grpc_core::UniquePtr p,
size_t len);
grpc_slice grpc_slice_from_moved_string(grpc_core::UniquePtr p);
@@ -78,6 +102,9 @@ grpc_slice grpc_slice_from_cpp_string(std::string str);
// 0. All other slices will return the size of the allocated chars.
size_t grpc_slice_memory_usage(grpc_slice s);
+grpc_core::UnmanagedMemorySlice grpc_slice_sub_no_ref(
+ const grpc_core::UnmanagedMemorySlice& source, size_t begin, size_t end);
+
namespace grpc_core {
struct SliceHash {
@@ -86,15 +113,6 @@ struct SliceHash {
}
};
-extern uint32_t g_hash_seed;
-
-// Converts grpc_slice to absl::string_view.
-inline absl::string_view StringViewFromSlice(const grpc_slice& slice) {
- return absl::string_view(
- reinterpret_cast(GRPC_SLICE_START_PTR(slice)),
- GRPC_SLICE_LENGTH(slice));
-}
-
} // namespace grpc_core
inline bool operator==(const grpc_slice& s1, const grpc_slice& s2) {
diff --git a/src/core/lib/slice/slice_refcount.cc b/src/core/lib/slice/slice_refcount.cc
index c3c0226a80e..10de62b72e7 100644
--- a/src/core/lib/slice/slice_refcount.cc
+++ b/src/core/lib/slice/slice_refcount.cc
@@ -15,19 +15,3 @@
#include
#include "src/core/lib/slice/slice_refcount.h"
-
-#include
-
-namespace grpc_core {
-
-uint32_t g_hash_seed = []() {
- std::random_device rd;
- std::uniform_int_distribution dist;
- return dist(rd);
-}();
-
-} // namespace grpc_core
-
-void grpc_test_only_set_slice_hash_seed(uint32_t seed) {
- grpc_core::g_hash_seed = seed;
-}
diff --git a/src/core/lib/slice/slice_refcount.h b/src/core/lib/slice/slice_refcount.h
index c3a90ca6f2a..a2f5f918ca2 100644
--- a/src/core/lib/slice/slice_refcount.h
+++ b/src/core/lib/slice/slice_refcount.h
@@ -27,18 +27,88 @@
namespace grpc_core {
extern uint32_t g_hash_seed;
+extern grpc_slice_refcount kNoopRefcount;
+
+// TODO(ctiller): when this is removed, remove the std::atomic* in
+// grpc_slice_refcount and just put it there directly.
+struct InternedSliceRefcount {
+ static void Destroy(void* arg) {
+ auto* rc = static_cast(arg);
+ rc->~InternedSliceRefcount();
+ gpr_free(rc);
+ }
+
+ InternedSliceRefcount(size_t length, uint32_t hash,
+ InternedSliceRefcount* bucket_next)
+ : base(grpc_slice_refcount::Type::INTERNED, &refcnt, Destroy, this, &sub),
+ sub(grpc_slice_refcount::Type::REGULAR, &refcnt, Destroy, this, &sub),
+ length(length),
+ hash(hash),
+ bucket_next(bucket_next) {}
+
+ ~InternedSliceRefcount();
+
+ grpc_slice_refcount base;
+ grpc_slice_refcount sub;
+ const size_t length;
+ std::atomic refcnt{1};
+ const uint32_t hash;
+ InternedSliceRefcount* bucket_next;
+};
} // namespace grpc_core
+inline size_t grpc_refcounted_slice_length(const grpc_slice& slice) {
+ GPR_DEBUG_ASSERT(slice.refcount != nullptr);
+ return slice.data.refcounted.length;
+}
+
+inline const uint8_t* grpc_refcounted_slice_data(const grpc_slice& slice) {
+ GPR_DEBUG_ASSERT(slice.refcount != nullptr);
+ return slice.data.refcounted.bytes;
+}
+
+inline int grpc_slice_refcount::Eq(const grpc_slice& a, const grpc_slice& b) {
+ GPR_DEBUG_ASSERT(a.refcount != nullptr);
+ GPR_DEBUG_ASSERT(a.refcount == this);
+ switch (ref_type_) {
+ case Type::INTERNED:
+ return a.refcount == b.refcount;
+ case Type::NOP:
+ case Type::REGULAR:
+ break;
+ }
+ if (grpc_refcounted_slice_length(a) != GRPC_SLICE_LENGTH(b)) return false;
+ if (grpc_refcounted_slice_length(a) == 0) return true;
+ return 0 == memcmp(grpc_refcounted_slice_data(a), GRPC_SLICE_START_PTR(b),
+ grpc_refcounted_slice_length(a));
+}
+
+inline uint32_t grpc_slice_refcount::Hash(const grpc_slice& slice) {
+ GPR_DEBUG_ASSERT(slice.refcount != nullptr);
+ GPR_DEBUG_ASSERT(slice.refcount == this);
+ switch (ref_type_) {
+ case Type::INTERNED:
+ return reinterpret_cast(slice.refcount)
+ ->hash;
+ case Type::NOP:
+ case Type::REGULAR:
+ break;
+ }
+ return gpr_murmur_hash3(grpc_refcounted_slice_data(slice),
+ grpc_refcounted_slice_length(slice),
+ grpc_core::g_hash_seed);
+}
+
inline const grpc_slice& grpc_slice_ref_internal(const grpc_slice& slice) {
- if (reinterpret_cast(slice.refcount) > 1) {
+ if (slice.refcount) {
slice.refcount->Ref();
}
return slice;
}
inline void grpc_slice_unref_internal(const grpc_slice& slice) {
- if (reinterpret_cast(slice.refcount) > 1) {
+ if (slice.refcount) {
slice.refcount->Unref();
}
}
diff --git a/src/core/lib/slice/slice_refcount_base.h b/src/core/lib/slice/slice_refcount_base.h
index 79aaf9c1f04..5d81fcd8fc8 100644
--- a/src/core/lib/slice/slice_refcount_base.h
+++ b/src/core/lib/slice/slice_refcount_base.h
@@ -23,39 +23,143 @@
#include
// grpc_slice_refcount : A reference count for grpc_slice.
+//
+// Non-inlined grpc_slice objects are refcounted. Historically this was
+// implemented via grpc_slice_refcount, a C-style polymorphic class using a
+// manually managed vtable of operations. Subclasses would define their own
+// vtable; the 'virtual' methods (ref, unref, equals and hash) would simply call
+// the function pointers in the vtable as necessary.
+//
+// Unfortunately, this leads to some inefficiencies in the generated code that
+// can be improved upon. For example, equality checking for interned slices is a
+// simple equality check on the refcount pointer. With the vtable approach, this
+// would translate to roughly the following (high-level) instructions:
+//
+// grpc_slice_equals(slice1, slice2):
+// load vtable->eq -> eq_func
+// call eq_func(slice1, slice2)
+//
+// interned_slice_equals(slice1, slice2)
+// load slice1.ref -> r1
+// load slice2.ref -> r2
+// cmp r1, r2 -> retval
+// ret retval
+//
+// This leads to a function call for a function defined in another translation
+// unit, which imposes memory barriers, which reduces the compiler's ability to
+// optimize (in addition to the added overhead of call/ret). Additionally, it
+// may be harder to reason about branch prediction when we're jumping to
+// essentially arbitrarily provided function pointers.
+//
+// In addition, it is arguable that while virtualization was helpful for
+// Equals()/Hash() methods, that it was fundamentally unnecessary for
+// Ref()/Unref().
+//
+// Instead, grpc_slice_refcount provides the same functionality as the C-style
+// virtual class, but in a de-virtualized manner - Eq(), Hash(), Ref() and
+// Unref() are provided within this header file. Fastpaths for Eq()/Hash()
+// (interned and static metadata slices), as well as the Ref() operation, can
+// all be inlined without any memory barriers.
+//
+// It does this by:
+// 1. Using grpc_core::RefCount<> (header-only) for Ref/Unref. Two special cases
+// need support: No-op ref/unref (eg. static metadata slices) and stream
+// slice references (where all the slices share the streamref). This is in
+// addition to the normal case of '1 slice, 1 ref'.
+// To support these cases, we explicitly track a nullable pointer to the
+// underlying RefCount<>. No-op ref/unref is used by checking the pointer for
+// null, and doing nothing if it is. Both stream slice refs and 'normal'
+// slices use the same path for Ref/Unref (by targeting the non-null
+// pointer).
+//
+// 2. introducing the notion of grpc_slice_refcount::Type. This describes if a
+// slice ref is used by a static metadata slice, an interned slice, or other
+// slices. We switch on the slice ref type in order to provide fastpaths for
+// Equals() and Hash().
+//
+// In total, this saves us roughly 1-2% latency for unary calls, with smaller
+// calls benefitting. The effect is present, but not as useful, for larger calls
+// where the cost of sending the data dominates.
+// TODO(arjunroy): Investigate if this can be removed with strongly typed
+// grpc_slices.
struct grpc_slice_refcount {
public:
- typedef void (*DestroyerFn)(grpc_slice_refcount*);
-
- static grpc_slice_refcount* NoopRefcount() {
- return reinterpret_cast(1);
- }
+ enum class Type {
+ INTERNED, // Refcount for an interned slice.
+ NOP, // No-Op
+ REGULAR // Refcount for non-static-metadata, non-interned slices.
+ };
+ typedef void (*DestroyerFn)(void*);
grpc_slice_refcount() = default;
+ explicit grpc_slice_refcount(Type t) : ref_type_(t) {}
+
+ explicit grpc_slice_refcount(grpc_slice_refcount* sub) : sub_refcount_(sub) {}
// Regular constructor for grpc_slice_refcount.
//
// Parameters:
- // 1. DestroyerFn destroyer_fn
- // Called when the refcount goes to 0, with 'this' as parameter.
- explicit grpc_slice_refcount(DestroyerFn destroyer_fn)
- : destroyer_fn_(destroyer_fn) {}
+ // 1. grpc_slice_refcount::Type type
+ // Whether we are the refcount for a static
+ // metadata slice, an interned slice, or any other kind of slice.
+ //
+ // 2. std::atomic* ref
+ // The pointer to the actual underlying grpc_core::RefCount.
+ // TODO(ctiller): remove the pointer indirection and just put the refcount on
+ // this object once we remove interning.
+ //
+ // 3. DestroyerFn destroyer_fn
+ // Called when the refcount goes to 0, with destroyer_arg as parameter.
+ //
+ // 4. void* destroyer_arg
+ // Argument for the virtualized destructor.
+ //
+ // 5. grpc_slice_refcount* sub
+ // Argument used for interned slices.
+ grpc_slice_refcount(grpc_slice_refcount::Type type, std::atomic* ref,
+ DestroyerFn destroyer_fn, void* destroyer_arg,
+ grpc_slice_refcount* sub)
+ : ref_(ref),
+ ref_type_(type),
+ sub_refcount_(sub),
+ dest_fn_(destroyer_fn),
+ destroy_fn_arg_(destroyer_arg) {}
+ // Initializer for static refcounts.
+ grpc_slice_refcount(grpc_slice_refcount* sub, Type type)
+ : ref_type_(type), sub_refcount_(sub) {}
- void Ref() { ref_.fetch_add(1, std::memory_order_relaxed); }
+ Type GetType() const { return ref_type_; }
+
+ int Eq(const grpc_slice& a, const grpc_slice& b);
+
+ uint32_t Hash(const grpc_slice& slice);
+ void Ref() {
+ if (ref_ == nullptr) return;
+ ref_->fetch_add(1, std::memory_order_relaxed);
+ }
void Unref() {
- if (ref_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
- destroyer_fn_(this);
+ if (ref_ == nullptr) return;
+ if (ref_->fetch_sub(1, std::memory_order_acq_rel) == 1) {
+ dest_fn_(destroy_fn_arg_);
}
}
- // Is this the only instance?
+ // Only for type REGULAR, is this the only instance?
// For this to be useful the caller needs to ensure that if this is the only
// instance, no other instance could be created during this call.
- bool IsUnique() const { return ref_.load(std::memory_order_relaxed) == 1; }
+ bool IsRegularUnique() {
+ GPR_DEBUG_ASSERT(ref_type_ == Type::REGULAR);
+ return ref_->load(std::memory_order_relaxed) == 1;
+ }
+
+ grpc_slice_refcount* sub_refcount() const { return sub_refcount_; }
private:
- std::atomic ref_{1};
- DestroyerFn destroyer_fn_ = nullptr;
+ std::atomic* ref_ = nullptr;
+ const Type ref_type_ = Type::REGULAR;
+ grpc_slice_refcount* sub_refcount_ = this;
+ DestroyerFn dest_fn_ = nullptr;
+ void* destroy_fn_arg_ = nullptr;
};
#endif // GRPC_CORE_LIB_SLICE_SLICE_REFCOUNT_BASE_H
diff --git a/src/core/lib/slice/slice_utils.h b/src/core/lib/slice/slice_utils.h
new file mode 100644
index 00000000000..d9a4253c1aa
--- /dev/null
+++ b/src/core/lib/slice/slice_utils.h
@@ -0,0 +1,200 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SLICE_SLICE_UTILS_H
+#define GRPC_CORE_LIB_SLICE_SLICE_UTILS_H
+
+#include
+
+#include
+
+#include "absl/strings/string_view.h"
+
+#include
+
+#include "src/core/lib/gpr/murmur_hash.h"
+
+namespace grpc_core {
+extern uint32_t g_hash_seed;
+} // namespace grpc_core
+
+// When we compare two slices, and we know the latter is not inlined, we can
+// short circuit our comparison operator. We specifically use differs()
+// semantics instead of equals() semantics due to more favourable code
+// generation when using differs(). Specifically, we may use the output of
+// grpc_slice_differs_refcounted for control flow. If we use differs()
+// semantics, we end with a tailcall to memcmp(). If we use equals() semantics,
+// we need to invert the result that memcmp provides us, which costs several
+// instructions to do so. If we're using the result for control flow (i.e.
+// branching based on the output) then we're just performing the extra
+// operations to invert the result pointlessly. Concretely, we save 6 ops on
+// x86-64/clang with differs().
+int grpc_slice_differs_refcounted(const grpc_slice& a,
+ const grpc_slice& b_not_inline);
+
+// When we compare two slices, and we *know* that one of them is static or
+// interned, we can short circuit our slice equality function. The second slice
+// here must be static or interned; slice a can be any slice, inlined or not.
+inline bool grpc_slice_eq_static_interned(const grpc_slice& a,
+ const grpc_slice& b_static_interned) {
+ if (a.refcount == b_static_interned.refcount) {
+ return true;
+ }
+ return !grpc_slice_differs_refcounted(a, b_static_interned);
+}
+
+// TODO(arjunroy): These type declarations ought to be in
+// src/core/lib/slice/slice_internal.h instead; they are here due to a circular
+// header depedency between slice_internal.h and
+// src/core/lib/transport/metadata.h. We need to fix this circular reference and
+// when we do, move these type declarations.
+//
+// Internal slice type declarations.
+// Externally, a grpc_slice is a grpc_slice is a grpc_slice.
+// Internally, we may have heap allocated slices, static slices, interned
+// slices, and inlined slices. If we know the specific type of slice
+// we're dealing with, we can save cycles (e.g. fast-paths when we know we don't
+// need to take a reference on a slice). Rather than introducing new methods
+// ad-hoc in these cases, we rely on type-system backed overloads to keep
+// internal APIs clean.
+//
+// For each overload, the definition and layout of the underlying slice does not
+// change; this is purely type-system information.
+namespace grpc_core {
+
+// There are two main types of slices: those that have their memory
+// managed by the slice library and those that do not.
+//
+// The following types of slices are not managed:
+// - inlined slices (i.e., refcount is null)
+// - slices that have a custom refcount type (i.e., not STATIC or INTERNED)
+// - slices where the memory is managed by some external agent. The slice is not
+// ref-counted by grpc, and the programmer is responsible for ensuring the
+// data is valid for the duration of the period that grpc may access it.
+//
+// The following types of slices are managed:
+// - static metadata slices (i.e., refcount type is STATIC)
+// - interned slices (i.e., refcount type is INTERNED)
+//
+// This categorization is reflected in the following hierarchy:
+//
+// - grpc_slice
+// > - UnmanagedMemorySlice
+// > - ExternallyManagedSlice
+// - ManagedMemorySlice
+// > - InternedSlice
+// - StaticMetadataSlice
+//
+struct ManagedMemorySlice : public grpc_slice {
+ ManagedMemorySlice() {
+ refcount = nullptr;
+ data.refcounted.bytes = nullptr;
+ data.refcounted.length = 0;
+ }
+ explicit ManagedMemorySlice(const char* string);
+ ManagedMemorySlice(const char* buf, size_t len);
+ explicit ManagedMemorySlice(const grpc_slice* slice);
+ bool operator==(const grpc_slice& other) const {
+ if (refcount == other.refcount) {
+ return true;
+ }
+ return !grpc_slice_differs_refcounted(other, *this);
+ }
+ bool operator!=(const grpc_slice& other) const { return !(*this == other); }
+ bool operator==(std::pair buflen) const {
+ return data.refcounted.length == buflen.second && buflen.first != nullptr &&
+ memcmp(buflen.first, data.refcounted.bytes, buflen.second) == 0;
+ }
+};
+struct UnmanagedMemorySlice : public grpc_slice {
+ // TODO(arjunroy): Can we use a default=false param instead of this enum?
+ enum class ForceHeapAllocation {};
+ UnmanagedMemorySlice() {
+ refcount = nullptr;
+ data.inlined.length = 0;
+ }
+ explicit UnmanagedMemorySlice(const char* source);
+ UnmanagedMemorySlice(const char* source, size_t length);
+ // The first constructor creates a slice that may be heap allocated, or
+ // inlined in the slice structure if length is small enough
+ // (< GRPC_SLICE_INLINED_SIZE). The second constructor forces heap alloc.
+ explicit UnmanagedMemorySlice(size_t length);
+ explicit UnmanagedMemorySlice(size_t length, const ForceHeapAllocation&) {
+ HeapInit(length);
+ }
+
+ private:
+ void HeapInit(size_t length);
+};
+
+extern grpc_slice_refcount kNoopRefcount;
+
+struct ExternallyManagedSlice : public UnmanagedMemorySlice {
+ ExternallyManagedSlice()
+ : ExternallyManagedSlice(&kNoopRefcount, 0, nullptr) {}
+ explicit ExternallyManagedSlice(const char* s)
+ : ExternallyManagedSlice(s, strlen(s)) {}
+ ExternallyManagedSlice(const void* s, size_t len)
+ : ExternallyManagedSlice(
+ &kNoopRefcount, len,
+ reinterpret_cast(const_cast(s))) {}
+ ExternallyManagedSlice(grpc_slice_refcount* ref, size_t length,
+ uint8_t* bytes) {
+ refcount = ref;
+ data.refcounted.length = length;
+ data.refcounted.bytes = bytes;
+ }
+ bool operator==(const grpc_slice& other) const {
+ return data.refcounted.length == GRPC_SLICE_LENGTH(other) &&
+ memcmp(data.refcounted.bytes, GRPC_SLICE_START_PTR(other),
+ data.refcounted.length) == 0;
+ }
+ bool operator!=(const grpc_slice& other) const { return !(*this == other); }
+ uint32_t Hash() {
+ return gpr_murmur_hash3(data.refcounted.bytes, data.refcounted.length,
+ g_hash_seed);
+ }
+};
+
+struct StaticMetadataSlice : public ManagedMemorySlice {
+ StaticMetadataSlice(grpc_slice_refcount* ref, size_t length,
+ const uint8_t* bytes) {
+ refcount = ref;
+ data.refcounted.length = length;
+ // NB: grpc_slice may or may not point to a static slice, but we are
+ // definitely pointing to static data here. Since we are not changing
+ // the underlying C-type, we need a const_cast here.
+ data.refcounted.bytes = const_cast(bytes);
+ }
+};
+
+struct InternedSliceRefcount;
+struct InternedSlice : public ManagedMemorySlice {
+ explicit InternedSlice(InternedSliceRefcount* s);
+};
+
+// Converts grpc_slice to absl::string_view.
+inline absl::string_view StringViewFromSlice(const grpc_slice& slice) {
+ return absl::string_view(
+ reinterpret_cast(GRPC_SLICE_START_PTR(slice)),
+ GRPC_SLICE_LENGTH(slice));
+}
+
+} // namespace grpc_core
+
+#endif /* GRPC_CORE_LIB_SLICE_SLICE_UTILS_H */
diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc
index 06b517fab4c..52e5a161f90 100644
--- a/src/core/lib/surface/call.cc
+++ b/src/core/lib/surface/call.cc
@@ -50,9 +50,9 @@
#include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/profiling/timers.h"
#include "src/core/lib/resource_quota/arena.h"
-#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/slice/slice_split.h"
#include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/slice/slice_utils.h"
#include "src/core/lib/surface/api_trace.h"
#include "src/core/lib/surface/call_test_only.h"
#include "src/core/lib/surface/channel.h"
diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc
index 191ef4f0610..9c0a588f880 100644
--- a/src/core/lib/surface/init.cc
+++ b/src/core/lib/surface/init.cc
@@ -104,6 +104,7 @@ void grpc_init(void) {
grpc_core::Fork::GlobalInit();
grpc_fork_handlers_auto_register();
grpc_stats_init();
+ grpc_slice_intern_init();
grpc_core::channelz::ChannelzRegistry::Init();
grpc_security_pre_init();
grpc_core::ApplicationCallbackExecCtx::GlobalInit();
@@ -139,6 +140,7 @@ void grpc_shutdown_internal_locked(void)
grpc_iomgr_shutdown();
gpr_timers_global_destroy();
grpc_tracer_shutdown();
+ grpc_slice_intern_shutdown();
grpc_core::channelz::ChannelzRegistry::Shutdown();
grpc_stats_shutdown();
grpc_core::Fork::GlobalShutdown();
diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc
index da38f98900a..9c4c669e413 100644
--- a/src/core/lib/surface/server.cc
+++ b/src/core/lib/surface/server.cc
@@ -982,7 +982,19 @@ class Server::ChannelData::ConnectivityWatcher
//
Server::ChannelData::~ChannelData() {
- registered_methods_.reset();
+ if (registered_methods_ != nullptr) {
+ for (const ChannelRegisteredMethod& crm : *registered_methods_) {
+ grpc_slice_unref_internal(crm.method);
+ GPR_DEBUG_ASSERT(crm.method.refcount == &kNoopRefcount ||
+ crm.method.refcount == nullptr);
+ if (crm.has_host) {
+ grpc_slice_unref_internal(crm.host);
+ GPR_DEBUG_ASSERT(crm.host.refcount == &kNoopRefcount ||
+ crm.host.refcount == nullptr);
+ }
+ }
+ registered_methods_.reset();
+ }
if (server_ != nullptr) {
if (server_->channelz_node_ != nullptr && channelz_socket_uuid_ != 0) {
server_->channelz_node_->RemoveChildSocket(channelz_socket_uuid_);
@@ -1015,11 +1027,11 @@ void Server::ChannelData::InitTransport(RefCountedPtr server,
registered_methods_ =
absl::make_unique>(slots);
for (std::unique_ptr& rm : server_->registered_methods_) {
- Slice host;
- Slice method = Slice::FromExternalString(rm->method);
+ ExternallyManagedSlice host;
+ ExternallyManagedSlice method(rm->method.c_str());
const bool has_host = !rm->host.empty();
if (has_host) {
- host = Slice::FromExternalString(rm->host.c_str());
+ host = ExternallyManagedSlice(rm->host.c_str());
}
uint32_t hash = MixHash32(has_host ? host.Hash() : 0, method.Hash());
uint32_t probes = 0;
@@ -1034,9 +1046,9 @@ void Server::ChannelData::InitTransport(RefCountedPtr server,
crm->flags = rm->flags;
crm->has_host = has_host;
if (has_host) {
- crm->host = std::move(host);
+ crm->host = host;
}
- crm->method = std::move(method);
+ crm->method = method;
}
GPR_ASSERT(slots <= UINT32_MAX);
registered_method_max_probes_ = max_probes;
diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h
index 276d917fc06..4e83c3ef45c 100644
--- a/src/core/lib/surface/server.h
+++ b/src/core/lib/surface/server.h
@@ -176,8 +176,8 @@ class Server : public InternallyRefCounted,
RegisteredMethod* server_registered_method = nullptr;
uint32_t flags;
bool has_host;
- Slice method;
- Slice host;
+ ExternallyManagedSlice method;
+ ExternallyManagedSlice host;
};
class RequestMatcherInterface;
diff --git a/src/cpp/ext/filters/census/client_filter.cc b/src/cpp/ext/filters/census/client_filter.cc
index 16438b3f3cd..e1e26d19f72 100644
--- a/src/cpp/ext/filters/census/client_filter.cc
+++ b/src/cpp/ext/filters/census/client_filter.cc
@@ -100,9 +100,9 @@ void OpenCensusCallTracer::OpenCensusCallAttemptTracer::
size_t tracing_len = TraceContextSerialize(context_.Context(), tracing_buf,
kMaxTraceContextLen);
if (tracing_len > 0) {
- send_initial_metadata->Set(
- grpc_core::GrpcTraceBinMetadata(),
- grpc_core::Slice::FromCopiedBuffer(tracing_buf, tracing_len));
+ send_initial_metadata->Set(grpc_core::GrpcTraceBinMetadata(),
+ grpc_core::Slice(grpc_core::UnmanagedMemorySlice(
+ tracing_buf, tracing_len)));
}
grpc_slice tags = grpc_empty_slice();
// TODO(unknown): Add in tagging serialization.
diff --git a/src/cpp/ext/filters/census/server_filter.cc b/src/cpp/ext/filters/census/server_filter.cc
index 011cd420755..2653c552952 100644
--- a/src/cpp/ext/filters/census/server_filter.cc
+++ b/src/cpp/ext/filters/census/server_filter.cc
@@ -128,7 +128,7 @@ void CensusServerCallData::StartTransportStreamOpBatch(
if (len > 0) {
op->send_trailing_metadata()->batch()->Set(
grpc_core::GrpcServerStatsBinMetadata(),
- grpc_core::Slice::FromCopiedBuffer(stats_buf_, len));
+ grpc_core::Slice(grpc_core::UnmanagedMemorySlice(stats_buf_, len)));
}
}
// Call next op.
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index cec32a17134..70ad5dc6c4f 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -603,6 +603,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/slice/slice.cc',
'src/core/lib/slice/slice_api.cc',
'src/core/lib/slice/slice_buffer.cc',
+ 'src/core/lib/slice/slice_intern.cc',
'src/core/lib/slice/slice_refcount.cc',
'src/core/lib/slice/slice_split.cc',
'src/core/lib/slice/slice_string_helpers.cc',
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index 45b475aaea0..1f2e3748e74 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -212,6 +212,7 @@ grpc_slice_new_with_user_data_type grpc_slice_new_with_user_data_import;
grpc_slice_new_with_len_type grpc_slice_new_with_len_import;
grpc_slice_malloc_type grpc_slice_malloc_import;
grpc_slice_malloc_large_type grpc_slice_malloc_large_import;
+grpc_slice_intern_type grpc_slice_intern_import;
grpc_slice_from_copied_string_type grpc_slice_from_copied_string_import;
grpc_slice_from_copied_buffer_type grpc_slice_from_copied_buffer_import;
grpc_slice_from_static_string_type grpc_slice_from_static_string_import;
@@ -222,6 +223,8 @@ grpc_slice_split_tail_type grpc_slice_split_tail_import;
grpc_slice_split_tail_maybe_ref_type grpc_slice_split_tail_maybe_ref_import;
grpc_slice_split_head_type grpc_slice_split_head_import;
grpc_empty_slice_type grpc_empty_slice_import;
+grpc_slice_default_hash_impl_type grpc_slice_default_hash_impl_import;
+grpc_slice_default_eq_impl_type grpc_slice_default_eq_impl_import;
grpc_slice_eq_type grpc_slice_eq_import;
grpc_slice_cmp_type grpc_slice_cmp_import;
grpc_slice_str_cmp_type grpc_slice_str_cmp_import;
@@ -229,6 +232,7 @@ grpc_slice_buf_start_eq_type grpc_slice_buf_start_eq_import;
grpc_slice_rchr_type grpc_slice_rchr_import;
grpc_slice_chr_type grpc_slice_chr_import;
grpc_slice_slice_type grpc_slice_slice_import;
+grpc_slice_hash_type grpc_slice_hash_import;
grpc_slice_is_equivalent_type grpc_slice_is_equivalent_import;
grpc_slice_dup_type grpc_slice_dup_import;
grpc_slice_to_c_string_type grpc_slice_to_c_string_import;
@@ -497,6 +501,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_slice_new_with_len_import = (grpc_slice_new_with_len_type) GetProcAddress(library, "grpc_slice_new_with_len");
grpc_slice_malloc_import = (grpc_slice_malloc_type) GetProcAddress(library, "grpc_slice_malloc");
grpc_slice_malloc_large_import = (grpc_slice_malloc_large_type) GetProcAddress(library, "grpc_slice_malloc_large");
+ grpc_slice_intern_import = (grpc_slice_intern_type) GetProcAddress(library, "grpc_slice_intern");
grpc_slice_from_copied_string_import = (grpc_slice_from_copied_string_type) GetProcAddress(library, "grpc_slice_from_copied_string");
grpc_slice_from_copied_buffer_import = (grpc_slice_from_copied_buffer_type) GetProcAddress(library, "grpc_slice_from_copied_buffer");
grpc_slice_from_static_string_import = (grpc_slice_from_static_string_type) GetProcAddress(library, "grpc_slice_from_static_string");
@@ -507,6 +512,8 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_slice_split_tail_maybe_ref_import = (grpc_slice_split_tail_maybe_ref_type) GetProcAddress(library, "grpc_slice_split_tail_maybe_ref");
grpc_slice_split_head_import = (grpc_slice_split_head_type) GetProcAddress(library, "grpc_slice_split_head");
grpc_empty_slice_import = (grpc_empty_slice_type) GetProcAddress(library, "grpc_empty_slice");
+ grpc_slice_default_hash_impl_import = (grpc_slice_default_hash_impl_type) GetProcAddress(library, "grpc_slice_default_hash_impl");
+ grpc_slice_default_eq_impl_import = (grpc_slice_default_eq_impl_type) GetProcAddress(library, "grpc_slice_default_eq_impl");
grpc_slice_eq_import = (grpc_slice_eq_type) GetProcAddress(library, "grpc_slice_eq");
grpc_slice_cmp_import = (grpc_slice_cmp_type) GetProcAddress(library, "grpc_slice_cmp");
grpc_slice_str_cmp_import = (grpc_slice_str_cmp_type) GetProcAddress(library, "grpc_slice_str_cmp");
@@ -514,6 +521,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_slice_rchr_import = (grpc_slice_rchr_type) GetProcAddress(library, "grpc_slice_rchr");
grpc_slice_chr_import = (grpc_slice_chr_type) GetProcAddress(library, "grpc_slice_chr");
grpc_slice_slice_import = (grpc_slice_slice_type) GetProcAddress(library, "grpc_slice_slice");
+ grpc_slice_hash_import = (grpc_slice_hash_type) GetProcAddress(library, "grpc_slice_hash");
grpc_slice_is_equivalent_import = (grpc_slice_is_equivalent_type) GetProcAddress(library, "grpc_slice_is_equivalent");
grpc_slice_dup_import = (grpc_slice_dup_type) GetProcAddress(library, "grpc_slice_dup");
grpc_slice_to_c_string_import = (grpc_slice_to_c_string_type) GetProcAddress(library, "grpc_slice_to_c_string");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index 02619469444..ba60c8aff35 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -611,6 +611,9 @@ extern grpc_slice_malloc_type grpc_slice_malloc_import;
typedef grpc_slice(*grpc_slice_malloc_large_type)(size_t length);
extern grpc_slice_malloc_large_type grpc_slice_malloc_large_import;
#define grpc_slice_malloc_large grpc_slice_malloc_large_import
+typedef grpc_slice(*grpc_slice_intern_type)(grpc_slice slice);
+extern grpc_slice_intern_type grpc_slice_intern_import;
+#define grpc_slice_intern grpc_slice_intern_import
typedef grpc_slice(*grpc_slice_from_copied_string_type)(const char* source);
extern grpc_slice_from_copied_string_type grpc_slice_from_copied_string_import;
#define grpc_slice_from_copied_string grpc_slice_from_copied_string_import
@@ -641,6 +644,12 @@ extern grpc_slice_split_head_type grpc_slice_split_head_import;
typedef grpc_slice(*grpc_empty_slice_type)(void);
extern grpc_empty_slice_type grpc_empty_slice_import;
#define grpc_empty_slice grpc_empty_slice_import
+typedef uint32_t(*grpc_slice_default_hash_impl_type)(grpc_slice s);
+extern grpc_slice_default_hash_impl_type grpc_slice_default_hash_impl_import;
+#define grpc_slice_default_hash_impl grpc_slice_default_hash_impl_import
+typedef int(*grpc_slice_default_eq_impl_type)(grpc_slice a, grpc_slice b);
+extern grpc_slice_default_eq_impl_type grpc_slice_default_eq_impl_import;
+#define grpc_slice_default_eq_impl grpc_slice_default_eq_impl_import
typedef int(*grpc_slice_eq_type)(grpc_slice a, grpc_slice b);
extern grpc_slice_eq_type grpc_slice_eq_import;
#define grpc_slice_eq grpc_slice_eq_import
@@ -662,6 +671,9 @@ extern grpc_slice_chr_type grpc_slice_chr_import;
typedef int(*grpc_slice_slice_type)(grpc_slice haystack, grpc_slice needle);
extern grpc_slice_slice_type grpc_slice_slice_import;
#define grpc_slice_slice grpc_slice_slice_import
+typedef uint32_t(*grpc_slice_hash_type)(grpc_slice s);
+extern grpc_slice_hash_type grpc_slice_hash_import;
+#define grpc_slice_hash grpc_slice_hash_import
typedef int(*grpc_slice_is_equivalent_type)(grpc_slice a, grpc_slice b);
extern grpc_slice_is_equivalent_type grpc_slice_is_equivalent_import;
#define grpc_slice_is_equivalent grpc_slice_is_equivalent_import
diff --git a/test/core/end2end/fuzzers/api_fuzzer.cc b/test/core/end2end/fuzzers/api_fuzzer.cc
index e1c357596f1..2d83fdda98d 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.cc
+++ b/test/core/end2end/fuzzers/api_fuzzer.cc
@@ -381,6 +381,11 @@ class Call : public std::enable_shared_from_this {
template
grpc_slice ReadSlice(const T& s) {
grpc_slice slice = grpc_slice_from_cpp_string(s.value());
+ if (s.intern()) {
+ auto interned_slice = grpc_slice_intern(slice);
+ grpc_slice_unref(slice);
+ slice = interned_slice;
+ }
unref_slices_.push_back(slice);
return slice;
}
diff --git a/test/core/end2end/fuzzers/api_fuzzer.proto b/test/core/end2end/fuzzers/api_fuzzer.proto
index eab3551cb01..af867c3a931 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.proto
+++ b/test/core/end2end/fuzzers/api_fuzzer.proto
@@ -19,15 +19,13 @@ package api_fuzzer;
message Empty {}
message ByteSlice {
- reserved 2;
- reserved "intern";
bytes value = 1;
+ bool intern = 2;
}
message StringSlice {
- reserved 2;
- reserved "intern";
string value = 1;
+ bool intern = 2;
}
message ResourceQuota {}
diff --git a/test/core/slice/BUILD b/test/core/slice/BUILD
index a51b697deed..192443a4a29 100644
--- a/test/core/slice/BUILD
+++ b/test/core/slice/BUILD
@@ -95,6 +95,18 @@ grpc_cc_test(
],
)
+grpc_cc_test(
+ name = "slice_intern_test",
+ srcs = ["slice_intern_test.cc"],
+ language = "C++",
+ uses_polling = False,
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//test/core/util:grpc_test_util",
+ ],
+)
+
grpc_cc_test(
name = "slice_string_helpers_test",
srcs = ["slice_string_helpers_test.cc"],
diff --git a/test/core/slice/slice_intern_test.cc b/test/core/slice/slice_intern_test.cc
new file mode 100644
index 00000000000..47e79c62d96
--- /dev/null
+++ b/test/core/slice/slice_intern_test.cc
@@ -0,0 +1,67 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST_NAME(x) gpr_log(GPR_INFO, "%s", x);
+
+static void test_slice_interning(void) {
+ LOG_TEST_NAME("test_slice_interning");
+
+ grpc_init();
+ grpc_slice src1 = grpc_slice_from_copied_string("hello123456789123456789");
+ grpc_slice src2 = grpc_slice_from_copied_string("hello123456789123456789");
+
+ // Explicitly checking that the slices are at different addresses prevents
+ // failure with windows opt 64bit build.
+ // See https://github.com/grpc/grpc/issues/20519
+ GPR_ASSERT(&src1 != &src2);
+ GPR_ASSERT(GRPC_SLICE_START_PTR(src1) != GRPC_SLICE_START_PTR(src2));
+
+ grpc_slice interned1 = grpc_slice_intern(src1);
+ grpc_slice interned2 = grpc_slice_intern(src2);
+ GPR_ASSERT(GRPC_SLICE_START_PTR(interned1) ==
+ GRPC_SLICE_START_PTR(interned2));
+ GPR_ASSERT(GRPC_SLICE_START_PTR(interned1) != GRPC_SLICE_START_PTR(src1));
+ GPR_ASSERT(GRPC_SLICE_START_PTR(interned2) != GRPC_SLICE_START_PTR(src2));
+ grpc_slice_unref(src1);
+ grpc_slice_unref(src2);
+ grpc_slice_unref(interned1);
+ grpc_slice_unref(interned2);
+ grpc_shutdown();
+}
+
+int main(int argc, char** argv) {
+ grpc::testing::TestEnvironment env(argc, argv);
+ grpc_init();
+ test_slice_interning();
+ grpc_shutdown();
+ return 0;
+}
diff --git a/test/core/slice/slice_test.cc b/test/core/slice/slice_test.cc
index 1bf2de65d12..1f5ea505054 100644
--- a/test/core/slice/slice_test.cc
+++ b/test/core/slice/slice_test.cc
@@ -358,7 +358,8 @@ size_t SumSlice(const Slice& slice) {
TEST(SliceTest, ExternalAsOwned) {
auto external_string = absl::make_unique(RandomString(1024));
- Slice slice = Slice::FromExternalString(*external_string);
+ Slice slice(ExternallyManagedSlice(external_string->data(),
+ external_string->length()));
const auto initial_sum = SumSlice(slice);
Slice owned = slice.AsOwned();
EXPECT_EQ(initial_sum, SumSlice(owned));
@@ -374,7 +375,9 @@ TEST(SliceTest, ExternalAsOwned) {
TEST(SliceTest, ExternalTakeOwned) {
std::unique_ptr external_string(
new std::string(RandomString(1024)));
- SumSlice(Slice::FromExternalString(*external_string).TakeOwned());
+ SumSlice(Slice(ExternallyManagedSlice(external_string->data(),
+ external_string->length()))
+ .TakeOwned());
}
TEST(SliceTest, StaticSlice) {
diff --git a/test/core/surface/public_headers_must_be_c89.c b/test/core/surface/public_headers_must_be_c89.c
index 35af6c516e7..96ebc19c460 100644
--- a/test/core/surface/public_headers_must_be_c89.c
+++ b/test/core/surface/public_headers_must_be_c89.c
@@ -256,6 +256,7 @@ int main(int argc, char **argv) {
printf("%lx", (unsigned long) grpc_slice_new_with_len);
printf("%lx", (unsigned long) grpc_slice_malloc);
printf("%lx", (unsigned long) grpc_slice_malloc_large);
+ printf("%lx", (unsigned long) grpc_slice_intern);
printf("%lx", (unsigned long) grpc_slice_from_copied_string);
printf("%lx", (unsigned long) grpc_slice_from_copied_buffer);
printf("%lx", (unsigned long) grpc_slice_from_static_string);
@@ -266,6 +267,8 @@ int main(int argc, char **argv) {
printf("%lx", (unsigned long) grpc_slice_split_tail_maybe_ref);
printf("%lx", (unsigned long) grpc_slice_split_head);
printf("%lx", (unsigned long) grpc_empty_slice);
+ printf("%lx", (unsigned long) grpc_slice_default_hash_impl);
+ printf("%lx", (unsigned long) grpc_slice_default_eq_impl);
printf("%lx", (unsigned long) grpc_slice_eq);
printf("%lx", (unsigned long) grpc_slice_cmp);
printf("%lx", (unsigned long) grpc_slice_str_cmp);
@@ -273,6 +276,7 @@ int main(int argc, char **argv) {
printf("%lx", (unsigned long) grpc_slice_rchr);
printf("%lx", (unsigned long) grpc_slice_chr);
printf("%lx", (unsigned long) grpc_slice_slice);
+ printf("%lx", (unsigned long) grpc_slice_hash);
printf("%lx", (unsigned long) grpc_slice_is_equivalent);
printf("%lx", (unsigned long) grpc_slice_dup);
printf("%lx", (unsigned long) grpc_slice_to_c_string);
diff --git a/test/core/transport/chttp2/hpack_encoder_test.cc b/test/core/transport/chttp2/hpack_encoder_test.cc
index c49c4372326..9d11f41a7bf 100644
--- a/test/core/transport/chttp2/hpack_encoder_test.cc
+++ b/test/core/transport/chttp2/hpack_encoder_test.cc
@@ -50,6 +50,7 @@ int g_failure = 0;
typedef struct {
bool eof;
bool use_true_binary_metadata;
+ bool only_intern_key;
} verify_params;
/* verify that the output frames that are generated by encoding the stream
@@ -168,8 +169,11 @@ static void verify(const verify_params params, const char* expected,
for (i = 0; i < nheaders; i++) {
char* key = va_arg(l, char*);
char* value = va_arg(l, char*);
- b.Append(key, grpc_core::Slice::FromStaticString(value),
- CrashOnAppendError);
+ grpc_slice value_slice = grpc_slice_from_static_string(value);
+ if (!params.only_intern_key) {
+ value_slice = grpc_slice_intern(value_slice);
+ }
+ b.Append(key, grpc_core::Slice(value_slice), CrashOnAppendError);
}
va_end(l);
@@ -208,6 +212,7 @@ static void test_basic_headers() {
verify_params params = {
false,
false,
+ false,
};
verify(params, "000005 0104 deadbeef 00 0161 0161", 1, "a", "a");
verify(params, "00000a 0104 deadbeef 00 0161 0161 00 0162 0163", 2, "a", "a",
diff --git a/test/core/util/evaluate_args_test_util.h b/test/core/util/evaluate_args_test_util.h
index 6beadf56860..27f5b445f63 100644
--- a/test/core/util/evaluate_args_test_util.h
+++ b/test/core/util/evaluate_args_test_util.h
@@ -33,11 +33,12 @@ class EvaluateArgsTestUtil {
~EvaluateArgsTestUtil() { delete channel_args_; }
void AddPairToMetadata(const char* key, const char* value) {
- metadata_.Append(key, Slice::FromStaticString(value),
- [](absl::string_view, const Slice&) {
- // We should never ever see an error here.
- abort();
- });
+ metadata_.Append(
+ key, Slice(grpc_slice_intern(grpc_slice_from_static_string(value))),
+ [](absl::string_view, const Slice&) {
+ // We should never ever see an error here.
+ abort();
+ });
}
void SetLocalEndpoint(absl::string_view local_uri) {
diff --git a/test/cpp/end2end/channelz_service_test.cc b/test/cpp/end2end/channelz_service_test.cc
index 027a08c9bfe..1f9423565bf 100644
--- a/test/cpp/end2end/channelz_service_test.cc
+++ b/test/cpp/end2end/channelz_service_test.cc
@@ -38,7 +38,7 @@
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
-#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/slice/slice_utils.h"
#include "src/cpp/client/secure_credentials.h"
#include "src/proto/grpc/channelz/channelz.grpc.pb.h"
#include "src/proto/grpc/testing/echo.grpc.pb.h"
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 083a42cda0d..c55f47006c7 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -2225,6 +2225,7 @@ src/core/lib/slice/slice.cc \
src/core/lib/slice/slice.h \
src/core/lib/slice/slice_api.cc \
src/core/lib/slice/slice_buffer.cc \
+src/core/lib/slice/slice_intern.cc \
src/core/lib/slice/slice_internal.h \
src/core/lib/slice/slice_refcount.cc \
src/core/lib/slice/slice_refcount.h \
@@ -2233,6 +2234,7 @@ src/core/lib/slice/slice_split.cc \
src/core/lib/slice/slice_split.h \
src/core/lib/slice/slice_string_helpers.cc \
src/core/lib/slice/slice_string_helpers.h \
+src/core/lib/slice/slice_utils.h \
src/core/lib/surface/api_trace.cc \
src/core/lib/surface/api_trace.h \
src/core/lib/surface/builtins.cc \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index ee5f20c23f7..8ac97d3a429 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -2024,6 +2024,7 @@ src/core/lib/slice/slice.cc \
src/core/lib/slice/slice.h \
src/core/lib/slice/slice_api.cc \
src/core/lib/slice/slice_buffer.cc \
+src/core/lib/slice/slice_intern.cc \
src/core/lib/slice/slice_internal.h \
src/core/lib/slice/slice_refcount.cc \
src/core/lib/slice/slice_refcount.h \
@@ -2032,6 +2033,7 @@ src/core/lib/slice/slice_split.cc \
src/core/lib/slice/slice_split.h \
src/core/lib/slice/slice_string_helpers.cc \
src/core/lib/slice/slice_string_helpers.h \
+src/core/lib/slice/slice_utils.h \
src/core/lib/surface/README.md \
src/core/lib/surface/api_trace.cc \
src/core/lib/surface/api_trace.h \
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 3c6f6115693..b03d7fa57fc 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -2151,6 +2151,30 @@
],
"uses_polling": false
},
+ {
+ "args": [],
+ "benchmark": false,
+ "ci_platforms": [
+ "linux",
+ "mac",
+ "posix",
+ "windows"
+ ],
+ "cpu_cost": 1.0,
+ "exclude_configs": [],
+ "exclude_iomgrs": [],
+ "flaky": false,
+ "gtest": false,
+ "language": "c",
+ "name": "slice_intern_test",
+ "platforms": [
+ "linux",
+ "mac",
+ "posix",
+ "windows"
+ ],
+ "uses_polling": false
+ },
{
"args": [],
"benchmark": false,