Compare commits

...

8 Commits

Author SHA1 Message Date
419604a4d2
libluna: Document Buffer
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-28 11:10:04 +02:00
97037b06cb
libluna: Document Ignore.h and ImplPOSIX.cpp 2023-08-27 20:50:53 +02:00
c2f173f584
libc: Call __builtin_trap() in abort() if all else fails 2023-08-27 20:49:18 +02:00
a772d92e6f
libluna: Fix initial allocation for HashTable 2023-08-27 20:48:50 +02:00
6f3ed70363
kernel+libluna: Avoid scrubbing when the memory is going to be overwritten anyway
This is the case for objects with constructors and temporary memory which is filled afterwards.
2023-08-27 20:48:33 +02:00
d48142f163
libluna: Document HashMap and HashTable 2023-08-26 20:50:12 +02:00
cbea66c533
libos+libluna: Fix misspellings of "succeeded" 2023-08-26 20:49:57 +02:00
c6d817a0fd
libluna: Document Hash.h 2023-08-26 20:31:16 +02:00
14 changed files with 358 additions and 17 deletions

View File

@ -12,7 +12,7 @@ Result<u64> sys_poll(Registers*, SyscallArgs args)
nfds_t nfds = (nfds_t)args[1];
int timeout = (int)args[2];
struct pollfd* kfds = (struct pollfd*)TRY(malloc_impl(nfds * sizeof(pollfd)));
struct pollfd* kfds = (struct pollfd*)TRY(malloc_impl(nfds * sizeof(pollfd), false, false));
auto guard = make_scope_guard([kfds] { free_impl(kfds); });
if (!MemoryManager::copy_from_user(fds, kfds, nfds * sizeof(pollfd))) return err(EFAULT);

View File

@ -117,9 +117,7 @@ extern "C"
raise(SIGABRT);
// There is no way we could end up here, unless there is some sort of race condition or the kernel decided to
// change the default action for SIGABRT because it's a Tuesday.
__builtin_unreachable();
__builtin_trap();
}
__noreturn void _Exit(int status)

View File

@ -24,7 +24,7 @@
*/
template <typename T, class... Args> [[nodiscard]] Result<T*> make(Args... args)
{
T* const result = (T*)TRY(malloc_impl(sizeof(T)));
T* const result = (T*)TRY(malloc_impl(sizeof(T), false, false));
new (result) T(args...);
return result;
}

View File

@ -1,7 +1,19 @@
/**
* @file Buffer.h
* @author apio (cloudapio.eu)
* @brief A managed wrapper around a resizable buffer of arbitrary memory.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <luna/Result.h>
#include <luna/Types.h>
/**
* @brief A managed wrapper around a resizable buffer of arbitrary memory.
*/
class Buffer
{
public:
@ -11,45 +23,135 @@ class Buffer
Buffer(Buffer&& other);
Buffer(const Buffer& other) = delete; // For now.
/**
* @brief Create a Buffer object, allocating a specific amount of memory for it.
*
* @param size The number of bytes to allocate.
* @return Result<Buffer> An error, or the newly created buffer.
*/
static Result<Buffer> create_sized(usize size);
/**
* @brief Resize the buffer.
*
* The existing data is kept, unless the new size is smaller than the old size, in which case only the first
* new_size bytes are kept.
*
* @param new_size The new size of the buffer, in bytes.
* @return Result<void> Whether the operation succeeded.
*/
Result<void> try_resize(usize new_size);
/**
* @brief Expand the buffer and return a pointer to the beginning of this new expanded area.
*
* @param size The amount of bytes to expand the buffer by.
* @return Result<u8*> An error, or a pointer to the new area of the buffer.
*/
Result<u8*> slice_at_end(usize size);
/**
* @brief Return a pointer to an area of the buffer, expanding it if necessary.
*
* @param offset The offset inside the buffer to start at.
* @param size The amount of bytes to reserve.
* @return Result<u8*> An error, or a pointer to the requested area.
*/
Result<u8*> slice(usize offset, usize size);
/**
* @brief Add data to the end of the buffer.
*
* @param data A pointer to the data to add.
* @param size The amount of bytes to add.
* @return Result<void> Whether the operation succeeded.
*/
Result<void> append_data(const u8* data, usize size);
/**
* @brief Remove data from the beginning of the buffer and return it.
*
* @param data A pointer to store the removed data in.
* @param size The amount of bytes to remove.
* @return usize The amount of bytes actually removed (may be less if the buffer was smaller than the requested
* size).
*/
usize dequeue_data(u8* data, usize size);
/**
* @brief Return a pointer to the data contained in the buffer.
*
* This pointer may be invalid after the buffer is resized.
*
* @return u8* The contained data.
*/
u8* data()
{
return m_data;
}
/**
* @brief Return a pointer to the data contained in the buffer.
*
* This pointer may be invalid after the buffer is resized.
*
* @return const u8* The contained data.
*/
const u8* data() const
{
return m_data;
}
/**
* @brief Return a pointer to the data contained in the buffer, moving the data out of the buffer.
*
* This call will empty the buffer, making it the caller's responsibility to manage the data, including freeing it
* when no longer used.
*
* @return u8* The released data.
*/
u8* release_data();
/**
* @brief Return a pointer to the end of the data contained in the buffer.
*
* This pointer points past the data; as such, dereferencing it directly is undefined behavior.
*
* @return u8* The end of the data.
*/
u8* end()
{
return m_data + m_size;
}
/**
* @brief Return a pointer to the end of the data contained in the buffer.
*
* This pointer points past the data; as such, dereferencing it directly is undefined behavior.
*
* @return const u8* The end of the data.
*/
const u8* end() const
{
return m_data + m_size;
}
/**
* @brief Return the size of the buffer in bytes.
*
* @return usize The buffer's size.
*/
usize size() const
{
return m_size;
}
/**
* @brief Check whether the buffer is empty.
*
* @return true The buffer is empty.
* @return false The buffer is not empty.
*/
bool is_empty() const
{
return m_size == 0;

View File

@ -46,7 +46,7 @@ template <typename T, usize Size> class CircularQueue
* @brief Push a value onto the queue.
*
* @param value The value to push.
* @return true The operation succeded.
* @return true The operation succeeded.
* @return false The queue was full or someone else was trying to push a value at the same time.
*/
bool try_push(const T& value)
@ -71,7 +71,7 @@ template <typename T, usize Size> class CircularQueue
* @brief Pop a value from the queue.
*
* @param value The variable to store the value into.
* @return true The operation succeded.
* @return true The operation succeeded.
* @return false The queue was empty or someone else was trying to pop a value at the same time.
*/
bool try_pop(T& value)
@ -136,7 +136,7 @@ template <typename T> class DynamicCircularQueue
* be called once to set the initial size of the queue and that's it.
*
* @param size The amount of elements to make space for.
* @return Result<void> Whether the operation succeded.
* @return Result<void> Whether the operation succeeded.
*/
Result<void> set_size(usize size)
{
@ -150,7 +150,7 @@ template <typename T> class DynamicCircularQueue
* @brief Push a value onto the queue.
*
* @param value The value to push.
* @return true The operation succeded.
* @return true The operation succeeded.
* @return false The queue was full or someone else was trying to push a value at the same time.
*/
bool try_push(const T& value)
@ -176,7 +176,7 @@ template <typename T> class DynamicCircularQueue
* @brief Pop a value from the queue.
*
* @param value The variable to store the value into.
* @return true The operation succeded.
* @return true The operation succeeded.
* @return false The queue was empty or someone else was trying to pop a value at the same time.
*/
bool try_pop(T& value)

View File

@ -1,16 +1,61 @@
/**
* @file Hash.h
* @author apio (cloudapio.eu)
* @brief Common hash functions for use in hash tables.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <luna/CString.h>
#include <luna/Types.h>
/**
* @brief Calculate the hash of an area of memory.
*
* @param mem A pointer to the memory to hash.
* @param size The amount of bytes to use.
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
* @return u64 The calculated hash.
*/
u64 hash_memory(const void* mem, usize size, u64 salt);
/**
* @brief Calculate the hash of a value.
*
* The default implementation simply hashes the raw memory representation of the value. This may not be suitable for
* some types, so those can define a template specialization of this function to do their own hashing.
*
* @tparam T The type of the value to hash.
* @param value The value to hash.
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
* @return u64 The calculated hash.
*/
template <typename T> u64 hash(const T& value, u64 salt)
{
return hash_memory(&value, sizeof(value), salt);
}
/**
* @brief Template specialization of hash() for C-strings.
*
* This function hashes the actual string instead of the pointer value.
*
* @param value The C-string to hash.
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
* @return u64 The calculated hash.
*/
template <> u64 hash(const char* const& value, u64 salt);
/**
* @brief Swap two values of the same type.
*
* FIXME: This function should be moved to a more appropriate header.
*
* @tparam T The type of the values to swap.
* @param a The first value to swap.
* @param b The second value to swap.
*/
template <typename T> static void swap(T* a, T* b)
{
char* x = (char*)a;

View File

@ -1,35 +1,94 @@
/**
* @file HashMap.h
* @author apio (cloudapio.eu)
* @brief Map between keys and values with best-case constant time lookup.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <luna/HashTable.h>
/**
* @brief Internal representation of a key-value pair in a HashMap.
*
* @tparam K The key type.
* @tparam V The value type.
*/
template <typename K, typename V> struct HashPair
{
K key;
Option<V> value;
/**
* @brief Compare two HashPair objects's keys.
*
* @param other The HashPair to compare against.
* @return true The keys match.
* @return false The keys do not match.
*/
bool operator==(const HashPair<K, V>& other) const
{
return key == other.key;
}
};
/**
* @brief Template specialization of hash() for HashPair objects.
*
* This function hashes only the key, ignoring the value.
*
* @tparam K The key type.
* @tparam V The value type.
* @param value The HashPair to hash.
* @param salt A randomly generated salt to vary the output and avoid hash table attacks.
* @return u64 The calculated hash.
*/
template <typename K, typename V> u64 hash(const HashPair<K, V>& value, u64 salt)
{
return hash(value.key, salt);
}
/**
* @brief A map between keys and values with best-case constant time lookup.
*
* @tparam K The key type.
* @tparam V The value type.
*/
template <typename K, typename V> struct HashMap
{
public:
/**
* @brief Try to insert a key-value pair into the HashMap.
*
* @param key The key to use.
* @param value The value to use.
* @return Result<bool> An error, true if the insertion succeeded, or false if the key already existed.
*/
Result<bool> try_set(const K& key, V&& value)
{
return m_table.try_set(HashPair<K, V> { key, move(value) });
}
/**
* @brief Try to insert a key-value pair into the HashMap.
*
* @param key The key to use.
* @param value The value to use.
* @return Result<bool> An error, true if the insertion succeeded, or false if the key already existed.
*/
Result<bool> try_set(const K& key, const V& value)
{
return m_table.try_set(HashPair<K, V> { key, value });
}
/**
* @brief Get the associated value for a key.
*
* @param key The key to use.
* @return Option<V> The associated value, or an empty option if the key did not exist.
*/
Option<V> try_get(const K& key)
{
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
@ -37,6 +96,14 @@ template <typename K, typename V> struct HashMap
return p->value;
}
/**
* @brief Get a pointer to the associated value for a key inside the HashMap.
*
* This pointer should always be checked before usage.
*
* @param key The key to use.
* @return V* A pointer to the associated value, or nullptr if the key did not exist.
*/
V* try_get_ref(const K& key)
{
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
@ -44,21 +111,44 @@ template <typename K, typename V> struct HashMap
return p->value.value_ptr();
}
/**
* @brief Remove a key-value pair from this HashMap.
*
* @param key The key to use.
* @return true The pair was successfully removed.
* @return false The key did not exist.
*/
bool try_remove(const K& key)
{
return m_table.try_remove(HashPair<K, V> { key, {} });
}
/**
* @brief Return the number of key-value pairs that can currently fit in the HashMap.
*
* The number of actual entries will always be smaller than this (unless it is 0), since the HashMap grows before
* the number of entries reaches the capacity.
*
* @return usize The current capacity.
*/
usize capacity() const
{
return m_table.capacity();
}
/**
* @brief Return the number of key-value pairs currently contained in this HashMap.
*
* @return usize The number of pairs contained.
*/
usize size() const
{
return m_table.size();
}
/**
* @brief Clear the HashMap.
*/
void clear()
{
m_table.clear();

View File

@ -1,23 +1,50 @@
/**
* @file HashTable.h
* @author apio (cloudapio.eu)
* @brief Table of values with best-case constant time lookup.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <luna/Hash.h>
#include <luna/Heap.h>
#include <luna/Option.h>
/**
* @brief A table of values with best-case constant time lookup.
*
* @tparam T The type of values to store.
*/
template <typename T> class HashTable
{
static constexpr usize GROW_RATE = 2;
static constexpr usize GROW_FACTOR = 16;
static constexpr usize GROW_FACTOR = 2;
static constexpr usize INITIAL_CAPACITY = 32;
public:
/**
* @brief Try to insert a value into the table.
*
* @param value The value to insert.
* @return Result<bool> An error, true if the insertion succeeded, or false if the value already existed.
*/
Result<bool> try_set(const T& value)
{
T copy { value };
return try_set(move(copy));
}
/**
* @brief Try to insert a value into the table.
*
* @param value The value to insert.
* @return Result<bool> An error, true if the insertion succeeded, or false if the value already existed.
*/
Result<bool> try_set(T&& value)
{
if (should_grow()) TRY(rehash(m_capacity + GROW_FACTOR));
if (should_grow()) TRY(rehash(m_capacity ? m_capacity * GROW_FACTOR : INITIAL_CAPACITY));
u64 index = hash(value, m_salt) % m_capacity;
@ -45,6 +72,12 @@ template <typename T> class HashTable
return true;
}
/**
* @brief Find a value inside the table, returning a pointer to it if found.
*
* @param value The value to compare against.
* @return T* A pointer to the value inside the table if found, or nullptr if it was not found.
*/
T* try_find(const T& value)
{
if (!m_size) return nullptr;
@ -69,6 +102,13 @@ template <typename T> class HashTable
return nullptr;
}
/**
* @brief Remove a value from this table.
*
* @param value The value to remove.
* @return true The value was successfully removed.
* @return false The value did not exist.
*/
bool try_remove(const T& value)
{
if (!m_size) return false;
@ -99,6 +139,9 @@ template <typename T> class HashTable
return false;
}
/**
* @brief Clear the table.
*/
void clear()
{
if (m_capacity)
@ -110,11 +153,24 @@ template <typename T> class HashTable
}
}
/**
* @brief Return the number of values that can currently fit in the table.
*
* The number of actual entries will always be smaller than this (unless it is 0), since the table grows before
* the number of entries reaches the capacity.
*
* @return usize The current capacity.
*/
usize capacity() const
{
return m_capacity;
}
/**
* @brief Return the number of values currently contained in the table.
*
* @return usize The number of values contained.
*/
usize size() const
{
return m_size;

View File

@ -1,5 +1,29 @@
/**
* @file Ignore.h
* @author apio (cloudapio.eu)
* @brief Do nothing.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
/**
* @brief Do nothing.
*
* Calling
* ignore(a, b, c);
* is a more compact equivalent of doing:
* (void)a;
* (void)b;
* (void)c;
*
* This function is used to discard unused variables avoiding compiler warnings, if you know they'll be used in the
* future.
*
* @tparam Args The list of ignored variable types.
*/
template <class... Args> constexpr void ignore(Args...)
{
}

View File

@ -1,3 +1,12 @@
/**
* @file Buffer.cpp
* @author apio (cloudapio.eu)
* @brief A managed wrapper around a resizable buffer of arbitrary memory.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <luna/Buffer.h>
#include <luna/CString.h>
#include <luna/Heap.h>

View File

@ -1,3 +1,13 @@
/**
* @file Hash.cpp
* @author apio (cloudapio.eu)
* @brief Common hash functions for use in hash tables.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <luna/CString.h>
#include <luna/Hash.h>
u64 hash_memory(const void* mem, usize size, u64 salt)

View File

@ -425,12 +425,12 @@ void dump_heap_usage()
#ifdef USE_FREESTANDING
void* operator new(usize size, const std::nothrow_t&) noexcept
{
return malloc_impl(size).value_or(nullptr);
return malloc_impl(size, false, false).value_or(nullptr);
}
void* operator new[](usize size, const std::nothrow_t&) noexcept
{
return malloc_impl(size).value_or(nullptr);
return malloc_impl(size, false, false).value_or(nullptr);
}
void operator delete(void* p) noexcept

View File

@ -1,4 +1,11 @@
/* POSIX userspace implementation of libluna hooks. */
/**
* @file ImplPOSIX.cpp
* @author apio (cloudapio.eu)
* @brief POSIX userspace implementation of libluna hooks.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <luna/Attributes.h>
#include <luna/Result.h>

View File

@ -41,7 +41,7 @@ namespace os
* _exit(2). If NULL, the promises are not changed.
* @param execpromises The promises to apply on the next call to execve(2), separated by spaces. If empty, the
* process may only call _exit(2). If NULL, the execpromises are not changed.
* @return Result<void> Whether the operation succeded.
* @return Result<void> Whether the operation succeeded.
*/
Result<void> pledge(const char* promises, const char* execpromises);
}