Skip to content

Commit

Permalink
Add StringRef, BytesRef, and CStringRef to support `std::string…
Browse files Browse the repository at this point in the history
…_view`

when it is a different type than `absl::string_view`. (Sadly, making
`absl::string_view` convertible from `std::string_view` would lead to
ambiguities e.g. regarding `operator==`.)

`StringRef` stores an `absl::string_view`, possibly converted from
`std::string_view`.

`BytesRef` stores an `absl::string_view`, possibly converted from
`std::string_view` or `absl::Span<const char>`. It replaces
`SupportsToStringView` with `ToStringView()`, but it is a type,
rather than a type predicate associated with a function, and thus
does not require making templates.

`CStringRef` stores a pointer to a C-style NUL-terminated string, or `nullptr`.
The string is copied unless it is known to end with NUL.

They are intended for function parameters when the implementation needs
`absl::string_view` or a C-style NUL-terminated string, and the caller might
have other representations of the string.

Use `CStringRef` in `Owned{Fd,CFile}::Open{,At}()` to reduce the number of
overloads. Also for Windows: while it does not need NUL-termination because
the strings are converted to `std::wstring`, the resulting performance penalty
is dwarfed by the cost of opening the file, and a unified API is nicer.

Make conversions to `absl::string_view` implicit, as is customary.

Make `CompactString` constructible/assignable from and comparable with
`BytesRef` instead of only `absl::string_view`.
Add `CompactString::{at,front,back}()` to complement `operator[]`.

PiperOrigin-RevId: 717516642
  • Loading branch information
QrczakMK committed Jan 20, 2025
1 parent 3fb6819 commit f2054c7
Show file tree
Hide file tree
Showing 49 changed files with 910 additions and 691 deletions.
5 changes: 3 additions & 2 deletions python/riegeli/base/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,8 @@ class BytesLike {
}

// Returns the binary contents.
explicit operator absl::string_view() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
/*implicit*/ operator absl::string_view() const
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return absl::string_view(static_cast<const char*>(buffer_.buf),
IntCast<size_t>(buffer_.len));
}
Expand Down Expand Up @@ -517,7 +518,7 @@ class StrOrBytes {
bool FromPython(PyObject* object ABSL_ATTRIBUTE_LIFETIME_BOUND);

// Returns the text contents.
explicit operator absl::string_view() const { return data_; }
/*implicit*/ operator absl::string_view() const { return data_; }

private:
absl::string_view data_;
Expand Down
4 changes: 2 additions & 2 deletions python/riegeli/records/record_position.cc
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ static PyRecordPositionObject* RecordPositionFromStr(PyTypeObject* cls,
return nullptr;
}
RecordPosition pos;
if (ABSL_PREDICT_FALSE(!pos.FromString(absl::string_view(serialized)))) {
if (ABSL_PREDICT_FALSE(!pos.FromString(serialized))) {
PyErr_SetString(PyExc_ValueError, "RecordPosition.from_str() failed");
return nullptr;
}
Expand Down Expand Up @@ -223,7 +223,7 @@ static PyRecordPositionObject* RecordPositionFromBytes(PyTypeObject* cls,
return nullptr;
}
RecordPosition pos;
if (ABSL_PREDICT_FALSE(!pos.FromBytes(absl::string_view(serialized)))) {
if (ABSL_PREDICT_FALSE(!pos.FromBytes(serialized))) {
PyErr_SetString(PyExc_ValueError, "RecordPosition.from_bytes() failed");
return nullptr;
}
Expand Down
23 changes: 9 additions & 14 deletions python/riegeli/records/record_writer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,7 @@ static int RecordWriterInit(PyRecordWriterObject* self, PyObject* args,
StrOrBytes options;
if (ABSL_PREDICT_FALSE(!options.FromPython(options_arg))) return -1;
{
const absl::Status status =
record_writer_options.FromString(absl::string_view(options));
const absl::Status status = record_writer_options.FromString(options);
if (ABSL_PREDICT_FALSE(!status.ok())) {
SetRiegeliError(status);
return -1;
Expand Down Expand Up @@ -418,9 +417,8 @@ static PyObject* RecordWriterWriteRecord(PyRecordWriterObject* self,
BytesLike record;
if (ABSL_PREDICT_FALSE(!record.FromPython(record_arg))) return nullptr;
if (ABSL_PREDICT_FALSE(!self->record_writer.Verify())) return nullptr;
const bool write_record_ok = PythonUnlocked([&] {
return self->record_writer->WriteRecord(absl::string_view(record));
});
const bool write_record_ok =
PythonUnlocked([&] { return self->record_writer->WriteRecord(record); });
if (ABSL_PREDICT_FALSE(!write_record_ok)) {
SetExceptionFromRecordWriter(self);
return nullptr;
Expand All @@ -447,9 +445,8 @@ static PyObject* RecordWriterWriteMessage(PyRecordWriterObject* self,
return nullptr;
}
if (ABSL_PREDICT_FALSE(!self->record_writer.Verify())) return nullptr;
const bool write_record_ok = PythonUnlocked([&] {
return self->record_writer->WriteRecord(absl::string_view(serialized));
});
const bool write_record_ok = PythonUnlocked(
[&] { return self->record_writer->WriteRecord(serialized); });
if (ABSL_PREDICT_FALSE(!write_record_ok)) {
SetExceptionFromRecordWriter(self);
return nullptr;
Expand All @@ -476,9 +473,8 @@ static PyObject* RecordWriterWriteRecords(PyRecordWriterObject* self,
return nullptr;
}
if (ABSL_PREDICT_FALSE(!self->record_writer.Verify())) return nullptr;
const bool write_record_ok = PythonUnlocked([&] {
return self->record_writer->WriteRecord(absl::string_view(record));
});
const bool write_record_ok = PythonUnlocked(
[&] { return self->record_writer->WriteRecord(record); });
if (ABSL_PREDICT_FALSE(!write_record_ok)) {
SetExceptionFromRecordWriter(self);
return nullptr;
Expand Down Expand Up @@ -511,9 +507,8 @@ static PyObject* RecordWriterWriteMessages(PyRecordWriterObject* self,
return nullptr;
}
if (ABSL_PREDICT_FALSE(!self->record_writer.Verify())) return nullptr;
const bool write_record_ok = PythonUnlocked([&] {
return self->record_writer->WriteRecord(absl::string_view(serialized));
});
const bool write_record_ok = PythonUnlocked(
[&] { return self->record_writer->WriteRecord(serialized); });
if (ABSL_PREDICT_FALSE(!write_record_ok)) {
SetExceptionFromRecordWriter(self);
return nullptr;
Expand Down
45 changes: 41 additions & 4 deletions riegeli/base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -224,16 +224,49 @@ cc_library(
)

cc_library(
name = "to_string_view",
hdrs = ["to_string_view.h"],
name = "string_ref",
hdrs = ["string_ref.h"],
deps = [
":assert",
":compare",
":type_traits",
"@com_google_absl//absl/base:config",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/meta:type_traits",
"@com_google_absl//absl/strings",
],
)

cc_library(
name = "bytes_ref",
hdrs = ["bytes_ref.h"],
deps = [
":compare",
":string_ref",
":type_traits",
"@com_google_absl//absl/base:config",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/meta:type_traits",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
],
)

cc_library(
name = "c_string_ref",
hdrs = ["c_string_ref.h"],
deps = [
":compare",
":initializer",
":string_ref",
":type_traits",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/meta:type_traits",
"@com_google_absl//absl/strings",
],
)

cc_library(
name = "memory_estimator",
srcs = ["memory_estimator.cc"],
Expand Down Expand Up @@ -270,12 +303,13 @@ cc_library(
],
deps = [
":assert",
":bytes_ref",
":compare",
":initializer",
":reset",
":to_string_view",
":type_id",
":type_traits",
"@com_google_absl//absl/base:config",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/meta:type_traits",
Expand Down Expand Up @@ -477,6 +511,7 @@ cc_library(
":arithmetic",
":assert",
":buffering",
":bytes_ref",
":compare",
":cord_utils",
":external_data",
Expand All @@ -486,7 +521,6 @@ cc_library(
":shared_ptr",
":stream_utils",
":string_utils",
":to_string_view",
":type_traits",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:inlined_vector",
Expand Down Expand Up @@ -541,13 +575,16 @@ cc_library(
deps = [
":arithmetic",
":assert",
":bytes_ref",
":compare",
":estimated_allocated_size",
":external_data",
":new_aligned",
":type_traits",
"@com_google_absl//absl/base:config",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/hash",
"@com_google_absl//absl/meta:type_traits",
"@com_google_absl//absl/strings",
],
)
Expand Down
114 changes: 114 additions & 0 deletions riegeli/base/bytes_ref.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2024 Google LLC
//
// 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 RIEGELI_BASE_BYTES_REF_H_
#define RIEGELI_BASE_BYTES_REF_H_

#include <stddef.h>

#include <type_traits>

#include "absl/base/attributes.h"
#include "absl/base/config.h" // IWYU pragma: keep
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "riegeli/base/compare.h"
#include "riegeli/base/string_ref.h"
#include "riegeli/base/type_traits.h"

namespace riegeli {

// `BytesRef` stores an `absl::string_view` representing text or binary data
// (see `StringRef` for text data), possibly converted from `std::string_view`
// or `absl::Span<const char>`.
//
// It is intended for function parameters when the implementation needs
// an `absl::string_view`, and the caller might have another representation
// of the string.
//
// It is convertible from:
// * types convertible to `absl::string_view`
// * types convertible to `std::string_view`
// * types convertible to `absl::Span<const char>`,
// e.g. `std::vector<char>` or `std::array<char, length>`.
//
// `BytesRef` does not own string contents and is efficiently copyable.
class BytesRef : public StringRef, public WithCompare<BytesRef> {
public:
// Stores an empty `absl::string_view`.
BytesRef() = default;

// Stores `str` converted to `StringRef` and then to `absl::string_view`.
template <typename T,
std::enable_if_t<
absl::conjunction<NotSelfCopy<BytesRef, T>,
std::is_convertible<T&&, StringRef>>::value,
int> = 0>
/*implicit*/ BytesRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
: StringRef(std::forward<T>(str)) {}

// Stores `str` converted to `absl::string_view`.
/*implicit*/ BytesRef(
absl::Span<const char> str ABSL_ATTRIBUTE_LIFETIME_BOUND)
: StringRef(absl::string_view(str.data(), str.size())) {}

// Stores `str` converted to `absl::Span<const char>` and then to
// `absl::string_view`.
template <typename T,
std::enable_if_t<
absl::conjunction<
NotSelfCopy<BytesRef, T>,
absl::negation<std::is_convertible<T&&, StringRef>>,
absl::negation<
std::is_same<std::decay_t<T>, absl::Span<const char>>>,
std::is_convertible<T&&, absl::Span<const char>>>::value,
int> = 0>
/*implicit*/ BytesRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
: BytesRef(absl::Span<const char>(std::forward<T>(str))) {}

BytesRef(const BytesRef& that) = default;
BytesRef& operator=(const BytesRef&) = delete;

friend bool operator==(BytesRef a, BytesRef b) {
return absl::string_view(a) == absl::string_view(b);
}
friend riegeli::StrongOrdering RIEGELI_COMPARE(BytesRef a, BytesRef b) {
return riegeli::Compare(absl::string_view(a), absl::string_view(b));
}

template <typename T,
std::enable_if_t<
absl::conjunction<NotSelfCopy<BytesRef, T>,
std::is_convertible<T&&, StringRef>>::value,
int> = 0>
friend bool operator==(BytesRef a, T&& b) {
return a == BytesRef(std::forward<T>(b));
}
template <typename T,
std::enable_if_t<
absl::conjunction<NotSelfCopy<BytesRef, T>,
std::is_convertible<T&&, StringRef>>::value,
int> = 0>
friend riegeli::StrongOrdering RIEGELI_COMPARE(BytesRef a, T&& b) {
return riegeli::Compare(a, BytesRef(std::forward<T>(b)));
}

// `absl::Span<const char>` is already comparable against types convertible to
// `absl::Span<const char>`, which includes `BytesRef`.
};

} // namespace riegeli

#endif // RIEGELI_BASE_BYTES_REF_H_
Loading

0 comments on commit f2054c7

Please sign in to comment.