mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
287 lines
12 KiB
C++
287 lines
12 KiB
C++
// Protocol Buffers - Google's data interchange format
|
|
// Copyright 2008 Google Inc. All rights reserved.
|
|
// https://developers.google.com/protocol-buffers/
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include <thirdparty/protobuf/inlined_string_field.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <thirdparty/protobuf/stubs/logging.h>
|
|
#include <thirdparty/protobuf/stubs/common.h>
|
|
#include <thirdparty/protobuf/io/coded_stream.h>
|
|
#include <thirdparty/protobuf/io/zero_copy_stream_impl.h>
|
|
#include <thirdparty/protobuf/arenastring.h>
|
|
#include <gtest/gtest.h>
|
|
#include <thirdparty/protobuf/stubs/strutil.h>
|
|
|
|
|
|
namespace google {
|
|
namespace protobuf {
|
|
|
|
using internal::ArenaStringPtr;
|
|
using internal::InlinedStringField;
|
|
|
|
static std::string WrapString(const char* value) { return value; }
|
|
|
|
namespace {
|
|
|
|
const uint32 kMask = ~0x00000001u;
|
|
const uint32 kMask1 = ~0x00000004u;
|
|
const uint32 kMask2 = ~0x00000020u;
|
|
|
|
TEST(InlinedStringFieldTest, SetOnHeap) {
|
|
InlinedStringField field;
|
|
uint32 donating_states = 0;
|
|
const std::string kDefaultValue = "default";
|
|
field.Set(&kDefaultValue, WrapString("Test short"), nullptr, false,
|
|
&donating_states, kMask);
|
|
EXPECT_EQ(std::string("Test short"), field.Get());
|
|
field.Set(&kDefaultValue, WrapString("Test long long long long value"),
|
|
nullptr, false, &donating_states, kMask);
|
|
EXPECT_EQ(std::string("Test long long long long value"), field.Get());
|
|
}
|
|
|
|
TEST(InlinedStringFieldTest, SetRvalueOnHeap) {
|
|
InlinedStringField field;
|
|
uint32 donating_states = 0;
|
|
std::string string_moved = "Moved long long long long string 1";
|
|
field.Set(nullptr, std::move(string_moved), nullptr, false, &donating_states,
|
|
kMask);
|
|
EXPECT_EQ("Moved long long long long string 1", field.Get());
|
|
EXPECT_EQ(donating_states & ~kMask1, 0);
|
|
}
|
|
|
|
TEST(InlinedStringFieldTest, UnsafeMutablePointerThenRelease) {
|
|
InlinedStringField field;
|
|
const std::string kDefaultValue = "default";
|
|
std::string* mut = field.UnsafeMutablePointer();
|
|
// The address of inlined string doesn't change behind the scene.
|
|
EXPECT_EQ(mut, field.UnsafeMutablePointer());
|
|
EXPECT_EQ(mut, &field.Get());
|
|
EXPECT_EQ(std::string(""), *mut);
|
|
*mut = "Test long long long long value"; // ensure string allocates
|
|
EXPECT_EQ(std::string("Test long long long long value"), field.Get());
|
|
|
|
std::string* released = field.ReleaseNonDefaultNoArena(&kDefaultValue);
|
|
EXPECT_EQ("Test long long long long value", *released);
|
|
// Default value is ignored.
|
|
EXPECT_EQ("", field.Get());
|
|
delete released;
|
|
}
|
|
|
|
// When donating mechanism is enabled:
|
|
// - Initially, the string is donated.
|
|
// - After lvalue Set: the string is still donated.
|
|
// - After Mutable: the string is undonated. The data buffer of the string is a
|
|
// new buffer on the heap.
|
|
TEST(InlinedStringFieldTest, ArenaSetThenMutable) {
|
|
Arena arena;
|
|
auto* field_arena = Arena::CreateMessage<InlinedStringField>(&arena);
|
|
uint32 donating_states = ~0u;
|
|
const std::string kDefaultValue = "default";
|
|
field_arena->Set(&kDefaultValue, WrapString("Test short"), &arena,
|
|
/*donated=*/true, &donating_states, kMask1);
|
|
EXPECT_EQ(std::string("Test short"), field_arena->Get());
|
|
field_arena->Set(&kDefaultValue, "Test long long long long value", &arena,
|
|
/*donated=*/(donating_states & ~kMask1) != 0,
|
|
&donating_states, kMask1);
|
|
EXPECT_EQ(std::string("Test long long long long value"), field_arena->Get());
|
|
#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
|
|
EXPECT_NE(donating_states & ~kMask1, 0); // donate.
|
|
#endif
|
|
|
|
const std::string* old_string = &field_arena->Get();
|
|
const char* old_data = old_string->data();
|
|
(void)old_data;
|
|
std::string* mut = field_arena->Mutable(
|
|
ArenaStringPtr::EmptyDefault{}, &arena,
|
|
/*donated=*/(donating_states & ~kMask1) != 0, &donating_states, kMask1);
|
|
EXPECT_EQ(old_string, mut);
|
|
#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
|
|
EXPECT_EQ(donating_states & ~kMask1, 0);
|
|
EXPECT_NE(old_data, mut->data()); // The data buffer of the mutated string is
|
|
// a new buffer on the heap.
|
|
#endif
|
|
*mut = "Test an even longer long long long long value";
|
|
EXPECT_EQ(std::string("Test an even longer long long long long value"),
|
|
field_arena->Get());
|
|
EXPECT_EQ(&field_arena->Get(), mut);
|
|
}
|
|
|
|
// Release doesn't change the donating state.
|
|
// When donating mechanism is enabled:
|
|
// - Initially, the string is donated.
|
|
// - Then lvalue Set: the string is still donated.
|
|
// - Then Release: the string is cleared, and still donated.
|
|
// - Then Mutable: the string is undonated.
|
|
// - Then Release: the string is still undonated.
|
|
TEST(InlinedStringFieldTest, ArenaRelease) {
|
|
Arena arena;
|
|
auto* field_arena = Arena::CreateMessage<InlinedStringField>(&arena);
|
|
uint32 donating_states = ~0u;
|
|
const std::string kDefaultValue = "default";
|
|
field_arena->Set(&kDefaultValue, WrapString("Test short"), &arena,
|
|
/*donated=*/true, &donating_states, kMask1);
|
|
std::string* released = field_arena->Release(
|
|
&kDefaultValue, &arena, /*donated=*/(donating_states & ~kMask1) != 0);
|
|
EXPECT_EQ("Test short", *released);
|
|
EXPECT_EQ("", field_arena->Get());
|
|
EXPECT_NE(released, &field_arena->Get());
|
|
#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
|
|
EXPECT_NE(donating_states & ~kMask1, 0); // still donated.
|
|
#endif
|
|
delete released;
|
|
|
|
std::string* mut = field_arena->Mutable(
|
|
ArenaStringPtr::EmptyDefault{}, &arena,
|
|
/*donated=*/(donating_states & ~kMask1) != 0u, &donating_states, kMask1);
|
|
*mut = "Test long long long long value";
|
|
std::string* released2 =
|
|
field_arena->Release(&kDefaultValue, &arena,
|
|
/*donated=*/(donating_states & ~kMask1) != 0);
|
|
EXPECT_EQ("Test long long long long value", *released2);
|
|
EXPECT_EQ("", field_arena->Get());
|
|
#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
|
|
EXPECT_EQ(donating_states & ~kMask1, 0); // undonated.
|
|
#endif
|
|
delete released2;
|
|
}
|
|
|
|
// Rvalue Set always undoantes a donated string.
|
|
TEST(InlinedStringFieldTest, SetRvalueArena) {
|
|
Arena arena;
|
|
auto* field1_arena = Arena::CreateMessage<InlinedStringField>(&arena);
|
|
auto* field2_arena = Arena::CreateMessage<InlinedStringField>(&arena);
|
|
uint32 donating_states = ~0u;
|
|
const std::string kDefaultValue = "default";
|
|
|
|
std::string string_moved = "Moved long long long long string 1";
|
|
field1_arena->Set(nullptr, std::move(string_moved), &arena, true,
|
|
&donating_states, kMask1);
|
|
EXPECT_EQ("Moved long long long long string 1", field1_arena->Get());
|
|
#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
|
|
EXPECT_EQ(donating_states & ~kMask1, 0); // Undonate.
|
|
#endif
|
|
|
|
field2_arena->Set(nullptr, std::string("string 2 on heap"), &arena, true,
|
|
&donating_states, kMask);
|
|
EXPECT_EQ("string 2 on heap", field2_arena->Get());
|
|
#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
|
|
EXPECT_EQ(donating_states & ~kMask, 0); // Undonated.
|
|
#endif
|
|
}
|
|
|
|
// Tests SetAllocated for non-arena string fields and arena string fields.
|
|
TEST(InlinedStringFieldTest, SetAllocated) {
|
|
InlinedStringField field;
|
|
Arena arena;
|
|
auto* field1_arena = Arena::CreateMessage<InlinedStringField>(&arena);
|
|
auto* field2_arena = Arena::CreateMessage<InlinedStringField>(&arena);
|
|
uint32 donating_states = ~0u;
|
|
const std::string kDefaultValue = "default";
|
|
|
|
// The string in field is on heap.
|
|
field.Set(&kDefaultValue, WrapString("String on heap"), nullptr, false,
|
|
&donating_states, kMask);
|
|
auto* allocated = new std::string("Allocated string on heap");
|
|
field.SetAllocatedNoArena(&kDefaultValue, allocated);
|
|
EXPECT_EQ("Allocated string on heap", field.Get());
|
|
|
|
// The string in field1_arena is on arena (aka. donated).
|
|
field1_arena->Set(&kDefaultValue, WrapString("String 1 on arena"), &arena,
|
|
true, &donating_states, kMask1);
|
|
*field1_arena->Mutable(ArenaStringPtr::EmptyDefault{}, &arena,
|
|
(donating_states & ~kMask1) != 0, &donating_states,
|
|
kMask1) = "Mutated string 1 is now on heap long long";
|
|
// After Mutable, the string is undonated.
|
|
allocated = new std::string("Allocated string on heap long long long");
|
|
field1_arena->SetAllocated(&kDefaultValue, allocated, &arena,
|
|
(donating_states & ~kMask1) != 0, &donating_states,
|
|
kMask1);
|
|
EXPECT_EQ("Allocated string on heap long long long", field1_arena->Get());
|
|
#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
|
|
EXPECT_EQ(donating_states & ~kMask1, 0); // Still undonated.
|
|
#endif
|
|
|
|
// The string in field2_arena is on arena (aka. donated).
|
|
field2_arena->Set(&kDefaultValue, WrapString("String 2 on arena long long"),
|
|
&arena, true, &donating_states,
|
|
kMask2); // Still donated.
|
|
allocated = new std::string("Allocated string on heap long long long 2");
|
|
field2_arena->SetAllocated(&kDefaultValue, allocated, &arena, true,
|
|
&donating_states, kMask2);
|
|
EXPECT_EQ("Allocated string on heap long long long 2", field2_arena->Get());
|
|
#if GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE
|
|
EXPECT_EQ(donating_states & ~kMask2, 0); // Undonated.
|
|
#endif
|
|
}
|
|
|
|
// Tests Swap for non-arena string fields and arena string fields.
|
|
TEST(InlinedStringFieldTest, Swap) {
|
|
// Swap should only be called when the from and to are on the same arena.
|
|
InlinedStringField field1;
|
|
InlinedStringField field2;
|
|
uint32 donating_states = 0;
|
|
Arena arena;
|
|
auto* field1_arena = Arena::CreateMessage<InlinedStringField>(&arena);
|
|
auto* field2_arena = Arena::CreateMessage<InlinedStringField>(&arena);
|
|
uint32 donating_states_1 = ~0u;
|
|
uint32 donating_states_2 = ~0u;
|
|
const std::string kDefaultValue = "default";
|
|
|
|
const std::string& string1_heap = "String 1 on heap";
|
|
const std::string& string2_heap = "String 2 on heap long long long long";
|
|
const std::string& string1_arena = "String 1 on arena";
|
|
const std::string& string2_arena = "String 2 on arena long long long long";
|
|
field1.SetNoArena(&kDefaultValue, string1_heap);
|
|
field2.SetNoArena(&kDefaultValue, string2_heap);
|
|
field1_arena->Set(&kDefaultValue, string1_arena, &arena, true,
|
|
&donating_states_1, kMask1);
|
|
field2_arena->Set(&kDefaultValue, string2_arena, &arena, true,
|
|
&donating_states_2, kMask1);
|
|
|
|
field1.Swap(&field2, &kDefaultValue, nullptr, false, false, &donating_states,
|
|
&donating_states_2, kMask);
|
|
field1_arena->Swap(field2_arena, &kDefaultValue, &arena, true, true,
|
|
&donating_states_1, &donating_states_2, kMask1);
|
|
EXPECT_EQ(field1.Get(), string2_heap);
|
|
EXPECT_EQ(field2.Get(), string1_heap);
|
|
EXPECT_EQ(field1_arena->Get(), string2_arena);
|
|
EXPECT_EQ(field2_arena->Get(), string1_arena);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace protobuf
|
|
} // namespace google
|