-- 1620e8ffaa93ef24510ca60c7fff2a07248ac9f6 by Abseil Team <absl-team@google.com>: Update comment. PiperOrigin-RevId: 382858259 -- 20db116f28469149d10e0f7f8b976cb903dd4879 by Gennadiy Rozental <rogeeff@google.com>: Add benchmark running on multiple flags. Update size_tester to include cost of absl::GetFlag call. Add size_tester invocation for bool flag. New benchmark better represent GetFlag usage. PiperOrigin-RevId: 382820341 -- 2e097ad3811c4e329f75b98877a5e74c1d3d84fd by Abseil Team <absl-team@google.com>: Avoid 64x64->128 multiplication in absl::Hash's mix on AArch64 On AArch64, calculating a 128-bit product is inefficient, because it requires a sequence of two instructions to calculate the upper and lower halves of the result. So calculate a 64-bit product instead. Making MultType 64-bits means the upper 32 bits of the result do not participate in shift/xor, but the add/multiply gives us sufficient mixing. PiperOrigin-RevId: 382625931 -- f3ae3f32cb53168c8dc91b766f2932dc87cec503 by Abseil Team <absl-team@google.com>: Remove homegrown Round implementation absl/time/duration.cc defined a Round implementation to accommodate old versions of MSVC that lacked std::round(long double). Abseil no longer supports those MSVCs, so we don’t need the homegrown implementation anymore. Remove it, and replace calls to it with std::rint. PiperOrigin-RevId: 382605191 -- a13631c91bf5478289e1a512ce215c85501a26f7 by Martijn Vels <mvels@google.com>: Move the Consume() conversion functions out of cord_rep_ring into cord_rep_consume. This makes these functions generic, so we can repurpose these for the new Btree conversion functions. PiperOrigin-RevId: 382594902 -- 7394c737500c2d8371fcf913b21ad1b321ba499d by Benjamin Barenblat <bbaren@google.com>: Remove homegrown Round implementation absl/time/duration.cc defined a Round implementation to accommodate old versions of MSVC that lacked std::round(long double). Abseil no longer supports those MSVCs, so we don’t need the homegrown implementation anymore. Remove it, and replace calls to it with std::rint. PiperOrigin-RevId: 382569900 -- d72a761f43dc5c9b9510c3a1363177ed26646b5d by Abseil Team <absl-team@google.com>: Prefer `getentropy` for Emscripten. It needs a different header, so I've separated it out from the GLIBC check above. PiperOrigin-RevId: 382332475 -- 74e261dbb467741b2ddd8b490e04c531fdd2f559 by Martijn Vels <mvels@google.com>: Add BTREE tag for CordRepNode implementing a Btree cord. This change only forward declared the CordRepBtree class (not implemented yet) and defines the enum value BTREE. While RING and BTREE should never co-exist, we define a new value for BTREE so as not to make transitioning between RING and BTREE harder than it needs to be. This changes shifts the FLAT value / computation from FLAT = 4 to FLAT =5 PiperOrigin-RevId: 382326710 GitOrigin-RevId: 1620e8ffaa93ef24510ca60c7fff2a07248ac9f6 Change-Id: Ia8f99dde3874808f56062bd37ab3e63764099734pull/986/head
parent
9a7e447c51
commit
58e042da92
13 changed files with 537 additions and 157 deletions
@ -0,0 +1,129 @@ |
||||
// Copyright 2021 The Abseil 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
|
||||
//
|
||||
// https://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 "absl/strings/internal/cord_rep_consume.h" |
||||
|
||||
#include <array> |
||||
#include <utility> |
||||
|
||||
#include "absl/container/inlined_vector.h" |
||||
#include "absl/functional/function_ref.h" |
||||
#include "absl/strings/internal/cord_internal.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace cord_internal { |
||||
|
||||
namespace { |
||||
|
||||
// Unrefs the provided `substring`, and returns `substring->child`
|
||||
// Adds or assumes a reference on `substring->child`
|
||||
CordRep* ClipSubstring(CordRepSubstring* substring) { |
||||
CordRep* child = substring->child; |
||||
if (substring->refcount.IsOne()) { |
||||
delete substring; |
||||
} else { |
||||
CordRep::Ref(child); |
||||
CordRep::Unref(substring); |
||||
} |
||||
return child; |
||||
} |
||||
|
||||
// Unrefs the provided `concat`, and returns `{concat->left, concat->right}`
|
||||
// Adds or assumes a reference on `concat->left` and `concat->right`.
|
||||
// Returns an array of 2 elements containing the left and right nodes.
|
||||
std::array<CordRep*, 2> ClipConcat(CordRepConcat* concat) { |
||||
std::array<CordRep*, 2> result{concat->left, concat->right}; |
||||
if (concat->refcount.IsOne()) { |
||||
delete concat; |
||||
} else { |
||||
CordRep::Ref(result[0]); |
||||
CordRep::Ref(result[1]); |
||||
CordRep::Unref(concat); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
void Consume(bool forward, CordRep* rep, ConsumeFn consume_fn) { |
||||
size_t offset = 0; |
||||
size_t length = rep->length; |
||||
struct Entry { |
||||
CordRep* rep; |
||||
size_t offset; |
||||
size_t length; |
||||
}; |
||||
absl::InlinedVector<Entry, 40> stack; |
||||
|
||||
for (;;) { |
||||
if (rep->tag == CONCAT) { |
||||
std::array<CordRep*, 2> res = ClipConcat(rep->concat()); |
||||
CordRep* left = res[0]; |
||||
CordRep* right = res[1]; |
||||
|
||||
if (left->length <= offset) { |
||||
// Don't need left node
|
||||
offset -= left->length; |
||||
CordRep::Unref(left); |
||||
rep = right; |
||||
continue; |
||||
} |
||||
|
||||
size_t length_left = left->length - offset; |
||||
if (length_left >= length) { |
||||
// Don't need right node
|
||||
CordRep::Unref(right); |
||||
rep = left; |
||||
continue; |
||||
} |
||||
|
||||
// Need both nodes
|
||||
size_t length_right = length - length_left; |
||||
if (forward) { |
||||
stack.push_back({right, 0, length_right}); |
||||
rep = left; |
||||
length = length_left; |
||||
} else { |
||||
stack.push_back({left, offset, length_left}); |
||||
rep = right; |
||||
offset = 0; |
||||
length = length_right; |
||||
} |
||||
} else if (rep->tag == SUBSTRING) { |
||||
offset += rep->substring()->start; |
||||
rep = ClipSubstring(rep->substring()); |
||||
} else { |
||||
consume_fn(rep, offset, length); |
||||
if (stack.empty()) return; |
||||
|
||||
rep = stack.back().rep; |
||||
offset = stack.back().offset; |
||||
length = stack.back().length; |
||||
stack.pop_back(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void Consume(CordRep* rep, ConsumeFn consume_fn) { |
||||
return Consume(true, rep, std::move(consume_fn)); |
||||
} |
||||
|
||||
void ReverseConsume(CordRep* rep, ConsumeFn consume_fn) { |
||||
return Consume(false, rep, std::move(consume_fn)); |
||||
} |
||||
|
||||
} // namespace cord_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,50 @@ |
||||
// Copyright 2021 The Abseil 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
|
||||
//
|
||||
// https://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 ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ |
||||
#define ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_ |
||||
|
||||
#include <functional> |
||||
|
||||
#include "absl/functional/function_ref.h" |
||||
#include "absl/strings/internal/cord_internal.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace cord_internal { |
||||
|
||||
// Functor for the Consume() and ReverseConsume() functions:
|
||||
// void ConsumeFunc(CordRep* rep, size_t offset, size_t length);
|
||||
// See the Consume() and ReverseConsume() function comments for documentation.
|
||||
using ConsumeFn = FunctionRef<void(CordRep*, size_t, size_t)>; |
||||
|
||||
// Consume() and ReverseConsume() consume CONCAT based trees and invoke the
|
||||
// provided functor with the contained nodes in the proper forward or reverse
|
||||
// order, which is used to convert CONCAT trees into other tree or cord data.
|
||||
// All CONCAT and SUBSTRING nodes are processed internally. The 'offset`
|
||||
// parameter of the functor is non-zero for any nodes below SUBSTRING nodes.
|
||||
// It's up to the caller to form these back into SUBSTRING nodes or otherwise
|
||||
// store offset / prefix information. These functions are intended to be used
|
||||
// only for migration / transitional code where due to factors such as ODR
|
||||
// violations, we can not 100% guarantee that all code respects 'new format'
|
||||
// settings and flags, so we need to be able to parse old data on the fly until
|
||||
// all old code is deprecated / no longer the default format.
|
||||
void Consume(CordRep* rep, ConsumeFn consume_fn); |
||||
void ReverseConsume(CordRep* rep, ConsumeFn consume_fn); |
||||
|
||||
} // namespace cord_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_
|
@ -0,0 +1,173 @@ |
||||
// Copyright 2021 The Abseil 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
|
||||
//
|
||||
// https://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 "absl/strings/internal/cord_rep_consume.h" |
||||
|
||||
#include <functional> |
||||
#include <utility> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/strings/internal/cord_internal.h" |
||||
#include "absl/strings/internal/cord_rep_flat.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace cord_internal { |
||||
namespace { |
||||
|
||||
using testing::InSequence; |
||||
using testing::MockFunction; |
||||
|
||||
// Returns the depth of a node
|
||||
int Depth(const CordRep* rep) { |
||||
return (rep->tag == CONCAT) ? rep->concat()->depth() : 0; |
||||
} |
||||
|
||||
// Creates a concatenation of the specified nodes.
|
||||
CordRepConcat* CreateConcat(CordRep* left, CordRep* right) { |
||||
auto* concat = new CordRepConcat(); |
||||
concat->tag = CONCAT; |
||||
concat->left = left; |
||||
concat->right = right; |
||||
concat->length = left->length + right->length; |
||||
concat->set_depth(1 + (std::max)(Depth(left), Depth(right))); |
||||
return concat; |
||||
} |
||||
|
||||
// Creates a flat with the length set to `length`
|
||||
CordRepFlat* CreateFlatWithLength(size_t length) { |
||||
auto* flat = CordRepFlat::New(length); |
||||
flat->length = length; |
||||
return flat; |
||||
} |
||||
|
||||
// Creates a substring node on the specified child.
|
||||
CordRepSubstring* CreateSubstring(CordRep* child, size_t start, size_t length) { |
||||
auto* rep = new CordRepSubstring(); |
||||
rep->length = length; |
||||
rep->tag = SUBSTRING; |
||||
rep->start = start; |
||||
rep->child = child; |
||||
return rep; |
||||
} |
||||
|
||||
// Flats we use in the tests
|
||||
CordRep* flat[6]; |
||||
|
||||
// Creates a test tree
|
||||
CordRep* CreateTestTree() { |
||||
flat[0] = CreateFlatWithLength(1); |
||||
flat[1] = CreateFlatWithLength(7); |
||||
CordRepConcat* left = CreateConcat(flat[0], CreateSubstring(flat[1], 2, 4)); |
||||
|
||||
flat[2] = CreateFlatWithLength(9); |
||||
flat[3] = CreateFlatWithLength(13); |
||||
CordRepConcat* right1 = CreateConcat(flat[2], flat[3]); |
||||
|
||||
flat[4] = CreateFlatWithLength(15); |
||||
flat[5] = CreateFlatWithLength(19); |
||||
CordRepConcat* right2 = CreateConcat(flat[4], flat[5]); |
||||
|
||||
CordRepConcat* right = CreateConcat(right1, CreateSubstring(right2, 5, 17)); |
||||
return CreateConcat(left, right); |
||||
} |
||||
|
||||
TEST(CordRepConsumeTest, Consume) { |
||||
InSequence in_sequence; |
||||
CordRep* tree = CreateTestTree(); |
||||
MockFunction<void(CordRep*, size_t, size_t)> consume; |
||||
EXPECT_CALL(consume, Call(flat[0], 0, 1)); |
||||
EXPECT_CALL(consume, Call(flat[1], 2, 4)); |
||||
EXPECT_CALL(consume, Call(flat[2], 0, 9)); |
||||
EXPECT_CALL(consume, Call(flat[3], 0, 13)); |
||||
EXPECT_CALL(consume, Call(flat[4], 5, 10)); |
||||
EXPECT_CALL(consume, Call(flat[5], 0, 7)); |
||||
Consume(tree, consume.AsStdFunction()); |
||||
for (CordRep* rep : flat) { |
||||
EXPECT_TRUE(rep->refcount.IsOne()); |
||||
CordRep::Unref(rep); |
||||
} |
||||
} |
||||
|
||||
TEST(CordRepConsumeTest, ConsumeShared) { |
||||
InSequence in_sequence; |
||||
CordRep* tree = CreateTestTree(); |
||||
MockFunction<void(CordRep*, size_t, size_t)> consume; |
||||
EXPECT_CALL(consume, Call(flat[0], 0, 1)); |
||||
EXPECT_CALL(consume, Call(flat[1], 2, 4)); |
||||
EXPECT_CALL(consume, Call(flat[2], 0, 9)); |
||||
EXPECT_CALL(consume, Call(flat[3], 0, 13)); |
||||
EXPECT_CALL(consume, Call(flat[4], 5, 10)); |
||||
EXPECT_CALL(consume, Call(flat[5], 0, 7)); |
||||
Consume(CordRep::Ref(tree), consume.AsStdFunction()); |
||||
for (CordRep* rep : flat) { |
||||
EXPECT_FALSE(rep->refcount.IsOne()); |
||||
CordRep::Unref(rep); |
||||
} |
||||
CordRep::Unref(tree); |
||||
} |
||||
|
||||
TEST(CordRepConsumeTest, Reverse) { |
||||
InSequence in_sequence; |
||||
CordRep* tree = CreateTestTree(); |
||||
MockFunction<void(CordRep*, size_t, size_t)> consume; |
||||
EXPECT_CALL(consume, Call(flat[5], 0, 7)); |
||||
EXPECT_CALL(consume, Call(flat[4], 5, 10)); |
||||
EXPECT_CALL(consume, Call(flat[3], 0, 13)); |
||||
EXPECT_CALL(consume, Call(flat[2], 0, 9)); |
||||
EXPECT_CALL(consume, Call(flat[1], 2, 4)); |
||||
EXPECT_CALL(consume, Call(flat[0], 0, 1)); |
||||
ReverseConsume(tree, consume.AsStdFunction()); |
||||
for (CordRep* rep : flat) { |
||||
EXPECT_TRUE(rep->refcount.IsOne()); |
||||
CordRep::Unref(rep); |
||||
} |
||||
} |
||||
|
||||
TEST(CordRepConsumeTest, ReverseShared) { |
||||
InSequence in_sequence; |
||||
CordRep* tree = CreateTestTree(); |
||||
MockFunction<void(CordRep*, size_t, size_t)> consume; |
||||
EXPECT_CALL(consume, Call(flat[5], 0, 7)); |
||||
EXPECT_CALL(consume, Call(flat[4], 5, 10)); |
||||
EXPECT_CALL(consume, Call(flat[3], 0, 13)); |
||||
EXPECT_CALL(consume, Call(flat[2], 0, 9)); |
||||
EXPECT_CALL(consume, Call(flat[1], 2, 4)); |
||||
EXPECT_CALL(consume, Call(flat[0], 0, 1)); |
||||
ReverseConsume(CordRep::Ref(tree), consume.AsStdFunction()); |
||||
for (CordRep* rep : flat) { |
||||
EXPECT_FALSE(rep->refcount.IsOne()); |
||||
CordRep::Unref(rep); |
||||
} |
||||
CordRep::Unref(tree); |
||||
} |
||||
|
||||
TEST(CordRepConsumeTest, UnreachableFlat) { |
||||
InSequence in_sequence; |
||||
CordRepFlat* flat1 = CreateFlatWithLength(10); |
||||
CordRepFlat* flat2 = CreateFlatWithLength(20); |
||||
CordRepConcat* concat = CreateConcat(flat1, flat2); |
||||
CordRepSubstring* tree = CreateSubstring(concat, 15, 10); |
||||
MockFunction<void(CordRep*, size_t, size_t)> consume; |
||||
EXPECT_CALL(consume, Call(flat2, 5, 10)); |
||||
Consume(tree, consume.AsStdFunction()); |
||||
EXPECT_TRUE(flat2->refcount.IsOne()); |
||||
CordRep::Unref(flat2); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace cord_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
Loading…
Reference in new issue