mirror of https://github.com/OpenTTD/OpenTTD
Codechange: Add InPlaceReplacement to couple StringConsumer and Builder on the same buffer.
parent
f5ffd4789b
commit
dc21fae18e
|
@ -27,6 +27,8 @@ add_files(
|
|||
string_builder.hpp
|
||||
string_consumer.cpp
|
||||
string_consumer.hpp
|
||||
string_inplace.cpp
|
||||
string_inplace.hpp
|
||||
strong_typedef_type.hpp
|
||||
utf8.cpp
|
||||
utf8.hpp
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file string_inplace.cpp Inplace-replacement of textual and binary data.
|
||||
*/
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "string_inplace.hpp"
|
||||
#include "../safeguards.h"
|
||||
|
||||
/**
|
||||
* Check whether any unused bytes are left between the Builder and Consumer position.
|
||||
*/
|
||||
[[nodiscard]] bool InPlaceBuilder::AnyBytesUnused() const noexcept
|
||||
{
|
||||
return this->consumer.GetBytesRead() > this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of unused bytes left between the Builder and Consumer position.
|
||||
*/
|
||||
[[nodiscard]] InPlaceBuilder::size_type InPlaceBuilder::GetBytesUnused() const noexcept
|
||||
{
|
||||
return this->consumer.GetBytesRead() - this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append buffer.
|
||||
*/
|
||||
void InPlaceBuilder::PutBuffer(const char *str, size_type len)
|
||||
{
|
||||
auto unused = this->GetBytesUnused();
|
||||
if (len > unused) NOT_REACHED();
|
||||
std::copy(str, str + len, this->dest.data() + this->position);
|
||||
this->position += len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create coupled Consumer+Builder pair.
|
||||
* @param buffer Data to consume and replace.
|
||||
* @note The lifetime of the buffer must exceed the lifetime of both the Consumer and the Builder.
|
||||
*/
|
||||
InPlaceReplacement::InPlaceReplacement(std::span<char> buffer)
|
||||
: consumer(buffer), builder(buffer, consumer)
|
||||
{
|
||||
}
|
||||
|
||||
InPlaceReplacement::InPlaceReplacement(const InPlaceReplacement &src)
|
||||
: consumer(src.consumer), builder(src.builder, consumer)
|
||||
{
|
||||
}
|
||||
|
||||
InPlaceReplacement& InPlaceReplacement::operator=(const InPlaceReplacement &src)
|
||||
{
|
||||
this->consumer = src.consumer;
|
||||
this->builder.AssignBuffer(src.builder);
|
||||
return *this;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file string_inplace.hpp Inplace-replacement of textual and binary data.
|
||||
*/
|
||||
|
||||
#ifndef STRING_INPLACE_HPP
|
||||
#define STRING_INPLACE_HPP
|
||||
|
||||
#include "string_builder.hpp"
|
||||
#include "string_consumer.hpp"
|
||||
|
||||
/**
|
||||
* Builder implementation for InPlaceReplacement.
|
||||
*/
|
||||
class InPlaceBuilder final : public BaseStringBuilder
|
||||
{
|
||||
std::span<char> dest;
|
||||
size_type position = 0;
|
||||
const StringConsumer &consumer;
|
||||
|
||||
friend class InPlaceReplacement;
|
||||
explicit InPlaceBuilder(std::span<char> dest, const StringConsumer &consumer) : dest(dest), consumer(consumer) {}
|
||||
InPlaceBuilder(const InPlaceBuilder &src, const StringConsumer &consumer) : dest(src.dest), position(src.position), consumer(consumer) {}
|
||||
void AssignBuffer(const InPlaceBuilder &src) { this->dest = src.dest; this->position = src.position; }
|
||||
public:
|
||||
InPlaceBuilder(const InPlaceBuilder &) = delete;
|
||||
InPlaceBuilder& operator=(const InPlaceBuilder &) = delete;
|
||||
|
||||
/**
|
||||
* Check whether any bytes have been written.
|
||||
*/
|
||||
[[nodiscard]] bool AnyBytesWritten() const noexcept { return this->position != 0; }
|
||||
/**
|
||||
* Get number of already written bytes.
|
||||
*/
|
||||
[[nodiscard]] size_type GetBytesWritten() const noexcept { return this->position; }
|
||||
/**
|
||||
* Get already written data.
|
||||
*/
|
||||
[[nodiscard]] std::string_view GetWrittenData() const noexcept { return {this->dest.data(), this->position}; }
|
||||
|
||||
[[nodiscard]] bool AnyBytesUnused() const noexcept;
|
||||
[[nodiscard]] size_type GetBytesUnused() const noexcept;
|
||||
|
||||
using BaseStringBuilder::PutBuffer;
|
||||
void PutBuffer(const char *str, size_type len) override;
|
||||
|
||||
/**
|
||||
* Implementation of std::back_insert_iterator for non-growing destination buffer.
|
||||
*/
|
||||
class back_insert_iterator {
|
||||
InPlaceBuilder *parent = nullptr;
|
||||
public:
|
||||
using value_type = void;
|
||||
using difference_type = void;
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using pointer = void;
|
||||
using reference = void;
|
||||
|
||||
back_insert_iterator(InPlaceBuilder &parent) : parent(&parent) {}
|
||||
|
||||
back_insert_iterator &operator++() { return *this; }
|
||||
back_insert_iterator operator++(int) { return *this; }
|
||||
back_insert_iterator &operator*() { return *this; }
|
||||
|
||||
back_insert_iterator &operator=(char value)
|
||||
{
|
||||
this->parent->PutChar(value);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Create a back-insert-iterator.
|
||||
*/
|
||||
back_insert_iterator back_inserter()
|
||||
{
|
||||
return back_insert_iterator(*this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose data into a fixed size buffer, which is consumed at the same time.
|
||||
* - The Consumer reads data from a buffer.
|
||||
* - The Builder writes data to the buffer, replacing already consumed data.
|
||||
* - The Builder asserts, if it overtakes the consumer.
|
||||
*/
|
||||
class InPlaceReplacement
|
||||
{
|
||||
public:
|
||||
StringConsumer consumer; ///< Consumer from shared buffer
|
||||
InPlaceBuilder builder; ///< Builder into shared buffer
|
||||
|
||||
public:
|
||||
InPlaceReplacement(std::span<char> buffer);
|
||||
InPlaceReplacement(const InPlaceReplacement &src);
|
||||
InPlaceReplacement& operator=(const InPlaceReplacement &src);
|
||||
};
|
||||
|
||||
#endif /* STRING_INPLACE_HPP */
|
|
@ -12,6 +12,7 @@ if (NOT HOST_BINARY_DIR)
|
|||
../string.cpp
|
||||
../core/string_builder.cpp
|
||||
../core/string_consumer.cpp
|
||||
../core/string_inplace.cpp
|
||||
../core/utf8.cpp
|
||||
)
|
||||
add_definitions(-DSETTINGSGEN)
|
||||
|
|
|
@ -14,6 +14,7 @@ if (NOT HOST_BINARY_DIR)
|
|||
../string.cpp
|
||||
../core/string_builder.cpp
|
||||
../core/string_consumer.cpp
|
||||
../core/string_inplace.cpp
|
||||
../core/utf8.cpp
|
||||
)
|
||||
add_definitions(-DSTRGEN)
|
||||
|
|
|
@ -9,6 +9,7 @@ add_test_files(
|
|||
mock_spritecache.h
|
||||
string_builder.cpp
|
||||
string_consumer.cpp
|
||||
string_inplace.cpp
|
||||
string_func.cpp
|
||||
test_main.cpp
|
||||
test_network_crypto.cpp
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file string_inplace.cpp Test functionality from core/string_inplace. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../3rdparty/catch2/catch.hpp"
|
||||
#include "../core/string_inplace.hpp"
|
||||
#include "../safeguards.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
TEST_CASE("InPlaceReplacement")
|
||||
{
|
||||
std::array<char, 4> buffer{1, 2, 3, 4};
|
||||
InPlaceReplacement inplace(buffer);
|
||||
|
||||
CHECK(!inplace.builder.AnyBytesWritten());
|
||||
CHECK(inplace.builder.GetBytesWritten() == 0);
|
||||
CHECK(inplace.builder.GetWrittenData() == ""sv);
|
||||
CHECK(!inplace.builder.AnyBytesUnused());
|
||||
CHECK(inplace.builder.GetBytesUnused() == 0);
|
||||
CHECK(!inplace.consumer.AnyBytesRead());
|
||||
CHECK(inplace.consumer.GetBytesRead() == 0);
|
||||
CHECK(inplace.consumer.AnyBytesLeft());
|
||||
CHECK(inplace.consumer.GetBytesLeft() == 4);
|
||||
|
||||
CHECK(inplace.consumer.ReadUint16LE() == 0x201);
|
||||
|
||||
CHECK(inplace.builder.GetBytesWritten() == 0);
|
||||
CHECK(inplace.builder.GetBytesUnused() == 2);
|
||||
CHECK(inplace.consumer.GetBytesRead() == 2);
|
||||
CHECK(inplace.consumer.GetBytesLeft() == 2);
|
||||
|
||||
inplace.builder.PutUint8(11);
|
||||
|
||||
CHECK(inplace.builder.GetBytesWritten() == 1);
|
||||
CHECK(inplace.builder.GetBytesUnused() == 1);
|
||||
CHECK(inplace.consumer.GetBytesRead() == 2);
|
||||
CHECK(inplace.consumer.GetBytesLeft() == 2);
|
||||
|
||||
inplace.builder.PutUint8(12);
|
||||
|
||||
CHECK(inplace.builder.GetBytesWritten() == 2);
|
||||
CHECK(inplace.builder.GetBytesUnused() == 0);
|
||||
CHECK(inplace.consumer.GetBytesRead() == 2);
|
||||
CHECK(inplace.consumer.GetBytesLeft() == 2);
|
||||
|
||||
CHECK(buffer[0] == 11);
|
||||
CHECK(buffer[1] == 12);
|
||||
CHECK(buffer[2] == 3);
|
||||
CHECK(buffer[3] == 4);
|
||||
}
|
Loading…
Reference in New Issue