ladybird/Userland/Libraries/LibSQL/Serializer.h
Jan de Visser 85a84b0794 LibSQL: Introduce Serializer as a mediator between Heap and client code
Classes reading and writing to the data heap would communicate directly
with the Heap object, and transfer ByteBuffers back and forth with it.
This makes things like caching and locking hard. Therefore all data
persistence activity will be funneled through a Serializer object which
in turn submits it to the Heap.

Introducing this unfortunately resulted in a huge amount of churn, in
which a number of smaller refactorings got caught up as well.
2021-08-21 22:03:30 +02:00

177 lines
4.0 KiB
C++

/*
* Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/Debug.h>
#include <AK/Format.h>
#include <AK/ScopeGuard.h>
#include <AK/String.h>
#include <LibSQL/Forward.h>
#include <LibSQL/Heap.h>
#include <string.h>
namespace SQL {
class Serializer {
public:
Serializer() = default;
Serializer(RefPtr<Heap> heap)
: m_heap(heap)
{
}
void get_block(u32 pointer)
{
VERIFY(m_heap.ptr() != nullptr);
auto buffer_or_error = m_heap->read_block(pointer);
if (buffer_or_error.is_error())
VERIFY_NOT_REACHED();
m_buffer = buffer_or_error.value();
m_current_offset = 0;
}
void reset()
{
m_buffer.clear();
m_current_offset = 0;
}
void rewind()
{
m_current_offset = 0;
}
template<typename T, typename... Args>
T deserialize_block(u32 pointer, Args&&... args)
{
get_block(pointer);
return deserialize<T>(forward<Args>(args)...);
}
template<typename T>
void deserialize_block_to(u32 pointer, T& t)
{
get_block(pointer);
return deserialize_to<T>(t);
}
template<typename T>
void deserialize_to(T& t)
{
if constexpr (IsArithmetic<T>)
memcpy(&t, read(sizeof(T)), sizeof(T));
else
t.deserialize(*this);
}
void deserialize_to(String& text);
template<typename T, typename... Args>
NonnullOwnPtr<T> make_and_deserialize(Args&&... args)
{
auto ptr = make<T>(forward<Args>(args)...);
ptr->deserialize(*this);
return ptr;
}
template<typename T, typename... Args>
NonnullRefPtr<T> adopt_and_deserialize(Args&&... args)
{
auto ptr = adopt_ref(*new T(forward<Args>(args)...));
ptr->deserialize(*this);
return ptr;
}
template<typename T, typename... Args>
T deserialize(Args&&... args)
{
T t(forward<Args>(args)...);
deserialize_to(t);
return t;
}
template<typename T>
void serialize(T const& t)
{
if constexpr (IsArithmetic<T>)
write((u8 const*)(&t), sizeof(T));
else
t.serialize(*this);
}
void serialize(String const&);
template<typename T>
bool serialize_and_write(T const& t, u32 pointer)
{
VERIFY(m_heap.ptr() != nullptr);
reset();
serialize<T>(t);
m_heap->add_to_wal(pointer, m_buffer);
return true;
}
[[nodiscard]] size_t offset() const { return m_current_offset; }
u32 new_record_pointer()
{
VERIFY(m_heap.ptr() != nullptr);
return m_heap->new_record_pointer();
}
bool has_block(u32 pointer) const
{
VERIFY(m_heap.ptr() != nullptr);
return pointer < m_heap->size();
}
Heap& heap()
{
VERIFY(m_heap.ptr() != nullptr);
return *(m_heap.ptr());
}
private:
void write(u8 const* ptr, size_t sz)
{
if constexpr (SQL_DEBUG)
dump(ptr, sz, "(out) =>");
m_buffer.append(ptr, sz);
m_current_offset += sz;
}
u8 const* read(size_t sz)
{
auto buffer_ptr = m_buffer.offset_pointer((int)m_current_offset);
if constexpr (SQL_DEBUG)
dump(buffer_ptr, sz, "<= (in)");
m_current_offset += sz;
return buffer_ptr;
}
static void dump(u8 const* ptr, size_t sz, String const& prefix)
{
StringBuilder builder;
builder.appendff("{0} {1:04x} | ", prefix, sz);
Vector<String> bytes;
for (auto ix = 0u; ix < sz; ++ix) {
bytes.append(String::formatted("{0:02x}", *(ptr + ix)));
}
StringBuilder bytes_builder;
bytes_builder.join(" ", bytes);
builder.append(bytes_builder.to_string());
dbgln(builder.to_string());
}
ByteBuffer m_buffer {};
size_t m_current_offset { 0 };
RefPtr<Heap> m_heap { nullptr };
};
}