mirror of https://github.com/grpc/grpc.git
Merge pull request #19218 from soheilhy/string-view
Introduce string_view and use it for gpr_split_host_port.pull/19228/head
commit
f9516b04d5
117 changed files with 1280 additions and 881 deletions
@ -1,98 +0,0 @@ |
||||
/*
|
||||
* |
||||
* 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/gpr/host_port.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include "src/core/lib/gpr/string.h" |
||||
|
||||
int gpr_join_host_port(char** out, const char* host, int port) { |
||||
if (host[0] != '[' && strchr(host, ':') != nullptr) { |
||||
/* IPv6 literals must be enclosed in brackets. */ |
||||
return gpr_asprintf(out, "[%s]:%d", host, port); |
||||
} else { |
||||
/* Ordinary non-bracketed host:port. */ |
||||
return gpr_asprintf(out, "%s:%d", host, port); |
||||
} |
||||
} |
||||
|
||||
int gpr_split_host_port(const char* name, char** host, char** port) { |
||||
const char* host_start; |
||||
size_t host_len; |
||||
const char* port_start; |
||||
|
||||
*host = nullptr; |
||||
*port = nullptr; |
||||
|
||||
if (name[0] == '[') { |
||||
/* Parse a bracketed host, typically an IPv6 literal. */ |
||||
const char* rbracket = strchr(name, ']'); |
||||
if (rbracket == nullptr) { |
||||
/* Unmatched [ */ |
||||
return 0; |
||||
} |
||||
if (rbracket[1] == '\0') { |
||||
/* ]<end> */ |
||||
port_start = nullptr; |
||||
} else if (rbracket[1] == ':') { |
||||
/* ]:<port?> */ |
||||
port_start = rbracket + 2; |
||||
} else { |
||||
/* ]<invalid> */ |
||||
return 0; |
||||
} |
||||
host_start = name + 1; |
||||
host_len = static_cast<size_t>(rbracket - host_start); |
||||
if (memchr(host_start, ':', host_len) == nullptr) { |
||||
/* Require all bracketed hosts to contain a colon, because a hostname or
|
||||
IPv4 address should never use brackets. */ |
||||
return 0; |
||||
} |
||||
} else { |
||||
const char* colon = strchr(name, ':'); |
||||
if (colon != nullptr && strchr(colon + 1, ':') == nullptr) { |
||||
/* Exactly 1 colon. Split into host:port. */ |
||||
host_start = name; |
||||
host_len = static_cast<size_t>(colon - name); |
||||
port_start = colon + 1; |
||||
} else { |
||||
/* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ |
||||
host_start = name; |
||||
host_len = strlen(name); |
||||
port_start = nullptr; |
||||
} |
||||
} |
||||
|
||||
/* Allocate return values. */ |
||||
*host = static_cast<char*>(gpr_malloc(host_len + 1)); |
||||
memcpy(*host, host_start, host_len); |
||||
(*host)[host_len] = '\0'; |
||||
|
||||
if (port_start != nullptr) { |
||||
*port = gpr_strdup(port_start); |
||||
} |
||||
|
||||
return 1; |
||||
} |
@ -0,0 +1,97 @@ |
||||
/*
|
||||
* |
||||
* 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/gprpp/host_port.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include "src/core/lib/gpr/string.h" |
||||
#include "src/core/lib/gprpp/string_view.h" |
||||
|
||||
namespace grpc_core { |
||||
int JoinHostPort(UniquePtr<char>* out, const char* host, int port) { |
||||
char* tmp; |
||||
int ret; |
||||
if (host[0] != '[' && strchr(host, ':') != nullptr) { |
||||
/* IPv6 literals must be enclosed in brackets. */ |
||||
ret = gpr_asprintf(&tmp, "[%s]:%d", host, port); |
||||
} else { |
||||
/* Ordinary non-bracketed host:port. */ |
||||
ret = gpr_asprintf(&tmp, "%s:%d", host, port); |
||||
} |
||||
out->reset(tmp); |
||||
return ret; |
||||
} |
||||
|
||||
bool SplitHostPort(StringView name, StringView* host, StringView* port) { |
||||
if (name[0] == '[') { |
||||
/* Parse a bracketed host, typically an IPv6 literal. */ |
||||
const size_t rbracket = name.find(']', 1); |
||||
if (rbracket == grpc_core::StringView::npos) { |
||||
/* Unmatched [ */ |
||||
return false; |
||||
} |
||||
if (rbracket == name.size() - 1) { |
||||
/* ]<end> */ |
||||
port->clear(); |
||||
} else if (name[rbracket + 1] == ':') { |
||||
/* ]:<port?> */ |
||||
*port = name.substr(rbracket + 2, name.size() - rbracket - 2); |
||||
} else { |
||||
/* ]<invalid> */ |
||||
return false; |
||||
} |
||||
*host = name.substr(1, rbracket - 1); |
||||
if (host->find(':') == grpc_core::StringView::npos) { |
||||
/* Require all bracketed hosts to contain a colon, because a hostname or
|
||||
IPv4 address should never use brackets. */ |
||||
host->clear(); |
||||
return false; |
||||
} |
||||
} else { |
||||
size_t colon = name.find(':'); |
||||
if (colon != grpc_core::StringView::npos && |
||||
name.find(':', colon + 1) == grpc_core::StringView::npos) { |
||||
/* Exactly 1 colon. Split into host:port. */ |
||||
*host = name.substr(0, colon); |
||||
*port = name.substr(colon + 1, name.size() - colon - 1); |
||||
} else { |
||||
/* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ |
||||
*host = name; |
||||
port->clear(); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool SplitHostPort(StringView name, UniquePtr<char>* host, |
||||
UniquePtr<char>* port) { |
||||
StringView host_view; |
||||
StringView port_view; |
||||
const bool ret = SplitHostPort(name, &host_view, &port_view); |
||||
host->reset(host_view.empty() ? nullptr : host_view.dup().release()); |
||||
port->reset(port_view.empty() ? nullptr : port_view.dup().release()); |
||||
return ret; |
||||
} |
||||
} // namespace grpc_core
|
@ -0,0 +1,143 @@ |
||||
/*
|
||||
* |
||||
* 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_GPRPP_STRING_VIEW_H |
||||
#define GRPC_CORE_LIB_GPRPP_STRING_VIEW_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <grpc/impl/codegen/slice.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
|
||||
#include <algorithm> |
||||
#include <cstdint> |
||||
#include <cstring> |
||||
#include <limits> |
||||
|
||||
#include "src/core/lib/gpr/string.h" |
||||
#include "src/core/lib/gpr/useful.h" |
||||
#include "src/core/lib/gprpp/memory.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Provides a light-weight view over a char array or a slice, similar but not
|
||||
// identical to absl::string_view.
|
||||
//
|
||||
// Any method that has the same name as absl::string_view MUST HAVE identical
|
||||
// semantics to what absl::string_view provides.
|
||||
//
|
||||
// Methods that are not part of absl::string_view API, must be clearly
|
||||
// annotated.
|
||||
//
|
||||
// StringView does not own the buffers that back the view. Callers must ensure
|
||||
// the buffer stays around while the StringView is accessible.
|
||||
//
|
||||
// Pass StringView by value in functions, since it is exactly two pointers in
|
||||
// size.
|
||||
//
|
||||
// The interface used here is not identical to absl::string_view. Notably, we
|
||||
// need to support slices while we cannot support std::string, and gpr string
|
||||
// style functions such as strdup() and cmp(). Once we switch to
|
||||
// absl::string_view this class will inherit from absl::string_view and add the
|
||||
// gRPC-specific APIs.
|
||||
class StringView final { |
||||
public: |
||||
static constexpr size_t npos = std::numeric_limits<size_t>::max(); |
||||
|
||||
constexpr StringView(const char* ptr, size_t size) : ptr_(ptr), size_(size) {} |
||||
constexpr StringView(const char* ptr) |
||||
: StringView(ptr, ptr == nullptr ? 0 : strlen(ptr)) {} |
||||
// Not part of absl::string_view API.
|
||||
StringView(const grpc_slice& slice) |
||||
: StringView(reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(slice)), |
||||
GRPC_SLICE_LENGTH(slice)) {} |
||||
constexpr StringView() : StringView(nullptr, 0) {} |
||||
|
||||
constexpr const char* data() const { return ptr_; } |
||||
constexpr size_t size() const { return size_; } |
||||
constexpr bool empty() const { return size_ == 0; } |
||||
|
||||
StringView substr(size_t start, size_t size = npos) { |
||||
GPR_DEBUG_ASSERT(start + size <= size_); |
||||
return StringView(ptr_ + start, std::min(size, size_ - start)); |
||||
} |
||||
|
||||
constexpr const char& operator[](size_t i) const { return ptr_[i]; } |
||||
|
||||
const char& front() const { return ptr_[0]; } |
||||
const char& back() const { return ptr_[size_ - 1]; } |
||||
|
||||
void remove_prefix(size_t n) { |
||||
GPR_DEBUG_ASSERT(n <= size_); |
||||
ptr_ += n; |
||||
size_ -= n; |
||||
} |
||||
|
||||
void remove_suffix(size_t n) { |
||||
GPR_DEBUG_ASSERT(n <= size_); |
||||
size_ -= n; |
||||
} |
||||
|
||||
size_t find(char c, size_t pos = 0) const { |
||||
if (empty() || pos >= size_) return npos; |
||||
const char* result = |
||||
static_cast<const char*>(memchr(ptr_ + pos, c, size_ - pos)); |
||||
return result != nullptr ? result - ptr_ : npos; |
||||
} |
||||
|
||||
void clear() { |
||||
ptr_ = nullptr; |
||||
size_ = 0; |
||||
} |
||||
|
||||
// Creates a dup of the string viewed by this class.
|
||||
// Return value is null-terminated and never nullptr.
|
||||
//
|
||||
// Not part of absl::string_view API.
|
||||
grpc_core::UniquePtr<char> dup() const { |
||||
char* str = static_cast<char*>(gpr_malloc(size_ + 1)); |
||||
if (size_ > 0) memcpy(str, ptr_, size_); |
||||
str[size_] = '\0'; |
||||
return grpc_core::UniquePtr<char>(str); |
||||
} |
||||
|
||||
// Not part of absl::string_view API.
|
||||
int cmp(StringView other) const { |
||||
const size_t len = GPR_MIN(size(), other.size()); |
||||
const int ret = strncmp(data(), other.data(), len); |
||||
if (ret != 0) return ret; |
||||
if (size() == other.size()) return 0; |
||||
if (size() < other.size()) return -1; |
||||
return 1; |
||||
} |
||||
|
||||
private: |
||||
const char* ptr_; |
||||
size_t size_; |
||||
}; |
||||
|
||||
inline bool operator==(StringView lhs, StringView rhs) { |
||||
return lhs.size() == rhs.size() && |
||||
strncmp(lhs.data(), rhs.data(), lhs.size()) == 0; |
||||
} |
||||
|
||||
inline bool operator!=(StringView lhs, StringView rhs) { return !(lhs == rhs); } |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_GPRPP_STRING_VIEW_H */ |
@ -0,0 +1,163 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 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 <grpc/slice.h> |
||||
|
||||
#include "src/core/lib/gprpp/string_view.h" |
||||
|
||||
#include <gtest/gtest.h> |
||||
#include "src/core/lib/gprpp/memory.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace testing { |
||||
|
||||
TEST(StringViewTest, Empty) { |
||||
grpc_core::StringView empty; |
||||
EXPECT_TRUE(empty.empty()); |
||||
EXPECT_EQ(empty.size(), 0lu); |
||||
|
||||
grpc_core::StringView empty_buf(""); |
||||
EXPECT_TRUE(empty_buf.empty()); |
||||
EXPECT_EQ(empty_buf.size(), 0lu); |
||||
|
||||
grpc_core::StringView empty_trimmed("foo", 0); |
||||
EXPECT_TRUE(empty_trimmed.empty()); |
||||
EXPECT_EQ(empty_trimmed.size(), 0lu); |
||||
|
||||
grpc_core::StringView empty_slice(grpc_empty_slice()); |
||||
EXPECT_TRUE(empty_slice.empty()); |
||||
EXPECT_EQ(empty_slice.size(), 0lu); |
||||
} |
||||
|
||||
TEST(StringViewTest, Size) { |
||||
constexpr char kStr[] = "foo"; |
||||
grpc_core::StringView str1(kStr); |
||||
EXPECT_EQ(str1.size(), strlen(kStr)); |
||||
grpc_core::StringView str2(kStr, 2); |
||||
EXPECT_EQ(str2.size(), 2lu); |
||||
} |
||||
|
||||
TEST(StringViewTest, Data) { |
||||
constexpr char kStr[] = "foo-bar"; |
||||
grpc_core::StringView str(kStr); |
||||
EXPECT_EQ(str.size(), strlen(kStr)); |
||||
for (size_t i = 0; i < strlen(kStr); ++i) { |
||||
EXPECT_EQ(str[i], kStr[i]); |
||||
} |
||||
} |
||||
|
||||
TEST(StringViewTest, Slice) { |
||||
constexpr char kStr[] = "foo"; |
||||
grpc_core::StringView slice(grpc_slice_from_static_string(kStr)); |
||||
EXPECT_EQ(slice.size(), strlen(kStr)); |
||||
} |
||||
|
||||
TEST(StringViewTest, Dup) { |
||||
constexpr char kStr[] = "foo"; |
||||
grpc_core::StringView slice(grpc_slice_from_static_string(kStr)); |
||||
grpc_core::UniquePtr<char> dup = slice.dup(); |
||||
EXPECT_EQ(0, strcmp(kStr, dup.get())); |
||||
EXPECT_EQ(slice.size(), strlen(kStr)); |
||||
} |
||||
|
||||
TEST(StringViewTest, Eq) { |
||||
constexpr char kStr1[] = "foo"; |
||||
constexpr char kStr2[] = "bar"; |
||||
grpc_core::StringView str1(kStr1); |
||||
EXPECT_EQ(kStr1, str1); |
||||
EXPECT_EQ(str1, kStr1); |
||||
grpc_core::StringView slice1(grpc_slice_from_static_string(kStr1)); |
||||
EXPECT_EQ(slice1, str1); |
||||
EXPECT_EQ(str1, slice1); |
||||
EXPECT_NE(slice1, kStr2); |
||||
EXPECT_NE(kStr2, slice1); |
||||
grpc_core::StringView slice2(grpc_slice_from_static_string(kStr2)); |
||||
EXPECT_NE(slice2, str1); |
||||
EXPECT_NE(str1, slice2); |
||||
} |
||||
|
||||
TEST(StringViewTest, Cmp) { |
||||
constexpr char kStr1[] = "abc"; |
||||
constexpr char kStr2[] = "abd"; |
||||
constexpr char kStr3[] = "abcd"; |
||||
grpc_core::StringView str1(kStr1); |
||||
grpc_core::StringView str2(kStr2); |
||||
grpc_core::StringView str3(kStr3); |
||||
EXPECT_EQ(str1.cmp(str1), 0); |
||||
EXPECT_LT(str1.cmp(str2), 0); |
||||
EXPECT_LT(str1.cmp(str3), 0); |
||||
EXPECT_EQ(str2.cmp(str2), 0); |
||||
EXPECT_GT(str2.cmp(str1), 0); |
||||
EXPECT_GT(str2.cmp(str3), 0); |
||||
EXPECT_EQ(str3.cmp(str3), 0); |
||||
EXPECT_GT(str3.cmp(str1), 0); |
||||
EXPECT_LT(str3.cmp(str2), 0); |
||||
} |
||||
|
||||
TEST(StringViewTest, RemovePrefix) { |
||||
constexpr char kStr[] = "abcd"; |
||||
grpc_core::StringView str(kStr); |
||||
str.remove_prefix(1); |
||||
EXPECT_EQ("bcd", str); |
||||
str.remove_prefix(2); |
||||
EXPECT_EQ("d", str); |
||||
str.remove_prefix(1); |
||||
EXPECT_EQ("", str); |
||||
} |
||||
|
||||
TEST(StringViewTest, RemoveSuffix) { |
||||
constexpr char kStr[] = "abcd"; |
||||
grpc_core::StringView str(kStr); |
||||
str.remove_suffix(1); |
||||
EXPECT_EQ("abc", str); |
||||
str.remove_suffix(2); |
||||
EXPECT_EQ("a", str); |
||||
str.remove_suffix(1); |
||||
EXPECT_EQ("", str); |
||||
} |
||||
|
||||
TEST(StringViewTest, Substring) { |
||||
constexpr char kStr[] = "abcd"; |
||||
grpc_core::StringView str(kStr); |
||||
EXPECT_EQ("bcd", str.substr(1)); |
||||
EXPECT_EQ("bc", str.substr(1, 2)); |
||||
} |
||||
|
||||
TEST(StringViewTest, Find) { |
||||
// Passing StringView::npos directly to GTEST macros result in link errors.
|
||||
// Store the value in a local variable and use it in the test.
|
||||
const size_t npos = grpc_core::StringView::npos; |
||||
constexpr char kStr[] = "abacad"; |
||||
grpc_core::StringView str(kStr); |
||||
EXPECT_EQ(0ul, str.find('a')); |
||||
EXPECT_EQ(2ul, str.find('a', 1)); |
||||
EXPECT_EQ(4ul, str.find('a', 3)); |
||||
EXPECT_EQ(1ul, str.find('b')); |
||||
EXPECT_EQ(npos, str.find('b', 2)); |
||||
EXPECT_EQ(npos, str.find('z')); |
||||
} |
||||
|
||||
} // namespace testing
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc::testing::TestEnvironment env(argc, argv); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue