1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 07:54:58 +00:00
serenity/Userland/Applications/HexEditor/HexDocument.cpp
Tim Schumacher d5871f5717 AK: Rename Stream::{read,write} to Stream::{read_some,write_some}
Similar to POSIX read, the basic read and write functions of AK::Stream
do not have a lower limit of how much data they read or write (apart
from "none at all").

Rename the functions to "read some [data]" and "write some [data]" (with
"data" being omitted, since everything here is reading and writing data)
to make them sufficiently distinct from the functions that ensure to
use the entire buffer (which should be the go-to function for most
usages).

No functional changes, just a lot of new FIXMEs.
2023-03-13 15:16:20 +00:00

253 lines
6.4 KiB
C++

/*
* Copyright (c) 2021, Arne Elster <arne@elster.li>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "HexDocument.h"
#include <AK/TypeCasts.h>
#include <LibCore/File.h>
void HexDocument::set(size_t position, u8 value)
{
auto unchanged_value = get_unchanged(position);
if (value == unchanged_value) {
m_changes.remove(position);
} else {
m_changes.set(position, value);
}
}
bool HexDocument::is_dirty() const
{
return m_changes.size() > 0;
}
HexDocumentMemory::HexDocumentMemory(ByteBuffer&& buffer)
: m_buffer(move(buffer))
{
}
HexDocument::Cell HexDocumentMemory::get(size_t position)
{
auto tracked_change = m_changes.get(position);
if (tracked_change.has_value()) {
return Cell { tracked_change.value(), true };
} else {
return Cell { m_buffer[position], false };
}
}
u8 HexDocumentMemory::get_unchanged(size_t position)
{
return m_buffer[position];
}
size_t HexDocumentMemory::size() const
{
return m_buffer.size();
}
HexDocument::Type HexDocumentMemory::type() const
{
return Type::Memory;
}
void HexDocumentMemory::clear_changes()
{
m_changes.clear();
}
ErrorOr<void> HexDocumentMemory::write_to_file(Core::File& file)
{
TRY(file.seek(0, SeekMode::SetPosition));
// FIXME: This should write the entire span.
TRY(file.write_some(m_buffer));
for (auto& change : m_changes) {
TRY(file.seek(change.key, SeekMode::SetPosition));
// FIXME: This should write the entire span.
TRY(file.write_some({ &change.value, 1 }));
}
return {};
}
ErrorOr<NonnullOwnPtr<HexDocumentFile>> HexDocumentFile::create(NonnullOwnPtr<Core::File> file)
{
auto document = TRY(adopt_nonnull_own_or_enomem(new HexDocumentFile(move(file))));
TRY(document->initialize_internal_state());
return document;
}
HexDocumentFile::HexDocumentFile(NonnullOwnPtr<Core::File> file)
: m_file(move(file))
{
}
ErrorOr<void> HexDocumentFile::write_to_file()
{
for (auto& change : m_changes) {
TRY(m_file->seek(change.key, SeekMode::SetPosition));
// FIXME: This should write the entire span.
TRY(m_file->write_some({ &change.value, 1 }));
}
clear_changes();
// make sure the next get operation triggers a read
m_buffer_file_pos = m_file_size + 1;
return {};
}
ErrorOr<void> HexDocumentFile::write_to_file(Core::File& file)
{
TRY(file.truncate(size()));
TRY(file.seek(0, SeekMode::SetPosition));
TRY(m_file->seek(0, SeekMode::SetPosition));
while (true) {
Array<u8, 64 * KiB> buffer;
auto copy_buffer = TRY(m_file->read_some(buffer));
if (copy_buffer.size() == 0)
break;
// FIXME: This should write the entire span.
TRY(file.write_some(copy_buffer));
}
for (auto& change : m_changes) {
TRY(file.seek(change.key, SeekMode::SetPosition));
// FIXME: This should write the entire span.
TRY(file.write_some({ &change.value, 1 }));
}
return {};
}
HexDocument::Cell HexDocumentFile::get(size_t position)
{
auto tracked_change = m_changes.get(position);
if (tracked_change.has_value()) {
return Cell { tracked_change.value(), true };
}
ensure_position_in_buffer(position);
return { m_buffer[position - m_buffer_file_pos], false };
}
u8 HexDocumentFile::get_unchanged(size_t position)
{
ensure_position_in_buffer(position);
return m_buffer[position - m_buffer_file_pos];
}
size_t HexDocumentFile::size() const
{
return m_file_size;
}
HexDocument::Type HexDocumentFile::type() const
{
return Type::File;
}
void HexDocumentFile::clear_changes()
{
m_changes.clear();
}
ErrorOr<void> HexDocumentFile::set_file(NonnullOwnPtr<Core::File> file)
{
m_file = move(file);
TRY(initialize_internal_state());
return {};
}
ErrorOr<void> HexDocumentFile::initialize_internal_state()
{
if (auto result = m_file->seek(0, SeekMode::FromEndPosition); result.is_error())
m_file_size = 0;
else
m_file_size = result.value();
TRY(m_file->seek(0, SeekMode::SetPosition));
clear_changes();
// make sure the next get operation triggers a read
m_buffer_file_pos = m_file_size + 1;
return {};
}
NonnullOwnPtr<Core::File> const& HexDocumentFile::file() const
{
return m_file;
}
void HexDocumentFile::ensure_position_in_buffer(size_t position)
{
if (position < m_buffer_file_pos || position >= m_buffer_file_pos + m_buffer.size()) {
m_file->seek(position, SeekMode::SetPosition).release_value_but_fixme_should_propagate_errors();
// FIXME: This seems wrong. We don't track how much of the buffer is actually filled.
m_file->read_some(m_buffer).release_value_but_fixme_should_propagate_errors();
m_buffer_file_pos = position;
}
}
HexDocumentUndoCommand::HexDocumentUndoCommand(WeakPtr<HexDocument> document, size_t position)
: m_document(move(document))
, m_position(position)
{
}
void HexDocumentUndoCommand::undo()
{
for (size_t i = 0; i < m_old.size(); i++)
m_document->set(m_position + i, m_old[i]);
}
void HexDocumentUndoCommand::redo()
{
for (size_t i = 0; i < m_new.size(); i++)
m_document->set(m_position + i, m_new[i]);
}
bool HexDocumentUndoCommand::merge_with(GUI::Command const& other)
{
if (!is<HexDocumentUndoCommand>(other) || commit_time_expired())
return false;
auto const& typed_other = static_cast<HexDocumentUndoCommand const&>(other);
size_t relative_start = typed_other.m_position - m_position;
size_t other_length = typed_other.m_old.size();
size_t length = m_old.size();
if (typed_other.m_position < m_position || m_position + length < typed_other.m_position)
return false;
m_old.resize(relative_start + other_length);
m_new.resize(relative_start + other_length);
for (size_t i = 0; i < other_length; i++) {
m_new[relative_start + i] = typed_other.m_new[i];
if (relative_start + i >= length)
m_old[relative_start + i] = typed_other.m_old[i];
}
m_timestamp = Time::now_monotonic();
return true;
}
ErrorOr<void> HexDocumentUndoCommand::try_add_changed_byte(u8 old_value, u8 new_value)
{
TRY(m_old.try_append(old_value));
TRY(m_new.try_append(new_value));
return {};
}
ErrorOr<void> HexDocumentUndoCommand::try_add_changed_bytes(ByteBuffer old_values, ByteBuffer new_values)
{
TRY(m_old.try_append(move(old_values)));
TRY(m_new.try_append(move(new_values)));
return {};
}