mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
Compare commits
154 commits
351b33a64e
...
cf70aef726
Author | SHA1 | Date | |
---|---|---|---|
|
cf70aef726 | ||
|
001df24935 | ||
|
b60cb699a9 | ||
|
8d13115d9a | ||
|
cd14b215d1 | ||
|
35764db0b7 | ||
|
caf7983039 | ||
|
cdb54fe504 | ||
|
7f989765f5 | ||
|
41c172c663 | ||
|
ac5699c8fc | ||
|
ce26e5d757 | ||
|
3a2cc1aa20 | ||
|
0448d4d609 | ||
|
a1687854ab | ||
|
d1120e1809 | ||
|
248e4bb517 | ||
|
b4ba65c6e5 | ||
|
009f328308 | ||
|
d55caff227 | ||
|
570c5f8df2 | ||
|
d6bcd3fb0b | ||
|
d527c5df5d | ||
|
13f349aea2 | ||
|
84f673515b | ||
|
4e48298414 | ||
|
bf5cf720b5 | ||
|
488034477a | ||
|
f57ff63432 | ||
|
c715711f88 | ||
|
5689621c2b | ||
|
4742775262 | ||
|
a80523be18 | ||
|
55c81482b0 | ||
|
dfaa3bf649 | ||
|
5fe0d3352d | ||
|
eca378a7a3 | ||
|
e4e05837e1 | ||
|
c8d2404230 | ||
|
d368fcadac | ||
|
f7517c5b8d | ||
|
b94307583b | ||
|
e236f1d2ae | ||
|
63a5717bc7 | ||
|
c5afe58540 | ||
|
3bcd91b109 | ||
|
7d1291b9f0 | ||
|
6911c45bab | ||
|
879ae94183 | ||
|
7e20f4726f | ||
|
7f72c28e78 | ||
|
d704b61066 | ||
|
b93d8ef875 | ||
|
8a07131229 | ||
|
063cd68bf4 | ||
|
f638f84185 | ||
|
4203b7823f | ||
|
cd446e5e9c | ||
|
ab0dc83d28 | ||
|
6fc06f45c2 | ||
|
e98e9b8e81 | ||
|
4b1deb6fe1 | ||
|
356507284e | ||
|
ec5ea0d686 | ||
|
b3c253e50f | ||
|
d0c0db5bb3 | ||
|
b99a3ec2df | ||
|
74b27d620d | ||
|
dabf3da7e5 | ||
|
11460b3daa | ||
|
43056a8684 | ||
|
a423493dd8 | ||
|
77d205571d | ||
|
f09ed59351 | ||
|
866609c682 | ||
|
3468a83e45 | ||
|
4a731b3858 | ||
|
ddd15e96b6 | ||
|
61d52c8a3f | ||
|
33e7d6121b | ||
|
d87144fde2 | ||
|
69f5f40617 | ||
|
a828a0e158 | ||
|
4b4a6991e3 | ||
|
fca6fd0b85 | ||
|
829391e714 | ||
|
726f2cfb11 | ||
|
32cf4d1e29 | ||
|
d5fb48a6f5 | ||
|
458167935c | ||
|
a95f761cb4 | ||
|
6033349574 | ||
|
ed409eacf5 | ||
|
6ffc7ea36d | ||
|
564dc0a434 | ||
|
24a6fd3d76 | ||
|
c47d19d05a | ||
|
a820308a02 | ||
|
b342758dbf | ||
|
801499f13e | ||
|
135daeb8bb | ||
|
be09893fa7 | ||
|
8bd394f349 | ||
|
ed76e1ed4b | ||
|
dd6acfecd4 | ||
|
f88826691c | ||
|
59e0b7ccb7 | ||
|
8cd514d83c | ||
|
7fe110225b | ||
|
96a35767b6 | ||
|
7b7bb60393 | ||
|
e28e4f6700 | ||
|
9f541c363d | ||
|
ed80e929e5 | ||
|
66530086a4 | ||
|
ff791a63fc | ||
|
3856dd946b | ||
|
c898ee90cf | ||
|
8d511b2f7b | ||
|
3e8c8b185e | ||
|
5bcba896c2 | ||
|
638a8aecad | ||
|
c0da3e356a | ||
|
35ba7c7e00 | ||
|
5a2260a0bc | ||
|
fd15910adf | ||
|
355ce72c06 | ||
|
b11fdea175 | ||
|
a91af764f6 | ||
|
077ae6efa1 | ||
|
6affbf78c2 | ||
|
fd0c63b338 | ||
|
e8c228fb93 | ||
|
634823d5b4 | ||
|
a4b43cae9a | ||
|
061ac1f8c7 | ||
|
5a4d657a4e | ||
|
b264d18ad1 | ||
|
00bc22c332 | ||
|
69c84d3f63 | ||
|
aa9ed71ff3 | ||
|
0339ece565 | ||
|
98dadb0ce6 | ||
|
8d93cac983 | ||
|
ab309dcc58 | ||
|
ed7ec7a0f8 | ||
|
13bd52249d | ||
|
c3783cf3bd | ||
|
63873f3809 | ||
|
2173219eac | ||
|
0b9c4b8adc | ||
|
596a4e55dd | ||
|
f949334a9a | ||
|
a59d9a3986 |
963 changed files with 21167 additions and 32738 deletions
|
@ -23,6 +23,14 @@ extension Swift.String {
|
|||
}
|
||||
}
|
||||
|
||||
extension AK.String {
|
||||
public init(swiftString: consuming Swift.String) {
|
||||
self.init() // Create empty string first, using default constructor
|
||||
swiftString.withUTF8 { buffer in
|
||||
self = AK.String.from_utf8_without_validation(AK.ReadonlyBytes(buffer.baseAddress!, buffer.count))
|
||||
}
|
||||
}
|
||||
}
|
||||
extension AK.StringView: ExpressibleByStringLiteral {
|
||||
public typealias StringLiteralType = Swift.StaticString
|
||||
|
||||
|
|
|
@ -9,10 +9,8 @@
|
|||
#include <AK/BitmapView.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Try.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/kmalloc.h>
|
||||
|
||||
|
|
|
@ -50,6 +50,10 @@
|
|||
# cmakedefine01 CSS_TRANSITIONS_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef DNS_DEBUG
|
||||
# cmakedefine01 DNS_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef EDITOR_DEBUG
|
||||
# cmakedefine01 EDITOR_DEBUG
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,15 @@ public:
|
|||
m_data[i] = data[i];
|
||||
}
|
||||
|
||||
constexpr IPv6Address(Array<u8, 16> const& data)
|
||||
{
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
m_data[i] = data[i];
|
||||
}
|
||||
|
||||
template<SameAs<char const*> T>
|
||||
constexpr IPv6Address(T const&) = delete; // Disable implicit conversion of char const* -> ipv4 -> ipv6
|
||||
|
||||
constexpr IPv6Address(IPv4Address const& ipv4_address)
|
||||
{
|
||||
// IPv4 mapped IPv6 address
|
||||
|
|
|
@ -58,9 +58,14 @@ LexicalPath::LexicalPath(ByteString path)
|
|||
}
|
||||
}
|
||||
|
||||
bool LexicalPath::is_absolute() const
|
||||
bool LexicalPath::is_absolute_path(StringView path)
|
||||
{
|
||||
return m_string.starts_with('/');
|
||||
return path.starts_with('/');
|
||||
}
|
||||
|
||||
bool LexicalPath::is_root() const
|
||||
{
|
||||
return m_string == "/";
|
||||
}
|
||||
|
||||
Vector<ByteString> LexicalPath::parts() const
|
||||
|
|
|
@ -26,7 +26,10 @@ public:
|
|||
|
||||
explicit LexicalPath(ByteString);
|
||||
|
||||
bool is_absolute() const;
|
||||
static bool is_absolute_path(StringView path);
|
||||
bool is_absolute() const { return is_absolute_path(m_string); }
|
||||
bool is_root() const;
|
||||
|
||||
ByteString const& string() const { return m_string; }
|
||||
|
||||
StringView dirname() const { return m_dirname; }
|
||||
|
|
|
@ -10,14 +10,9 @@
|
|||
|
||||
namespace AK {
|
||||
|
||||
static bool is_absolute_path(StringView path)
|
||||
{
|
||||
return path.length() >= 2 && path[1] == ':';
|
||||
}
|
||||
|
||||
static bool is_root(auto const& parts)
|
||||
{
|
||||
return parts.size() == 1 && is_absolute_path(parts[0]);
|
||||
return parts.size() == 1 && LexicalPath::is_absolute_path(parts[0]);
|
||||
}
|
||||
|
||||
LexicalPath::LexicalPath(ByteString path)
|
||||
|
@ -45,9 +40,14 @@ LexicalPath::LexicalPath(ByteString path)
|
|||
}
|
||||
}
|
||||
|
||||
bool LexicalPath::is_absolute() const
|
||||
bool LexicalPath::is_absolute_path(StringView path)
|
||||
{
|
||||
return is_absolute_path(m_string);
|
||||
return path.length() >= 2 && path[1] == ':';
|
||||
}
|
||||
|
||||
bool LexicalPath::is_root() const
|
||||
{
|
||||
return AK::is_root(m_parts);
|
||||
}
|
||||
|
||||
Vector<ByteString> LexicalPath::parts() const
|
||||
|
@ -86,7 +86,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
|
|||
continue;
|
||||
if (part == ".." && !canonical_parts.is_empty()) {
|
||||
// At the root, .. does nothing.
|
||||
if (is_root(canonical_parts))
|
||||
if (AK::is_root(canonical_parts))
|
||||
continue;
|
||||
// A .. and a previous non-.. part cancel each other.
|
||||
if (canonical_parts.last() != "..") {
|
||||
|
@ -100,7 +100,7 @@ ByteString LexicalPath::canonicalized_path(ByteString path)
|
|||
StringBuilder builder;
|
||||
builder.join('\\', canonical_parts);
|
||||
// "X:" -> "X:\"
|
||||
if (is_root(canonical_parts))
|
||||
if (AK::is_root(canonical_parts))
|
||||
builder.append('\\');
|
||||
path = builder.to_byte_string();
|
||||
return path == "" ? "." : path;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace AK {
|
||||
|
|
|
@ -73,16 +73,20 @@ String human_readable_size_long(u64 size, UseThousandsSeparator use_thousands_se
|
|||
return MUST(String::formatted("{} ({} bytes)", human_readable_size_string, size));
|
||||
}
|
||||
|
||||
String human_readable_time(i64 time_in_seconds)
|
||||
String human_readable_time(Duration duration)
|
||||
{
|
||||
auto days = time_in_seconds / 86400;
|
||||
time_in_seconds = time_in_seconds % 86400;
|
||||
auto milliseconds = duration.to_milliseconds();
|
||||
|
||||
auto hours = time_in_seconds / 3600;
|
||||
time_in_seconds = time_in_seconds % 3600;
|
||||
auto days = milliseconds / 86400000;
|
||||
milliseconds = milliseconds % 86400000;
|
||||
|
||||
auto minutes = time_in_seconds / 60;
|
||||
time_in_seconds = time_in_seconds % 60;
|
||||
auto hours = milliseconds / 3600000;
|
||||
milliseconds = milliseconds % 3600000;
|
||||
|
||||
auto minutes = milliseconds / 60000;
|
||||
milliseconds = milliseconds % 60000;
|
||||
|
||||
auto seconds = static_cast<double>(milliseconds) / 1000.0;
|
||||
|
||||
StringBuilder builder;
|
||||
|
||||
|
@ -95,7 +99,7 @@ String human_readable_time(i64 time_in_seconds)
|
|||
if (minutes > 0)
|
||||
builder.appendff("{} minute{} ", minutes, minutes == 1 ? "" : "s");
|
||||
|
||||
builder.appendff("{} second{}", time_in_seconds, time_in_seconds == 1 ? "" : "s");
|
||||
builder.appendff("{:.3} second{}", seconds, seconds == 1.0 ? "" : "s");
|
||||
|
||||
return MUST(builder.to_string());
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Time.h>
|
||||
|
||||
namespace AK {
|
||||
|
||||
|
@ -24,7 +25,7 @@ String human_readable_size(u64 size, HumanReadableBasedOn based_on = HumanReadab
|
|||
String human_readable_quantity(u64 quantity, HumanReadableBasedOn based_on = HumanReadableBasedOn::Base2, StringView unit = "B"sv, UseThousandsSeparator use_thousands_separator = UseThousandsSeparator::No);
|
||||
|
||||
String human_readable_size_long(u64 size, UseThousandsSeparator use_thousands_separator = UseThousandsSeparator::No);
|
||||
String human_readable_time(i64 time_in_seconds);
|
||||
String human_readable_time(Duration);
|
||||
String human_readable_digital_time(i64 time_in_seconds);
|
||||
|
||||
}
|
||||
|
|
|
@ -18,4 +18,11 @@
|
|||
# define SWIFT_CONFORMS_TO_PROTOCOL(protocol)
|
||||
# define SWIFT_COMPUTED_PROPERTY
|
||||
# define SWIFT_MUTATING
|
||||
# define SWIFT_UNCHECKED_SENDABLE
|
||||
# define SWIFT_NONCOPYABLE
|
||||
# define SWIFT_NONESCAPABLE
|
||||
# define SWIFT_ESCAPABLE
|
||||
# define SWIFT_ESCAPABLE_IF(...)
|
||||
# define SWIFT_RETURNS_RETAINED
|
||||
# define SWIFT_RETURNS_UNRETAINED
|
||||
#endif
|
||||
|
|
|
@ -147,6 +147,7 @@ using __ptrdiff_t = __PTRDIFF_TYPE__;
|
|||
# if defined(AK_OS_WINDOWS)
|
||||
using ssize_t = AK::Detail::MakeSigned<size_t>;
|
||||
using mode_t = unsigned short;
|
||||
using pid_t = int;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -35,5 +35,5 @@ you are welcome to ask on [Discord](../README.md#get-in-touch-and-participate).
|
|||
* [LibWeb: From Loading to Painting](LibWebFromLoadingToPainting.md)
|
||||
* [LibWeb: Browsing Contexts and Navigables](BrowsingContextsAndNavigables.md)
|
||||
* [How to Add an IDL File](AddNewIDLFile.md)
|
||||
* [LibWeb Code Style & Patterns](Browser/Patterns.md)
|
||||
* [LibWeb Code Style & Patterns](LibWebPatterns.md)
|
||||
* [CSS Generated Files](CSSGeneratedFiles.md)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Testing Ladybird
|
||||
|
||||
Tests are locates in `Tests/`, with a directory for each library.
|
||||
Tests are located in `Tests/`, with a directory for each library.
|
||||
|
||||
Every feature or bug fix added to LibWeb should have a corresponding test in `Tests/LibWeb`.
|
||||
The test should be either a Text, Layout, Ref, or Screenshot test depending on the feature.
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include <AK/Assertions.h>
|
||||
#include <AK/BinarySearch.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <LibCompress/Deflate.h>
|
||||
#include <LibCompress/Huffman.h>
|
||||
|
||||
|
@ -480,10 +478,7 @@ DeflateCompressor::DeflateCompressor(NonnullOwnPtr<LittleEndianOutputBitStream>
|
|||
m_distance_frequencies.fill(0);
|
||||
}
|
||||
|
||||
DeflateCompressor::~DeflateCompressor()
|
||||
{
|
||||
VERIFY(m_finished);
|
||||
}
|
||||
DeflateCompressor::~DeflateCompressor() = default;
|
||||
|
||||
ErrorOr<Bytes> DeflateCompressor::read_some(Bytes)
|
||||
{
|
||||
|
|
18
Libraries/LibCompress/Forward.h
Normal file
18
Libraries/LibCompress/Forward.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Compress {
|
||||
|
||||
class DeflateCompressor;
|
||||
class DeflateDecompressor;
|
||||
class GzipCompressor;
|
||||
class GzipDecompressor;
|
||||
class ZlibCompressor;
|
||||
class ZlibDecompressor;
|
||||
|
||||
}
|
|
@ -11,9 +11,6 @@
|
|||
#include <AK/MemoryStream.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibCore/System.h>
|
||||
|
||||
namespace Compress {
|
||||
|
||||
|
@ -189,8 +186,25 @@ ErrorOr<size_t> GzipDecompressor::write_some(ReadonlyBytes)
|
|||
return Error::from_errno(EBADF);
|
||||
}
|
||||
|
||||
GzipCompressor::GzipCompressor(MaybeOwned<Stream> stream)
|
||||
: m_output_stream(move(stream))
|
||||
ErrorOr<NonnullOwnPtr<GzipCompressor>> GzipCompressor::create(MaybeOwned<Stream> output_stream)
|
||||
{
|
||||
BlockHeader header;
|
||||
header.identification_1 = 0x1f;
|
||||
header.identification_2 = 0x8b;
|
||||
header.compression_method = 0x08;
|
||||
header.flags = 0;
|
||||
header.modification_time = 0;
|
||||
header.extra_flags = 3; // DEFLATE sets 2 for maximum compression and 4 for minimum compression
|
||||
header.operating_system = 3; // unix
|
||||
TRY(output_stream->write_until_depleted({ &header, sizeof(header) }));
|
||||
|
||||
auto deflate_compressor = TRY(DeflateCompressor::construct(MaybeOwned(*output_stream)));
|
||||
return adopt_own(*new GzipCompressor { move(output_stream), move(deflate_compressor) });
|
||||
}
|
||||
|
||||
GzipCompressor::GzipCompressor(MaybeOwned<Stream> output_stream, NonnullOwnPtr<DeflateCompressor> deflate_compressor)
|
||||
: m_output_stream(move(output_stream))
|
||||
, m_deflate_compressor(move(deflate_compressor))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -201,25 +215,27 @@ ErrorOr<Bytes> GzipCompressor::read_some(Bytes)
|
|||
|
||||
ErrorOr<size_t> GzipCompressor::write_some(ReadonlyBytes bytes)
|
||||
{
|
||||
BlockHeader header;
|
||||
header.identification_1 = 0x1f;
|
||||
header.identification_2 = 0x8b;
|
||||
header.compression_method = 0x08;
|
||||
header.flags = 0;
|
||||
header.modification_time = 0;
|
||||
header.extra_flags = 3; // DEFLATE sets 2 for maximum compression and 4 for minimum compression
|
||||
header.operating_system = 3; // unix
|
||||
TRY(m_output_stream->write_until_depleted({ &header, sizeof(header) }));
|
||||
auto compressed_stream = TRY(DeflateCompressor::construct(MaybeOwned(*m_output_stream)));
|
||||
TRY(compressed_stream->write_until_depleted(bytes));
|
||||
TRY(compressed_stream->final_flush());
|
||||
Crypto::Checksum::CRC32 crc32;
|
||||
crc32.update(bytes);
|
||||
TRY(m_output_stream->write_value<LittleEndian<u32>>(crc32.digest()));
|
||||
TRY(m_output_stream->write_value<LittleEndian<u32>>(bytes.size()));
|
||||
VERIFY(!m_finished);
|
||||
|
||||
TRY(m_deflate_compressor->write_until_depleted(bytes));
|
||||
m_total_bytes += bytes.size();
|
||||
m_crc32.update(bytes);
|
||||
|
||||
return bytes.size();
|
||||
}
|
||||
|
||||
ErrorOr<void> GzipCompressor::finish()
|
||||
{
|
||||
VERIFY(!m_finished);
|
||||
m_finished = true;
|
||||
|
||||
TRY(m_deflate_compressor->final_flush());
|
||||
TRY(m_output_stream->write_value<LittleEndian<u32>>(m_crc32.digest()));
|
||||
TRY(m_output_stream->write_value<LittleEndian<u32>>(m_total_bytes));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool GzipCompressor::is_eof() const
|
||||
{
|
||||
return true;
|
||||
|
@ -237,12 +253,14 @@ void GzipCompressor::close()
|
|||
ErrorOr<ByteBuffer> GzipCompressor::compress_all(ReadonlyBytes bytes)
|
||||
{
|
||||
auto output_stream = TRY(try_make<AllocatingMemoryStream>());
|
||||
GzipCompressor gzip_stream { MaybeOwned<Stream>(*output_stream) };
|
||||
auto gzip_stream = TRY(GzipCompressor::create(MaybeOwned { *output_stream }));
|
||||
|
||||
TRY(gzip_stream.write_until_depleted(bytes));
|
||||
TRY(gzip_stream->write_until_depleted(bytes));
|
||||
TRY(gzip_stream->finish());
|
||||
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(output_stream->used_buffer_size()));
|
||||
TRY(output_stream->read_until_filled(buffer.bytes()));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
|
||||
virtual bool is_eof() const override;
|
||||
virtual bool is_open() const override { return true; }
|
||||
virtual void close() override {};
|
||||
virtual void close() override { }
|
||||
|
||||
static ErrorOr<ByteBuffer> decompress_all(ReadonlyBytes);
|
||||
|
||||
|
@ -83,7 +83,7 @@ private:
|
|||
|
||||
class GzipCompressor final : public Stream {
|
||||
public:
|
||||
GzipCompressor(MaybeOwned<Stream>);
|
||||
static ErrorOr<NonnullOwnPtr<GzipCompressor>> create(MaybeOwned<Stream>);
|
||||
|
||||
virtual ErrorOr<Bytes> read_some(Bytes) override;
|
||||
virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
|
||||
|
@ -93,8 +93,17 @@ public:
|
|||
|
||||
static ErrorOr<ByteBuffer> compress_all(ReadonlyBytes bytes);
|
||||
|
||||
ErrorOr<void> finish();
|
||||
|
||||
private:
|
||||
GzipCompressor(MaybeOwned<Stream>, NonnullOwnPtr<DeflateCompressor>);
|
||||
|
||||
MaybeOwned<Stream> m_output_stream;
|
||||
NonnullOwnPtr<DeflateCompressor> m_deflate_compressor;
|
||||
|
||||
Crypto::Checksum::CRC32 m_crc32;
|
||||
size_t m_total_bytes { 0 };
|
||||
bool m_finished { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,19 @@ namespace Compress {
|
|||
|
||||
ErrorOr<NonnullOwnPtr<ZlibDecompressor>> ZlibDecompressor::create(MaybeOwned<Stream> stream)
|
||||
{
|
||||
auto header = TRY(stream->read_value<ZlibHeader>());
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) ZlibDecompressor(move(stream)));
|
||||
}
|
||||
|
||||
ZlibDecompressor::ZlibDecompressor(MaybeOwned<Stream> stream)
|
||||
: m_has_seen_header(false)
|
||||
, m_stream(move(stream))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<Bytes> ZlibDecompressor::read_some(Bytes bytes)
|
||||
{
|
||||
if (!m_has_seen_header) {
|
||||
auto header = TRY(m_stream->read_value<ZlibHeader>());
|
||||
|
||||
if (header.compression_method != ZlibCompressionMethod::Deflate || header.compression_info > 7)
|
||||
return Error::from_string_literal("Non-DEFLATE compression inside Zlib is not supported");
|
||||
|
@ -28,20 +40,12 @@ ErrorOr<NonnullOwnPtr<ZlibDecompressor>> ZlibDecompressor::create(MaybeOwned<Str
|
|||
if (header.as_u16 % 31 != 0)
|
||||
return Error::from_string_literal("Zlib error correction code does not match");
|
||||
|
||||
auto bit_stream = make<LittleEndianInputBitStream>(move(stream));
|
||||
auto bit_stream = make<LittleEndianInputBitStream>(move(m_stream));
|
||||
auto deflate_stream = TRY(Compress::DeflateDecompressor::construct(move(bit_stream)));
|
||||
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) ZlibDecompressor(header, move(deflate_stream)));
|
||||
m_stream = move(deflate_stream);
|
||||
m_has_seen_header = true;
|
||||
}
|
||||
|
||||
ZlibDecompressor::ZlibDecompressor(ZlibHeader header, NonnullOwnPtr<Stream> stream)
|
||||
: m_header(header)
|
||||
, m_stream(move(stream))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<Bytes> ZlibDecompressor::read_some(Bytes bytes)
|
||||
{
|
||||
return m_stream->read_some(bytes);
|
||||
}
|
||||
|
||||
|
@ -84,10 +88,7 @@ ZlibCompressor::ZlibCompressor(MaybeOwned<Stream> stream, NonnullOwnPtr<Stream>
|
|||
{
|
||||
}
|
||||
|
||||
ZlibCompressor::~ZlibCompressor()
|
||||
{
|
||||
VERIFY(m_finished);
|
||||
}
|
||||
ZlibCompressor::~ZlibCompressor() = default;
|
||||
|
||||
ErrorOr<void> ZlibCompressor::write_header(ZlibCompressionMethod compression_method, ZlibCompressionLevel compression_level)
|
||||
{
|
||||
|
|
|
@ -55,10 +55,10 @@ public:
|
|||
virtual void close() override;
|
||||
|
||||
private:
|
||||
ZlibDecompressor(ZlibHeader, NonnullOwnPtr<Stream>);
|
||||
ZlibDecompressor(MaybeOwned<Stream>);
|
||||
|
||||
ZlibHeader m_header;
|
||||
NonnullOwnPtr<Stream> m_stream;
|
||||
bool m_has_seen_header { false };
|
||||
MaybeOwned<Stream> m_stream;
|
||||
};
|
||||
|
||||
class ZlibCompressor : public Stream {
|
||||
|
|
|
@ -28,7 +28,7 @@ AnonymousBufferImpl::~AnonymousBufferImpl()
|
|||
|
||||
ErrorOr<NonnullRefPtr<AnonymousBufferImpl>> AnonymousBufferImpl::create(size_t size)
|
||||
{
|
||||
HANDLE map_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, HIWORD(size), LOWORD(size), NULL);
|
||||
HANDLE map_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, size >> 31 >> 1, size & 0xFFFFFFFF, NULL);
|
||||
if (!map_handle)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
|
|
@ -4,10 +4,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Directory.h"
|
||||
#include "DirIterator.h"
|
||||
#include "System.h"
|
||||
#include <dirent.h>
|
||||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/System.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
@ -31,6 +29,7 @@ Directory::~Directory()
|
|||
MUST(System::close(m_directory_fd));
|
||||
}
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<void> Directory::chown(uid_t uid, gid_t gid)
|
||||
{
|
||||
if (m_directory_fd == -1)
|
||||
|
@ -38,6 +37,7 @@ ErrorOr<void> Directory::chown(uid_t uid, gid_t gid)
|
|||
TRY(Core::System::fchown(m_directory_fd, uid, gid));
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
ErrorOr<bool> Directory::is_valid_directory(int fd)
|
||||
{
|
||||
|
@ -69,7 +69,7 @@ ErrorOr<Directory> Directory::create(LexicalPath path, CreateDirectories create_
|
|||
|
||||
ErrorOr<void> Directory::ensure_directory(LexicalPath const& path, mode_t creation_mode)
|
||||
{
|
||||
if (path.basename() == "/" || path.basename() == ".")
|
||||
if (path.is_root() || path.string() == ".")
|
||||
return {};
|
||||
|
||||
TRY(ensure_directory(path.parent(), creation_mode));
|
||||
|
|
|
@ -10,14 +10,11 @@
|
|||
#include <AK/Error.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/IterationDecision.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/DirectoryEntry.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace Core {
|
||||
|
@ -51,7 +48,9 @@ public:
|
|||
static ErrorOr<void> for_each_entry(StringView path, DirIterator::Flags, ForEachEntryCallback);
|
||||
ErrorOr<void> for_each_entry(DirIterator::Flags, ForEachEntryCallback);
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<void> chown(uid_t, gid_t);
|
||||
#endif
|
||||
|
||||
static ErrorOr<bool> is_valid_directory(int fd);
|
||||
|
||||
|
|
|
@ -61,6 +61,24 @@ struct ArgvList {
|
|||
}
|
||||
};
|
||||
|
||||
Process::Process(Process&& other)
|
||||
: m_pid(exchange(other.m_pid, 0))
|
||||
, m_should_disown(exchange(other.m_should_disown, false))
|
||||
{
|
||||
}
|
||||
|
||||
Process& Process::operator=(Process&& other)
|
||||
{
|
||||
m_pid = exchange(other.m_pid, 0);
|
||||
m_should_disown = exchange(other.m_should_disown, false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Process::~Process()
|
||||
{
|
||||
(void)disown();
|
||||
}
|
||||
|
||||
Process Process::current()
|
||||
{
|
||||
auto p = Process { getpid() };
|
||||
|
@ -121,7 +139,7 @@ ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
|
|||
return Process { pid };
|
||||
}
|
||||
|
||||
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
{
|
||||
auto process = TRY(spawn({
|
||||
.executable = path,
|
||||
|
@ -131,14 +149,11 @@ ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<ByteString> argument
|
|||
|
||||
if (keep_as_child == KeepAsChild::No)
|
||||
TRY(process.disown());
|
||||
else {
|
||||
// FIXME: This won't be needed if return value is changed to Process.
|
||||
process.m_should_disown = false;
|
||||
}
|
||||
return process.pid();
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
{
|
||||
Vector<ByteString> backing_strings;
|
||||
backing_strings.ensure_capacity(arguments.size());
|
||||
|
@ -153,29 +168,8 @@ ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<StringView> argument
|
|||
|
||||
if (keep_as_child == KeepAsChild::No)
|
||||
TRY(process.disown());
|
||||
else
|
||||
process.m_should_disown = false;
|
||||
return process.pid();
|
||||
}
|
||||
|
||||
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<char const*> arguments, ByteString working_directory, KeepAsChild keep_as_child)
|
||||
{
|
||||
Vector<ByteString> backing_strings;
|
||||
backing_strings.ensure_capacity(arguments.size());
|
||||
for (auto const& argument : arguments)
|
||||
backing_strings.append(argument);
|
||||
|
||||
auto process = TRY(spawn({
|
||||
.executable = path,
|
||||
.arguments = backing_strings,
|
||||
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : Optional<ByteString> { working_directory },
|
||||
}));
|
||||
|
||||
if (keep_as_child == KeepAsChild::No)
|
||||
TRY(process.disown());
|
||||
else
|
||||
process.m_should_disown = false;
|
||||
return process.pid();
|
||||
return process;
|
||||
}
|
||||
|
||||
ErrorOr<String> Process::get_name()
|
||||
|
@ -321,6 +315,11 @@ void Process::wait_for_debugger_and_break()
|
|||
}
|
||||
}
|
||||
|
||||
pid_t Process::pid() const
|
||||
{
|
||||
return m_pid;
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::disown()
|
||||
{
|
||||
if (m_pid != 0 && m_should_disown) {
|
||||
|
@ -336,19 +335,19 @@ ErrorOr<void> Process::disown()
|
|||
}
|
||||
}
|
||||
|
||||
ErrorOr<bool> Process::wait_for_termination()
|
||||
ErrorOr<int> Process::wait_for_termination()
|
||||
{
|
||||
VERIFY(m_pid > 0);
|
||||
|
||||
bool exited_with_code_0 = true;
|
||||
int exit_code = -1;
|
||||
int status;
|
||||
if (waitpid(m_pid, &status, 0) == -1)
|
||||
return Error::from_syscall("waitpid"sv, errno);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
exited_with_code_0 &= WEXITSTATUS(status) == 0;
|
||||
exit_code = WEXITSTATUS(status);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
exited_with_code_0 = false;
|
||||
exit_code = 128 + WTERMSIG(status);
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
// This is only possible if the child process is being traced by us.
|
||||
VERIFY_NOT_REACHED();
|
||||
|
@ -357,7 +356,7 @@ ErrorOr<bool> Process::wait_for_termination()
|
|||
}
|
||||
|
||||
m_should_disown = false;
|
||||
return exited_with_code_0;
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,33 +53,15 @@ public:
|
|||
No
|
||||
};
|
||||
|
||||
Process(Process&& other)
|
||||
: m_pid(exchange(other.m_pid, 0))
|
||||
, m_should_disown(exchange(other.m_should_disown, false))
|
||||
{
|
||||
}
|
||||
|
||||
Process& operator=(Process&& other)
|
||||
{
|
||||
m_pid = exchange(other.m_pid, 0);
|
||||
m_should_disown = exchange(other.m_should_disown, false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Process()
|
||||
{
|
||||
(void)disown();
|
||||
}
|
||||
Process(Process&& other);
|
||||
Process& operator=(Process&& other);
|
||||
~Process();
|
||||
|
||||
static ErrorOr<Process> spawn(ProcessSpawnOptions const& options);
|
||||
static Process current();
|
||||
|
||||
// FIXME: Make the following 2 functions return Process instance or delete them.
|
||||
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
|
||||
// FIXME: Remove this. char const* should not exist on this level of abstraction.
|
||||
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<char const*> arguments = {}, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
static ErrorOr<Process> spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
static ErrorOr<Process> spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
|
||||
|
||||
static ErrorOr<String> get_name();
|
||||
enum class SetThreadName {
|
||||
|
@ -91,15 +73,17 @@ public:
|
|||
static void wait_for_debugger_and_break();
|
||||
static ErrorOr<bool> is_being_debugged();
|
||||
|
||||
pid_t pid() const { return m_pid; }
|
||||
pid_t pid() const;
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<void> disown();
|
||||
#endif
|
||||
|
||||
// FIXME: Make it return an exit code.
|
||||
ErrorOr<bool> wait_for_termination();
|
||||
ErrorOr<int> wait_for_termination();
|
||||
|
||||
private:
|
||||
Process(pid_t pid)
|
||||
#ifndef AK_OS_WINDOWS
|
||||
Process(pid_t pid = -1)
|
||||
: m_pid(pid)
|
||||
, m_should_disown(true)
|
||||
{
|
||||
|
@ -107,6 +91,14 @@ private:
|
|||
|
||||
pid_t m_pid;
|
||||
bool m_should_disown;
|
||||
#else
|
||||
Process(void* handle = 0)
|
||||
: m_handle(handle)
|
||||
{
|
||||
}
|
||||
|
||||
void* m_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
162
Libraries/LibCore/ProcessWindows.cpp
Normal file
162
Libraries/LibCore/ProcessWindows.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
|
||||
* Copyright (c) 2023-2024, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2024, stasoid <stasoid@yahoo.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Utf16View.h>
|
||||
#include <LibCore/Process.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
Process::Process(Process&& other)
|
||||
: m_handle(exchange(other.m_handle, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
Process& Process::operator=(Process&& other)
|
||||
{
|
||||
m_handle = exchange(other.m_handle, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Process::~Process()
|
||||
{
|
||||
if (m_handle)
|
||||
CloseHandle(m_handle);
|
||||
}
|
||||
|
||||
Process Process::current()
|
||||
{
|
||||
return GetCurrentProcess();
|
||||
}
|
||||
|
||||
ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
|
||||
{
|
||||
// file actions are not supported
|
||||
VERIFY(options.file_actions.is_empty());
|
||||
|
||||
char const* program_path = 0;
|
||||
StringBuilder builder;
|
||||
if (options.search_for_executable_in_path)
|
||||
builder.appendff("\"{}\" ", options.executable);
|
||||
else
|
||||
program_path = options.executable.characters();
|
||||
|
||||
builder.join(' ', options.arguments);
|
||||
builder.append('\0');
|
||||
ByteBuffer command_line = TRY(builder.to_byte_buffer());
|
||||
|
||||
auto curdir = options.working_directory.has_value() ? options.working_directory->characters() : 0;
|
||||
|
||||
STARTUPINFO startup_info = {};
|
||||
PROCESS_INFORMATION process_info = {};
|
||||
|
||||
BOOL result = CreateProcess(
|
||||
program_path,
|
||||
(char*)command_line.data(),
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
curdir,
|
||||
&startup_info,
|
||||
&process_info);
|
||||
|
||||
if (!result)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
CloseHandle(process_info.hThread);
|
||||
|
||||
return Process(process_info.hProcess);
|
||||
}
|
||||
|
||||
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild)
|
||||
{
|
||||
return spawn({
|
||||
.executable = path,
|
||||
.arguments = Vector<ByteString> { arguments },
|
||||
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : working_directory,
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild)
|
||||
{
|
||||
Vector<ByteString> backing_strings;
|
||||
backing_strings.ensure_capacity(arguments.size());
|
||||
for (auto argument : arguments)
|
||||
backing_strings.append(argument);
|
||||
|
||||
return spawn({
|
||||
.executable = path,
|
||||
.arguments = backing_strings,
|
||||
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : working_directory,
|
||||
});
|
||||
}
|
||||
|
||||
// Get the full path of the executable file of the current process
|
||||
ErrorOr<String> Process::get_name()
|
||||
{
|
||||
wchar_t path[MAX_PATH] = {};
|
||||
|
||||
DWORD length = GetModuleFileNameW(NULL, path, MAX_PATH);
|
||||
if (!length)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
return String::from_utf16(Utf16View { { (u16*)path, length } });
|
||||
}
|
||||
|
||||
ErrorOr<void> Process::set_name(StringView, SetThreadName)
|
||||
{
|
||||
// Process::set_name() probably cannot be implemented on Windows.
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<bool> Process::is_being_debugged()
|
||||
{
|
||||
return IsDebuggerPresent();
|
||||
}
|
||||
|
||||
// Forces the process to sleep until a debugger is attached, then breaks.
|
||||
void Process::wait_for_debugger_and_break()
|
||||
{
|
||||
bool print_message = true;
|
||||
for (;;) {
|
||||
if (IsDebuggerPresent()) {
|
||||
DebugBreak();
|
||||
return;
|
||||
}
|
||||
if (print_message) {
|
||||
dbgln("Process {} with pid {} is sleeping, waiting for debugger.", Process::get_name(), GetCurrentProcessId());
|
||||
print_message = false;
|
||||
}
|
||||
Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t Process::pid() const
|
||||
{
|
||||
return GetProcessId(m_handle);
|
||||
}
|
||||
|
||||
ErrorOr<int> Process::wait_for_termination()
|
||||
{
|
||||
auto result = WaitForSingleObject(m_handle, INFINITE);
|
||||
if (result == WAIT_FAILED)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
DWORD exit_code = 0;
|
||||
if (!GetExitCodeProcess(m_handle, &exit_code))
|
||||
return Error::from_windows_error(GetLastError());
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
}
|
|
@ -159,6 +159,7 @@ class TCPSocket final : public Socket {
|
|||
public:
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(ByteString const& host, u16 port);
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address);
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> connect(SocketAddress const& address, ByteString const&) { return connect(address); }
|
||||
static ErrorOr<NonnullOwnPtr<TCPSocket>> adopt_fd(int fd);
|
||||
|
||||
TCPSocket(TCPSocket&& other)
|
||||
|
@ -220,6 +221,7 @@ class UDPSocket final : public Socket {
|
|||
public:
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(ByteString const& host, u16 port, Optional<AK::Duration> timeout = {});
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(SocketAddress const& address, Optional<AK::Duration> timeout = {});
|
||||
static ErrorOr<NonnullOwnPtr<UDPSocket>> connect(SocketAddress const& address, ByteString const&, Optional<AK::Duration> timeout = {}) { return connect(address, timeout); }
|
||||
|
||||
UDPSocket(UDPSocket&& other)
|
||||
: Socket(static_cast<Socket&&>(other))
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
#include <AK/ByteString.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
|
||||
namespace Core::System {
|
||||
|
@ -20,9 +21,21 @@ namespace Core::System {
|
|||
ErrorOr<int> open(StringView path, int options, mode_t mode)
|
||||
{
|
||||
ByteString string_path = path;
|
||||
int rc = _open(string_path.characters(), options, mode);
|
||||
if (rc < 0)
|
||||
return Error::from_syscall("open"sv, -errno);
|
||||
auto sz_path = string_path.characters();
|
||||
int rc = _open(sz_path, options, mode);
|
||||
if (rc < 0) {
|
||||
int error = errno;
|
||||
struct stat st = {};
|
||||
if (::stat(sz_path, &st) == 0 && (st.st_mode & S_IFDIR)) {
|
||||
HANDLE dir_handle = CreateFile(sz_path, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (dir_handle == INVALID_HANDLE_VALUE)
|
||||
return Error::from_windows_error(GetLastError());
|
||||
int dir_fd = _open_osfhandle((intptr_t)dir_handle, 0);
|
||||
if (dir_fd != -1)
|
||||
return dir_fd;
|
||||
}
|
||||
return Error::from_syscall("open"sv, -error);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -88,4 +101,69 @@ ErrorOr<void> ioctl(int, unsigned, ...)
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<ByteString> getcwd()
|
||||
{
|
||||
auto* cwd = _getcwd(nullptr, 0);
|
||||
if (!cwd)
|
||||
return Error::from_syscall("getcwd"sv, -errno);
|
||||
|
||||
ByteString string_cwd(cwd);
|
||||
free(cwd);
|
||||
return string_cwd;
|
||||
}
|
||||
|
||||
ErrorOr<struct stat> stat(StringView path)
|
||||
{
|
||||
if (path.is_null())
|
||||
return Error::from_syscall("stat"sv, -EFAULT);
|
||||
|
||||
struct stat st = {};
|
||||
ByteString path_string = path;
|
||||
if (::stat(path_string.characters(), &st) < 0)
|
||||
return Error::from_syscall("stat"sv, -errno);
|
||||
return st;
|
||||
}
|
||||
|
||||
ErrorOr<void> rmdir(StringView path)
|
||||
{
|
||||
if (path.is_null())
|
||||
return Error::from_errno(EFAULT);
|
||||
|
||||
ByteString path_string = path;
|
||||
if (_rmdir(path_string.characters()) < 0)
|
||||
return Error::from_syscall("rmdir"sv, -errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> unlink(StringView path)
|
||||
{
|
||||
if (path.is_null())
|
||||
return Error::from_errno(EFAULT);
|
||||
|
||||
ByteString path_string = path;
|
||||
if (_unlink(path_string.characters()) < 0)
|
||||
return Error::from_syscall("unlink"sv, -errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> mkdir(StringView path, mode_t)
|
||||
{
|
||||
ByteString str = path;
|
||||
if (_mkdir(str.characters()) < 0)
|
||||
return Error::from_syscall("mkdir"sv, -errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<int> openat(int, StringView, int, mode_t)
|
||||
{
|
||||
dbgln("Core::System::openat() is not implemented");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<struct stat> fstatat(int, StringView, int)
|
||||
{
|
||||
dbgln("Core::System::fstatat() is not implemented");
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/Time.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
|
||||
namespace Crypto::ASN1 {
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/BitmapView.h>
|
||||
#include <AK/Result.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCrypto/ASN1/ASN1.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Span.h>
|
||||
#include <LibCrypto/ASN1/ASN1.h>
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
|
||||
namespace Crypto {
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <AK/ByteString.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCrypto/Hash/HashFunction.h>
|
||||
|
||||
namespace Crypto::Authentication {
|
||||
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
*/
|
||||
|
||||
#include "UnsignedBigIntegerAlgorithms.h"
|
||||
#include <AK/BigIntBase.h>
|
||||
#include <AK/BuiltinWrappers.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
|
||||
namespace Crypto {
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "UnsignedBigIntegerAlgorithms.h"
|
||||
#include <AK/BigIntBase.h>
|
||||
#include <AK/BuiltinWrappers.h>
|
||||
|
||||
namespace Crypto {
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Span.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
|
||||
namespace Crypto {
|
||||
|
|
|
@ -601,6 +601,11 @@ bool UnsignedBigInteger::operator<(UnsignedBigInteger const& other) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool UnsignedBigInteger::operator<=(UnsignedBigInteger const& other) const
|
||||
{
|
||||
return *this < other || *this == other;
|
||||
}
|
||||
|
||||
bool UnsignedBigInteger::operator>(UnsignedBigInteger const& other) const
|
||||
{
|
||||
return *this != other && !(*this < other);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/BigIntBase.h>
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Span.h>
|
||||
|
@ -128,6 +127,7 @@ public:
|
|||
[[nodiscard]] bool operator==(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator!=(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator<(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator<=(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator>(UnsignedBigInteger const& other) const;
|
||||
[[nodiscard]] bool operator>=(UnsignedBigInteger const& other) const;
|
||||
|
||||
|
@ -171,8 +171,26 @@ struct AK::Formatter<Crypto::UnsignedBigInteger> : Formatter<StringView> {
|
|||
ErrorOr<void> format(FormatBuilder&, Crypto::UnsignedBigInteger const&);
|
||||
};
|
||||
|
||||
inline Crypto::UnsignedBigInteger
|
||||
operator""_bigint(char const* string, size_t length)
|
||||
inline Crypto::UnsignedBigInteger operator""_bigint(char const* string, size_t length)
|
||||
{
|
||||
return MUST(Crypto::UnsignedBigInteger::from_base(10, { string, length }));
|
||||
}
|
||||
|
||||
inline Crypto::UnsignedBigInteger operator""_bigint(unsigned long long value)
|
||||
{
|
||||
auto result = Crypto::UnsignedBigInteger { static_cast<u64>(value) };
|
||||
VERIFY(!result.is_invalid());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline Crypto::UnsignedBigInteger operator""_bigint(long double value)
|
||||
{
|
||||
VERIFY(value >= 0);
|
||||
VERIFY(value < static_cast<long double>(NumericLimits<double>::max()));
|
||||
|
||||
auto result = Crypto::UnsignedBigInteger { static_cast<double>(value) };
|
||||
VERIFY(!result.is_invalid());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <AK/Span.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCrypto/Checksum/CRC32.h>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCrypto/Cipher/Cipher.h>
|
||||
#include <LibCrypto/Cipher/Mode/CBC.h>
|
||||
#include <LibCrypto/Cipher/Mode/CTR.h>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Span.h>
|
||||
|
||||
namespace Crypto::Cipher {
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Span.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Random.h>
|
||||
|
||||
namespace Crypto::Curves {
|
||||
|
||||
class Curve25519 {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <LibCrypto/Curves/EllipticCurve.h>
|
||||
|
||||
namespace Crypto::Curves {
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/ByteReader.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Random.h>
|
||||
#include <LibCrypto/Curves/Curve25519.h>
|
||||
#include <LibCrypto/Curves/X25519.h>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Random.h>
|
||||
#include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h>
|
||||
#include <LibCrypto/NumberTheory/ModularFunctions.h>
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Random.h>
|
||||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
|
||||
namespace Crypto::NumberTheory {
|
||||
|
|
6
Libraries/LibDNS/CMakeLists.txt
Normal file
6
Libraries/LibDNS/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
Message.cpp
|
||||
)
|
||||
|
||||
serenity_lib(LibDNS dns)
|
||||
target_link_libraries(LibDNS PRIVATE LibCore)
|
1177
Libraries/LibDNS/Message.cpp
Normal file
1177
Libraries/LibDNS/Message.cpp
Normal file
File diff suppressed because it is too large
Load diff
702
Libraries/LibDNS/Message.h
Normal file
702
Libraries/LibDNS/Message.h
Normal file
|
@ -0,0 +1,702 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Base64.h>
|
||||
#include <AK/IPv4Address.h>
|
||||
#include <AK/IPv6Address.h>
|
||||
#include <AK/RedBlackTree.h>
|
||||
#include <AK/Time.h>
|
||||
|
||||
namespace DNS {
|
||||
namespace Messages {
|
||||
|
||||
struct DomainName;
|
||||
struct ParseContext {
|
||||
CountingStream& stream;
|
||||
NonnullOwnPtr<RedBlackTree<u16, DomainName>> pointers;
|
||||
};
|
||||
|
||||
enum class OpCode : u8;
|
||||
|
||||
struct Options {
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ID |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |QR| Opcode |AA|TC|RD|RA| Z |AD|CD| RCODE |
|
||||
constexpr inline static u16 QRMask = 0b1000000000000000;
|
||||
constexpr inline static u16 OpCodeMask = 0b0111100000000000;
|
||||
constexpr inline static u16 AuthoritativeAnswerMask = 0b0000010000000000;
|
||||
constexpr inline static u16 TruncatedMask = 0b0000001000000000;
|
||||
constexpr inline static u16 RecursionDesiredMask = 0b0000000100000000;
|
||||
constexpr inline static u16 RecursionAvailableMask = 0b0000000010000000;
|
||||
constexpr inline static u16 AuthenticatedDataMask = 0b0000000000100000;
|
||||
constexpr inline static u16 CheckingDisabledMask = 0b0000000000010000;
|
||||
constexpr inline static u16 ResponseCodeMask = 0b0000000000001111;
|
||||
|
||||
enum class ResponseCode : u16 {
|
||||
NoError = 0,
|
||||
FormatError = 1,
|
||||
ServerFailure = 2,
|
||||
NameError = 3,
|
||||
NotImplemented = 4,
|
||||
Refused = 5,
|
||||
};
|
||||
|
||||
void set_is_question(bool value) { raw = (raw & ~QRMask) | (value ? QRMask : 0); }
|
||||
void set_is_authoritative_answer(bool value) { raw = (raw & ~AuthoritativeAnswerMask) | (value ? AuthoritativeAnswerMask : 0); }
|
||||
void set_is_truncated(bool value) { raw = (raw & ~TruncatedMask) | (value ? TruncatedMask : 0); }
|
||||
void set_recursion_desired(bool value) { raw = (raw & ~RecursionDesiredMask) | (value ? RecursionDesiredMask : 0); }
|
||||
void set_recursion_available(bool value) { raw = (raw & ~RecursionAvailableMask) | (value ? RecursionAvailableMask : 0); }
|
||||
void set_response_code(ResponseCode code) { raw = (raw & ~ResponseCodeMask) | static_cast<u16>(code); }
|
||||
void set_checking_disabled(bool value) { raw = (raw & ~CheckingDisabledMask) | (value ? CheckingDisabledMask : 0); }
|
||||
void set_authenticated_data(bool value) { raw = (raw & ~AuthenticatedDataMask) | (value ? AuthenticatedDataMask : 0); }
|
||||
void set_op_code(OpCode code) { raw = (raw & ~OpCodeMask) | (static_cast<u16>(code) << 11); }
|
||||
|
||||
bool is_question() const { return (raw & QRMask) == 0; }
|
||||
bool is_authoritative_answer() const { return (raw & AuthoritativeAnswerMask) != 0; }
|
||||
bool is_truncated() const { return (raw & TruncatedMask) != 0; }
|
||||
bool recursion_desired() const { return (raw & RecursionDesiredMask) != 0; }
|
||||
bool recursion_available() const { return (raw & RecursionAvailableMask) != 0; }
|
||||
bool checking_disabled() const { return (raw & CheckingDisabledMask) != 0; }
|
||||
bool authenticated_data() const { return (raw & AuthenticatedDataMask) != 0; }
|
||||
ResponseCode response_code() const { return static_cast<ResponseCode>(raw & ResponseCodeMask); }
|
||||
OpCode op_code() const { return static_cast<OpCode>((raw & OpCodeMask) >> 11); }
|
||||
|
||||
String to_string() const;
|
||||
|
||||
NetworkOrdered<u16> raw { 0 };
|
||||
};
|
||||
StringView to_string(Options::ResponseCode);
|
||||
|
||||
struct Header {
|
||||
NetworkOrdered<u16> id;
|
||||
Options options;
|
||||
NetworkOrdered<u16> question_count;
|
||||
NetworkOrdered<u16> answer_count;
|
||||
NetworkOrdered<u16> authority_count;
|
||||
NetworkOrdered<u16> additional_count;
|
||||
};
|
||||
|
||||
struct DomainName {
|
||||
Vector<ByteString> labels;
|
||||
|
||||
static DomainName from_string(StringView);
|
||||
static ErrorOr<DomainName> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
String to_string() const;
|
||||
};
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4.
|
||||
enum class ResourceType : u16 {
|
||||
Reserved = 0, // [RFC6895]
|
||||
A = 1, // a host address [RFC1035]
|
||||
NS = 2, // an authoritative name server [RFC1035]
|
||||
MD = 3, // a mail destination (OBSOLETE - use MX) [RFC1035]
|
||||
MF = 4, // a mail forwarder (OBSOLETE - use MX) [RFC1035]
|
||||
CNAME = 5, // the canonical name for an alias [RFC1035]
|
||||
SOA = 6, // marks the start of a zone of authority [RFC1035]
|
||||
MB = 7, // a mailbox domain name (EXPERIMENTAL) [RFC1035]
|
||||
MG = 8, // a mail group member (EXPERIMENTAL) [RFC1035]
|
||||
MR = 9, // a mail rename domain name (EXPERIMENTAL) [RFC1035]
|
||||
NULL_ = 10, // a null RR (EXPERIMENTAL) [RFC1035]
|
||||
WKS = 11, // a well known service description [RFC1035]
|
||||
PTR = 12, // a domain name pointer [RFC1035]
|
||||
HINFO = 13, // host information [RFC1035]
|
||||
MINFO = 14, // mailbox or mail list information [RFC1035]
|
||||
MX = 15, // mail exchange [RFC1035]
|
||||
TXT = 16, // text strings [RFC1035]
|
||||
RP = 17, // for Responsible Person [RFC1183]
|
||||
AFSDB = 18, // for AFS Data Base location [RFC1183][RFC5864]
|
||||
X25 = 19, // for X.25 PSDN address [RFC1183]
|
||||
ISDN = 20, // for ISDN address [RFC1183]
|
||||
RT = 21, // for Route Through [RFC1183]
|
||||
NSAP = 22, // for NSAP address, NSAP style A record (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic]
|
||||
NSAP_PTR = 23, // for domain name pointer, NSAP style (DEPRECATED) [RFC1706][Moving TPC.INT and NSAP.INT infrastructure domains to historic]
|
||||
SIG = 24, // for security signature [RFC2536][RFC2931][RFC3110][RFC4034]
|
||||
KEY = 25, // for security key [RFC2536][RFC2539][RFC3110][RFC4034]
|
||||
PX = 26, // X.400 mail mapping information [RFC2163]
|
||||
GPOS = 27, // Geographical Position [RFC1712]
|
||||
AAAA = 28, // IP6 Address [RFC3596]
|
||||
LOC = 29, // Location Information [RFC1876]
|
||||
NXT = 30, // Next Domain (OBSOLETE) [RFC2535][RFC3755]
|
||||
EID = 31, // Endpoint Identifier [Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt]
|
||||
NIMLOC = 32, // Nimrod Locator [1][Michael_Patton][http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt]
|
||||
SRV = 33, // Server Selection [1][RFC2782]
|
||||
ATMA = 34, // ATM Address "[ ATM Forum Technical Committee, "ATM Name System, V2.0", Doc ID: AF-DANS-0152.000, July 2000. Available from and held in escrow by IANA.]"
|
||||
NAPTR = 35, // Naming Authority Pointer [RFC3403]
|
||||
KX = 36, // Key Exchanger [RFC2230]
|
||||
CERT = 37, // CERT [RFC4398]
|
||||
A6 = 38, // A6 (OBSOLETE - use AAAA) [RFC2874][RFC3226][RFC6563]
|
||||
DNAME = 39, // DNAME [RFC6672]
|
||||
SINK = 40, // SINK [Donald_E_Eastlake][draft-eastlake-kitchen-sink]
|
||||
OPT = 41, // OPT [RFC3225][RFC6891]
|
||||
APL = 42, // APL [RFC3123]
|
||||
DS = 43, // Delegation Signer [RFC4034]
|
||||
SSHFP = 44, // SSH Key Fingerprint [RFC4255]
|
||||
IPSECKEY = 45, // IPSECKEY [RFC4025]
|
||||
RRSIG = 46, // RRSIG [RFC4034]
|
||||
NSEC = 47, // NSEC [RFC4034][RFC9077]
|
||||
DNSKEY = 48, // DNSKEY [RFC4034]
|
||||
DHCID = 49, // DHCID [RFC4701]
|
||||
NSEC3 = 50, // NSEC3 [RFC5155][RFC9077]
|
||||
NSEC3PARAM = 51, // NSEC3PARAM [RFC5155]
|
||||
TLSA = 52, // TLSA [RFC6698]
|
||||
SMIMEA = 53, // S/MIME cert association [RFC8162]
|
||||
HIP = 55, // Host Identity Protocol [RFC8005]
|
||||
NINFO = 56, // NINFO [Jim_Reid]
|
||||
RKEY = 57, // RKEY [Jim_Reid]
|
||||
TALINK = 58, // Trust Anchor LINK [Wouter_Wijngaards]
|
||||
CDS = 59, // Child DS [RFC7344]
|
||||
CDNSKEY = 60, // DNSKEY(s) the Child wants reflected in DS [RFC7344]
|
||||
OPENPGPKEY = 61, // OpenPGP Key [RFC7929]
|
||||
CSYNC = 62, // Child-To-Parent Synchronization [RFC7477]
|
||||
ZONEMD = 63, // Message Digest Over Zone Data [RFC8976]
|
||||
SVCB = 64, // General-purpose service binding [RFC9460]
|
||||
HTTPS = 65, // SVCB-compatible type for use with HTTP [RFC9460]
|
||||
SPF = 99, // [RFC7208]
|
||||
UINFO = 100, // [IANA-Reserved]
|
||||
UID = 101, // [IANA-Reserved]
|
||||
GID = 102, // [IANA-Reserved]
|
||||
UNSPEC = 103, // [IANA-Reserved]
|
||||
NID = 104, // [RFC6742]
|
||||
L32 = 105, // [RFC6742]
|
||||
L64 = 106, // [RFC6742]
|
||||
LP = 107, // [RFC6742]
|
||||
EUI48 = 108, // an EUI-48 address [RFC7043]
|
||||
EUI64 = 109, // an EUI-64 address [RFC7043]
|
||||
NXNAME = 128, // NXDOMAIN indicator for Compact Denial of Existence [draft-ietf-dnsop-compact-denial-of-existence-04]
|
||||
TKEY = 249, // Transaction Key [RFC2930]
|
||||
TSIG = 250, // Transaction Signature [RFC8945]
|
||||
IXFR = 251, // incremental transfer [RFC1995]
|
||||
AXFR = 252, // transfer of an entire zone [RFC1035][RFC5936]
|
||||
MAILB = 253, // mailbox-related RRs (MB, MG or MR) [RFC1035]
|
||||
MAILA = 254, // mail agent RRs (OBSOLETE - see MX) [RFC1035]
|
||||
ANY = 255, // A request for some or all records the server has available [RFC1035][RFC6895][RFC8482]
|
||||
URI = 256, // URI [RFC7553]
|
||||
CAA = 257, // Certification Authority Restriction [RFC8659]
|
||||
AVC = 258, // Application Visibility and Control [Wolfgang_Riedel]
|
||||
DOA = 259, // Digital Object Architecture [draft-durand-doa-over-dns]
|
||||
AMTRELAY = 260, // Automatic Multicast Tunneling Relay [RFC8777]
|
||||
RESINFO = 261, // Resolver Information as Key/Value Pairs [RFC9606]
|
||||
WALLET = 262, // Public wallet address [Paul_Hoffman]
|
||||
CLA = 263, // BP Convergence Layer Adapter [draft-johnson-dns-ipn-cla-07]
|
||||
IPN = 264, // BP Node Number [draft-johnson-dns-ipn-cla-07]
|
||||
TA = 32768, // DNSSEC Trust Authorities "[Sam_Weiler][Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.]"
|
||||
DLV = 32769, // DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431]
|
||||
};
|
||||
StringView to_string(ResourceType);
|
||||
Optional<ResourceType> resource_type_from_string(StringView);
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2.
|
||||
enum class Class : u16 {
|
||||
IN = 1, // the Internet [RFC1035]
|
||||
CH = 3, // the CHAOS class [Moon1981]
|
||||
HS = 4, // Hesiod [Dyer1987]
|
||||
};
|
||||
StringView to_string(Class);
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-3.
|
||||
enum class OpCode : u8 {
|
||||
Query = 0, // a standard query (QUERY)
|
||||
IQuery = 1, // an inverse query (IQUERY)
|
||||
Status = 2, // a server status request (STATUS)
|
||||
Notify = 4, // NOTIFY
|
||||
Update = 5, // dynamic update (RFC 2136)
|
||||
DSO = 6, // DNS Stateful Operations (DSO) [RFC8490]
|
||||
Reserved = 7, // [RFC6895]
|
||||
ReservedMask = 15 // [RFC6895]
|
||||
};
|
||||
StringView to_string(OpCode);
|
||||
|
||||
namespace TLSA {
|
||||
|
||||
// Listings from IANA https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml.
|
||||
enum class CertUsage : u8 {
|
||||
CAConstraint = 0,
|
||||
ServiceCertificateConstraint = 1,
|
||||
TrustAnchorAssertion = 2,
|
||||
DomainIssuedCertificate = 3,
|
||||
Private = 255
|
||||
};
|
||||
enum class Selector : u8 {
|
||||
FullCertificate = 0,
|
||||
SubjectPublicKeyInfo = 1,
|
||||
Private = 255
|
||||
};
|
||||
enum class MatchingType : u8 {
|
||||
Full = 0,
|
||||
SHA256 = 1,
|
||||
SHA512 = 2,
|
||||
Private = 255
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DNSSEC {
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class Algorithm : u8 {
|
||||
RSAMD5 = 1, // RSA/MD5 [RFC4034][RFC3110]
|
||||
DSA = 3, // DSA/SHA-1 [RFC3755][RFC2536]
|
||||
RSASHA1 = 5, // RSA/SHA-1 [RFC3110]
|
||||
RSASHA1NSEC3SHA1 = 7, // [RFC5155]
|
||||
RSASHA256 = 8, // RSA/SHA-256 [RFC5702]
|
||||
RSASHA512 = 10, // RSA/SHA-512 [RFC5702]
|
||||
ECDSAP256SHA256 = 13, // ECDSA Curve P-256 with SHA-256 [RFC6605]
|
||||
ECDSAP384SHA384 = 14, // ECDSA Curve P-384 with SHA-384 [RFC6605]
|
||||
ED25519 = 15, // Ed25519 [RFC8080]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(Algorithm algorithm)
|
||||
{
|
||||
switch (algorithm) {
|
||||
case Algorithm::RSAMD5:
|
||||
return "RSAMD5"sv;
|
||||
case Algorithm::DSA:
|
||||
return "DSA"sv;
|
||||
case Algorithm::RSASHA1:
|
||||
return "RSASHA1"sv;
|
||||
case Algorithm::RSASHA1NSEC3SHA1:
|
||||
return "RSASHA1NSEC3SHA1"sv;
|
||||
case Algorithm::RSASHA256:
|
||||
return "RSASHA256"sv;
|
||||
case Algorithm::RSASHA512:
|
||||
return "RSASHA512"sv;
|
||||
case Algorithm::ECDSAP256SHA256:
|
||||
return "ECDSAP256SHA256"sv;
|
||||
case Algorithm::ECDSAP384SHA384:
|
||||
return "ECDSAP384SHA384"sv;
|
||||
case Algorithm::ED25519:
|
||||
return "ED25519"sv;
|
||||
case Algorithm::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class DigestType : u8 {
|
||||
SHA1 = 1, // SHA-1 [RFC3658]
|
||||
SHA256 = 2, // SHA-256 [RFC4509]
|
||||
GOST3411 = 3, // GOST R 34.11-94 [RFC5933]
|
||||
SHA384 = 4, // SHA-384 [RFC6605]
|
||||
SHA512 = 5, // SHA-512 [RFC6605]
|
||||
SHA224 = 6, // SHA-224 [RFC6605]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(DigestType digest_type)
|
||||
{
|
||||
switch (digest_type) {
|
||||
case DigestType::SHA1:
|
||||
return "SHA1"sv;
|
||||
case DigestType::SHA256:
|
||||
return "SHA256"sv;
|
||||
case DigestType::GOST3411:
|
||||
return "GOST3411"sv;
|
||||
case DigestType::SHA384:
|
||||
return "SHA384"sv;
|
||||
case DigestType::SHA512:
|
||||
return "SHA512"sv;
|
||||
case DigestType::SHA224:
|
||||
return "SHA224"sv;
|
||||
case DigestType::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Listing from IANA https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml.
|
||||
enum class NSEC3HashAlgorithm : u8 {
|
||||
SHA1 = 1, // [RFC5155]
|
||||
SHA256 = 2, // [RFC6605]
|
||||
GOST3411 = 3, // [RFC5933]
|
||||
SHA384 = 4, // [RFC6605]
|
||||
SHA512 = 5, // [RFC6605]
|
||||
SHA224 = 6, // [RFC6605]
|
||||
Unknown = 255 // Reserved for Private Use
|
||||
};
|
||||
|
||||
static inline StringView to_string(NSEC3HashAlgorithm hash_algorithm)
|
||||
{
|
||||
switch (hash_algorithm) {
|
||||
case NSEC3HashAlgorithm::SHA1:
|
||||
return "SHA1"sv;
|
||||
case NSEC3HashAlgorithm::SHA256:
|
||||
return "SHA256"sv;
|
||||
case NSEC3HashAlgorithm::GOST3411:
|
||||
return "GOST3411"sv;
|
||||
case NSEC3HashAlgorithm::SHA384:
|
||||
return "SHA384"sv;
|
||||
case NSEC3HashAlgorithm::SHA512:
|
||||
return "SHA512"sv;
|
||||
case NSEC3HashAlgorithm::SHA224:
|
||||
return "SHA224"sv;
|
||||
case NSEC3HashAlgorithm::Unknown:
|
||||
return "Unknown"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Question {
|
||||
DomainName name;
|
||||
ResourceType type;
|
||||
Class class_;
|
||||
|
||||
static ErrorOr<Question> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
};
|
||||
|
||||
namespace Records {
|
||||
|
||||
struct A {
|
||||
IPv4Address address;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::A;
|
||||
static ErrorOr<A> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return address.to_string(); }
|
||||
};
|
||||
struct AAAA {
|
||||
IPv6Address address;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::AAAA;
|
||||
static ErrorOr<AAAA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return address.to_string(); }
|
||||
};
|
||||
struct TXT {
|
||||
ByteString content;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::TXT;
|
||||
static ErrorOr<TXT> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("Text: '{}'", StringView { content }); }
|
||||
};
|
||||
struct CNAME {
|
||||
DomainName names;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::CNAME;
|
||||
static ErrorOr<CNAME> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return names.to_string(); }
|
||||
};
|
||||
struct NS {
|
||||
DomainName name;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NS;
|
||||
static ErrorOr<NS> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return name.to_string(); }
|
||||
};
|
||||
struct SOA {
|
||||
DomainName mname;
|
||||
DomainName rname;
|
||||
u32 serial;
|
||||
u32 refresh;
|
||||
u32 retry;
|
||||
u32 expire;
|
||||
u32 minimum;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SOA;
|
||||
static ErrorOr<SOA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
return String::formatted("SOA MName: '{}', RName: '{}', Serial: {}, Refresh: {}, Retry: {}, Expire: {}, Minimum: {}", mname.to_string(), rname.to_string(), serial, refresh, retry, expire, minimum);
|
||||
}
|
||||
};
|
||||
struct MX {
|
||||
u16 preference;
|
||||
DomainName exchange;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::MX;
|
||||
static ErrorOr<MX> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("MX Preference: {}, Exchange: '{}'", preference, exchange.to_string()); }
|
||||
};
|
||||
struct PTR {
|
||||
DomainName name;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::PTR;
|
||||
static ErrorOr<PTR> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return name.to_string(); }
|
||||
};
|
||||
struct SRV {
|
||||
u16 priority;
|
||||
u16 weight;
|
||||
u16 port;
|
||||
DomainName target;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SRV;
|
||||
static ErrorOr<SRV> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("SRV Priority: {}, Weight: {}, Port: {}, Target: '{}'", priority, weight, port, target.to_string()); }
|
||||
};
|
||||
struct DNSKEY {
|
||||
u16 flags;
|
||||
u8 protocol;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
ByteBuffer public_key;
|
||||
|
||||
constexpr static inline u16 FlagSecureEntryPoint = 0b1000000000000000;
|
||||
constexpr static inline u16 FlagZoneKey = 0b0100000000000000;
|
||||
constexpr static inline u16 FlagRevoked = 0b0010000000000000;
|
||||
|
||||
constexpr bool is_secure_entry_point() const { return flags & FlagSecureEntryPoint; }
|
||||
constexpr bool is_zone_key() const { return flags & FlagZoneKey; }
|
||||
constexpr bool is_revoked() const { return flags & FlagRevoked; }
|
||||
constexpr bool is_key_signing_key() const { return is_secure_entry_point() && is_zone_key() && !is_revoked(); }
|
||||
|
||||
static constexpr ResourceType type = ResourceType::DNSKEY;
|
||||
static ErrorOr<DNSKEY> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
return String::formatted("DNSKEY Flags: {}{}{}{}({}), Protocol: {}, Algorithm: {}, Public Key: {}",
|
||||
is_secure_entry_point() ? "sep "sv : ""sv,
|
||||
is_zone_key() ? "zone "sv : ""sv,
|
||||
is_revoked() ? "revoked "sv : ""sv,
|
||||
is_key_signing_key() ? "ksk "sv : ""sv,
|
||||
flags,
|
||||
protocol,
|
||||
DNSSEC::to_string(algorithm),
|
||||
TRY(encode_base64(public_key)));
|
||||
}
|
||||
};
|
||||
struct CDNSKEY : public DNSKEY {
|
||||
template<typename... Ts>
|
||||
CDNSKEY(Ts&&... args)
|
||||
: DNSKEY(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr ResourceType type = ResourceType::CDNSKEY;
|
||||
static ErrorOr<CDNSKEY> from_raw(ParseContext& raw) { return DNSKEY::from_raw(raw); }
|
||||
};
|
||||
struct DS {
|
||||
u16 key_tag;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
DNSSEC::DigestType digest_type;
|
||||
ByteBuffer digest;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::DS;
|
||||
static ErrorOr<DS> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "DS"_string; }
|
||||
};
|
||||
struct CDS : public DS {
|
||||
template<typename... Ts>
|
||||
CDS(Ts&&... args)
|
||||
: DS(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
static constexpr ResourceType type = ResourceType::CDS;
|
||||
static ErrorOr<CDS> from_raw(ParseContext& raw) { return DS::from_raw(raw); }
|
||||
};
|
||||
struct SIG {
|
||||
ResourceType type_covered;
|
||||
DNSSEC::Algorithm algorithm;
|
||||
u8 label_count;
|
||||
u32 original_ttl;
|
||||
UnixDateTime expiration;
|
||||
UnixDateTime inception;
|
||||
u16 key_tag;
|
||||
DomainName signers_name;
|
||||
ByteBuffer signature;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::SIG;
|
||||
static ErrorOr<SIG> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const;
|
||||
};
|
||||
struct RRSIG : public SIG {
|
||||
template<typename... Ts>
|
||||
RRSIG(Ts&&... args)
|
||||
: SIG(forward<Ts>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr ResourceType type = ResourceType::RRSIG;
|
||||
static ErrorOr<RRSIG> from_raw(ParseContext& raw) { return SIG::from_raw(raw); }
|
||||
};
|
||||
struct NSEC {
|
||||
DomainName next_domain_name;
|
||||
Vector<ResourceType> types;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC;
|
||||
static ErrorOr<NSEC> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC"_string; }
|
||||
};
|
||||
struct NSEC3 {
|
||||
DNSSEC::NSEC3HashAlgorithm hash_algorithm;
|
||||
u8 flags;
|
||||
u16 iterations;
|
||||
ByteBuffer salt;
|
||||
DomainName next_hashed_owner_name;
|
||||
Vector<ResourceType> types;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC3;
|
||||
static ErrorOr<NSEC3> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC3"_string; }
|
||||
};
|
||||
struct NSEC3PARAM {
|
||||
DNSSEC::NSEC3HashAlgorithm hash_algorithm;
|
||||
u8 flags;
|
||||
u16 iterations;
|
||||
ByteBuffer salt;
|
||||
|
||||
constexpr static inline u8 FlagOptOut = 0b10000000;
|
||||
|
||||
constexpr bool is_opt_out() const { return flags & FlagOptOut; }
|
||||
|
||||
static constexpr ResourceType type = ResourceType::NSEC3PARAM;
|
||||
static ErrorOr<NSEC3PARAM> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "NSEC3PARAM"_string; }
|
||||
};
|
||||
struct TLSA {
|
||||
Messages::TLSA::CertUsage cert_usage;
|
||||
Messages::TLSA::Selector selector;
|
||||
Messages::TLSA::MatchingType matching_type;
|
||||
ByteBuffer certificate_association_data;
|
||||
|
||||
static ErrorOr<TLSA> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return "TLSA"_string; }
|
||||
};
|
||||
struct HINFO {
|
||||
ByteString cpu;
|
||||
ByteString os;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::HINFO;
|
||||
static ErrorOr<HINFO> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const { return Error::from_string_literal("Not implemented"); }
|
||||
ErrorOr<String> to_string() const { return String::formatted("HINFO CPU: '{}', OS: '{}'", StringView { cpu }, StringView { os }); }
|
||||
};
|
||||
struct OPT {
|
||||
struct Option {
|
||||
u16 code;
|
||||
ByteBuffer data;
|
||||
};
|
||||
|
||||
// 1 1 1 1 1 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | UDP Payload Size |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | Extended RCode | VER | ZZ |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |DO| Z |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | OPT-LEN / OPT-DATA...
|
||||
|
||||
NetworkOrdered<u16> udp_payload_size { 0 };
|
||||
NetworkOrdered<u32> extended_rcode_and_flags { 0 };
|
||||
Vector<Option> options;
|
||||
static constexpr u32 MaskExtendedRCode = 0b11111111000000000000000000000000;
|
||||
static constexpr u32 MaskVersion = 0b00000000111100000000000000000000;
|
||||
static constexpr u32 MaskDO = 0b00000000000000001000000000000000;
|
||||
|
||||
static constexpr ResourceType type = ResourceType::OPT;
|
||||
|
||||
constexpr u8 extended_rcode() const { return (extended_rcode_and_flags & MaskExtendedRCode) >> 24; }
|
||||
constexpr u8 version() const { return (extended_rcode_and_flags & MaskVersion) >> 20; }
|
||||
constexpr bool dnssec_ok() const { return extended_rcode_and_flags & MaskDO; }
|
||||
|
||||
void set_extended_rcode(u8 value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskExtendedRCode) | (value << 24); }
|
||||
void set_version(u8 value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskVersion) | (value << 20); }
|
||||
void set_dnssec_ok(bool value) { extended_rcode_and_flags = (extended_rcode_and_flags & ~MaskDO) | (value ? MaskDO : 0); }
|
||||
|
||||
static ErrorOr<OPT> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
ErrorOr<String> to_string() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.appendff("OPT UDP Payload Size: {}, Extended RCode: {}, Version: {}, DNSSEC OK: {}", udp_payload_size, extended_rcode(), version(), dnssec_ok());
|
||||
for (auto& option : options)
|
||||
builder.appendff(", opt[{} = '{:hex-dump}']", option.code, option.data.bytes());
|
||||
|
||||
return builder.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using Record = Variant<
|
||||
Records::A,
|
||||
Records::AAAA,
|
||||
Records::TXT,
|
||||
Records::CNAME,
|
||||
Records::NS,
|
||||
Records::SOA,
|
||||
Records::MX,
|
||||
Records::PTR,
|
||||
Records::SRV,
|
||||
Records::DNSKEY,
|
||||
Records::CDNSKEY,
|
||||
Records::DS,
|
||||
Records::CDS,
|
||||
Records::RRSIG,
|
||||
Records::NSEC,
|
||||
Records::NSEC3,
|
||||
Records::NSEC3PARAM,
|
||||
Records::TLSA,
|
||||
Records::HINFO,
|
||||
Records::OPT,
|
||||
// TODO: Add more records.
|
||||
ByteBuffer>; // Fallback for unknown records.
|
||||
|
||||
struct ResourceRecord {
|
||||
DomainName name;
|
||||
ResourceType type;
|
||||
Class class_;
|
||||
u32 ttl;
|
||||
Record record;
|
||||
Optional<ByteBuffer> raw;
|
||||
|
||||
static ErrorOr<ResourceRecord> from_raw(ParseContext&);
|
||||
ErrorOr<void> to_raw(ByteBuffer&) const;
|
||||
ErrorOr<String> to_string() const;
|
||||
};
|
||||
|
||||
struct ZoneAuthority {
|
||||
DomainName name;
|
||||
ByteString admin_mailbox;
|
||||
u32 serial;
|
||||
u32 refresh;
|
||||
u32 retry;
|
||||
u32 expire;
|
||||
u32 minimum_ttl;
|
||||
};
|
||||
|
||||
struct Message {
|
||||
Header header;
|
||||
Vector<Question> questions;
|
||||
Vector<ResourceRecord> answers;
|
||||
Vector<ResourceRecord> authorities;
|
||||
Vector<ResourceRecord> additional_records;
|
||||
|
||||
static ErrorOr<Message> from_raw(ParseContext&);
|
||||
static ErrorOr<Message> from_raw(Stream&);
|
||||
ErrorOr<size_t> to_raw(ByteBuffer&) const;
|
||||
|
||||
ErrorOr<String> format_for_log() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
489
Libraries/LibDNS/Resolver.h
Normal file
489
Libraries/LibDNS/Resolver.h
Normal file
|
@ -0,0 +1,489 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/AtomicRefCounted.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/MaybeOwned.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibCore/Promise.h>
|
||||
#include <LibCore/SocketAddress.h>
|
||||
#include <LibDNS/Message.h>
|
||||
#include <LibThreading/MutexProtected.h>
|
||||
#include <LibThreading/RWLockProtected.h>
|
||||
|
||||
namespace DNS {
|
||||
class Resolver;
|
||||
|
||||
class LookupResult : public AtomicRefCounted<LookupResult>
|
||||
, public Weakable<LookupResult> {
|
||||
public:
|
||||
explicit LookupResult(Messages::DomainName name)
|
||||
: m_name(move(name))
|
||||
{
|
||||
}
|
||||
|
||||
Vector<Variant<IPv4Address, IPv6Address>> cached_addresses() const
|
||||
{
|
||||
Vector<Variant<IPv4Address, IPv6Address>> result;
|
||||
for (auto& re : m_cached_records) {
|
||||
re.record.record.visit(
|
||||
[&](Messages::Records::A const& a) { result.append(a.address); },
|
||||
[&](Messages::Records::AAAA const& aaaa) { result.append(aaaa.address); },
|
||||
[](auto&) {});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void check_expiration()
|
||||
{
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
auto now = Core::DateTime::now();
|
||||
for (size_t i = 0; i < m_cached_records.size();) {
|
||||
auto& record = m_cached_records[i];
|
||||
if (record.expiration.has_value() && record.expiration.value() < now) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Removing expired record for {}", m_name.to_string());
|
||||
m_cached_records.remove(i);
|
||||
} else {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Keeping record for {} (expires in {})", m_name.to_string(), record.expiration.has_value() ? record.expiration.value().to_string() : "never"_string);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cached_records.is_empty())
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
void add_record(Messages::ResourceRecord record)
|
||||
{
|
||||
m_valid = true;
|
||||
auto expiration = record.ttl > 0 ? Optional<Core::DateTime>(Core::DateTime::from_timestamp(Core::DateTime::now().timestamp() + record.ttl)) : OptionalNone();
|
||||
m_cached_records.append({ move(record), move(expiration) });
|
||||
}
|
||||
|
||||
Vector<Messages::ResourceRecord> records() const
|
||||
{
|
||||
Vector<Messages::ResourceRecord> result;
|
||||
for (auto& re : m_cached_records)
|
||||
result.append(re.record);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool has_record_of_type(Messages::ResourceType type, bool later = false) const
|
||||
{
|
||||
if (later && m_desired_types.contains(type))
|
||||
return true;
|
||||
|
||||
for (auto const& re : m_cached_records) {
|
||||
if (re.record.type == type)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void will_add_record_of_type(Messages::ResourceType type) { m_desired_types.set(type); }
|
||||
|
||||
void set_id(u16 id) { m_id = id; }
|
||||
u16 id() { return m_id; }
|
||||
|
||||
bool is_valid() const { return m_valid; }
|
||||
Messages::DomainName const& name() const { return m_name; }
|
||||
|
||||
private:
|
||||
bool m_valid { false };
|
||||
Messages::DomainName m_name;
|
||||
struct RecordWithExpiration {
|
||||
Messages::ResourceRecord record;
|
||||
Optional<Core::DateTime> expiration;
|
||||
};
|
||||
Vector<RecordWithExpiration> m_cached_records;
|
||||
HashTable<Messages::ResourceType> m_desired_types;
|
||||
u16 m_id { 0 };
|
||||
};
|
||||
|
||||
class Resolver {
|
||||
public:
|
||||
enum class ConnectionMode {
|
||||
TCP,
|
||||
UDP,
|
||||
};
|
||||
|
||||
struct SocketResult {
|
||||
MaybeOwned<Core::Socket> socket;
|
||||
ConnectionMode mode;
|
||||
};
|
||||
|
||||
Resolver(Function<ErrorOr<SocketResult>()> create_socket)
|
||||
: m_pending_lookups(make<RedBlackTree<u16, PendingLookup>>())
|
||||
, m_create_socket(move(create_socket))
|
||||
{
|
||||
m_cache.with_write_locked([&](auto& cache) {
|
||||
auto add_v4v6_entry = [&cache](StringView name_string, IPv4Address v4, IPv6Address v6) {
|
||||
auto name = Messages::DomainName::from_string(name_string);
|
||||
auto ptr = make_ref_counted<LookupResult>(name);
|
||||
ptr->will_add_record_of_type(Messages::ResourceType::A);
|
||||
ptr->will_add_record_of_type(Messages::ResourceType::AAAA);
|
||||
cache.set(name_string, ptr);
|
||||
|
||||
ptr->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { v4 }, .raw = {} });
|
||||
ptr->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { v6 }, .raw = {} });
|
||||
};
|
||||
|
||||
add_v4v6_entry("localhost"sv, { 127, 0, 0, 1 }, IPv6Address::loopback());
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<Empty>> when_socket_ready()
|
||||
{
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
m_socket_ready_promises.append(promise);
|
||||
if (has_connection(false)) {
|
||||
promise->resolve({});
|
||||
return promise;
|
||||
}
|
||||
|
||||
if (!has_connection())
|
||||
promise->reject(Error::from_string_literal("Failed to create socket"));
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void reset_connection()
|
||||
{
|
||||
m_socket.with_write_locked([&](auto& socket) { socket = {}; });
|
||||
}
|
||||
|
||||
NonnullRefPtr<LookupResult const> expect_cached(StringView name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return expect_cached(name, class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
NonnullRefPtr<LookupResult const> expect_cached(StringView name, Messages::Class class_, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
auto result = lookup_in_cache(name, class_, desired_types);
|
||||
VERIFY(!result.is_null());
|
||||
dbgln_if(DNS_DEBUG, "DNS::expect({}) -> OK", name);
|
||||
return *result;
|
||||
}
|
||||
|
||||
RefPtr<LookupResult const> lookup_in_cache(StringView name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return lookup_in_cache(name, class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
RefPtr<LookupResult const> lookup_in_cache(StringView name, Messages::Class, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
return m_cache.with_read_locked([&](auto& cache) -> RefPtr<LookupResult const> {
|
||||
auto it = cache.find(name);
|
||||
if (it == cache.end())
|
||||
return {};
|
||||
|
||||
auto& result = *it->value;
|
||||
for (auto const& type : desired_types) {
|
||||
if (!result.has_record_of_type(type))
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> lookup(ByteString name, Messages::Class class_ = Messages::Class::IN)
|
||||
{
|
||||
return lookup(move(name), class_, Array { Messages::ResourceType::A, Messages::ResourceType::AAAA });
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> lookup(ByteString name, Messages::Class class_, Span<Messages::ResourceType const> desired_types)
|
||||
{
|
||||
flush_cache();
|
||||
|
||||
auto promise = Core::Promise<NonnullRefPtr<LookupResult const>>::construct();
|
||||
|
||||
if (auto maybe_ipv4 = IPv4Address::from_string(name); maybe_ipv4.has_value()) {
|
||||
if (desired_types.contains_slow(Messages::ResourceType::A)) {
|
||||
auto result = make_ref_counted<LookupResult>(Messages::DomainName {});
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { maybe_ipv4.release_value() }, .raw = {} });
|
||||
promise->resolve(move(result));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto maybe_ipv6 = IPv6Address::from_string(name); maybe_ipv6.has_value()) {
|
||||
if (desired_types.contains_slow(Messages::ResourceType::AAAA)) {
|
||||
auto result = make_ref_counted<LookupResult>(Messages::DomainName {});
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { maybe_ipv6.release_value() }, .raw = {} });
|
||||
promise->resolve(move(result));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto result = lookup_in_cache(name, class_, desired_types)) {
|
||||
promise->resolve(result.release_nonnull());
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto domain_name = Messages::DomainName::from_string(name);
|
||||
|
||||
if (!has_connection()) {
|
||||
// Use system resolver
|
||||
// FIXME: Use an underlying resolver instead.
|
||||
dbgln_if(DNS_DEBUG, "Not ready to resolve, using system resolver and skipping cache for {}", name);
|
||||
auto record_or_error = Core::Socket::resolve_host(name, Core::Socket::SocketType::Stream);
|
||||
if (record_or_error.is_error()) {
|
||||
promise->reject(record_or_error.release_error());
|
||||
return promise;
|
||||
}
|
||||
auto result = make_ref_counted<LookupResult>(domain_name);
|
||||
auto record = record_or_error.release_value();
|
||||
record.visit(
|
||||
[&](IPv4Address const& address) {
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::A, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::A { address }, .raw = {} });
|
||||
},
|
||||
[&](IPv6Address const& address) {
|
||||
result->add_record({ .name = {}, .type = Messages::ResourceType::AAAA, .class_ = Messages::Class::IN, .ttl = 0, .record = Messages::Records::AAAA { address }, .raw = {} });
|
||||
});
|
||||
promise->resolve(result);
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto already_in_cache = false;
|
||||
auto result = m_cache.with_write_locked([&](auto& cache) -> NonnullRefPtr<LookupResult> {
|
||||
auto existing = [&] -> RefPtr<LookupResult> {
|
||||
if (cache.contains(name)) {
|
||||
auto ptr = *cache.get(name);
|
||||
|
||||
already_in_cache = true;
|
||||
for (auto const& type : desired_types) {
|
||||
if (!ptr->has_record_of_type(type, true)) {
|
||||
already_in_cache = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (existing)
|
||||
return *existing;
|
||||
|
||||
auto ptr = make_ref_counted<LookupResult>(domain_name);
|
||||
for (auto const& type : desired_types)
|
||||
ptr->will_add_record_of_type(type);
|
||||
cache.set(name, ptr);
|
||||
return ptr;
|
||||
});
|
||||
|
||||
Optional<u16> cached_result_id;
|
||||
if (already_in_cache) {
|
||||
auto id = result->id();
|
||||
cached_result_id = id;
|
||||
auto existing_promise = m_pending_lookups.with_write_locked([&](auto& lookups) -> RefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> {
|
||||
if (auto* lookup = lookups->find(id))
|
||||
return lookup->promise;
|
||||
return nullptr;
|
||||
});
|
||||
if (existing_promise)
|
||||
return existing_promise.release_nonnull();
|
||||
|
||||
promise->resolve(*result);
|
||||
return promise;
|
||||
}
|
||||
|
||||
Messages::Message query;
|
||||
m_pending_lookups.with_read_locked([&](auto& lookups) {
|
||||
do
|
||||
fill_with_random({ &query.header.id, sizeof(query.header.id) });
|
||||
while (lookups->find(query.header.id) != nullptr);
|
||||
});
|
||||
query.header.question_count = max(1u, desired_types.size());
|
||||
query.header.options.set_response_code(Messages::Options::ResponseCode::NoError);
|
||||
query.header.options.set_recursion_desired(true);
|
||||
query.header.options.set_op_code(Messages::OpCode::Query);
|
||||
for (auto const& type : desired_types) {
|
||||
query.questions.append(Messages::Question {
|
||||
.name = domain_name,
|
||||
.type = type,
|
||||
.class_ = class_,
|
||||
});
|
||||
}
|
||||
|
||||
if (query.questions.is_empty()) {
|
||||
query.questions.append(Messages::Question {
|
||||
.name = Messages::DomainName::from_string(name),
|
||||
.type = Messages::ResourceType::A,
|
||||
.class_ = class_,
|
||||
});
|
||||
}
|
||||
|
||||
auto cached_entry = m_pending_lookups.with_write_locked([&](auto& pending_lookups) -> RefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> {
|
||||
// One more try to make sure we're not overwriting an existing lookup
|
||||
if (cached_result_id.has_value()) {
|
||||
if (auto* lookup = pending_lookups->find(*cached_result_id))
|
||||
return lookup->promise;
|
||||
}
|
||||
|
||||
pending_lookups->insert(query.header.id, { query.header.id, name, result->make_weak_ptr(), promise });
|
||||
return nullptr;
|
||||
});
|
||||
if (cached_entry) {
|
||||
dbgln_if(DNS_DEBUG, "DNS::lookup({}) -> Already in cache", name);
|
||||
return cached_entry.release_nonnull();
|
||||
}
|
||||
|
||||
ByteBuffer query_bytes;
|
||||
MUST(query.to_raw(query_bytes));
|
||||
|
||||
if (m_mode == ConnectionMode::TCP) {
|
||||
auto original_query_bytes = query_bytes;
|
||||
query_bytes = MUST(ByteBuffer::create_uninitialized(query_bytes.size() + sizeof(u16)));
|
||||
NetworkOrdered<u16> size = original_query_bytes.size();
|
||||
query_bytes.overwrite(0, &size, sizeof(size));
|
||||
query_bytes.overwrite(sizeof(size), original_query_bytes.data(), original_query_bytes.size());
|
||||
}
|
||||
|
||||
auto write_result = m_socket.with_write_locked([&](auto& socket) {
|
||||
return (*socket)->write_until_depleted(query_bytes.bytes());
|
||||
});
|
||||
if (write_result.is_error()) {
|
||||
promise->reject(write_result.release_error());
|
||||
return promise;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private:
|
||||
struct PendingLookup {
|
||||
u16 id { 0 };
|
||||
ByteString name;
|
||||
WeakPtr<LookupResult> result;
|
||||
NonnullRefPtr<Core::Promise<NonnullRefPtr<LookupResult const>>> promise;
|
||||
};
|
||||
|
||||
ErrorOr<Messages::Message> parse_one_message()
|
||||
{
|
||||
if (m_mode == ConnectionMode::UDP)
|
||||
return m_socket.with_write_locked([&](auto& socket) { return Messages::Message::from_raw(**socket); });
|
||||
|
||||
return m_socket.with_write_locked([&](auto& socket) -> ErrorOr<Messages::Message> {
|
||||
if (!TRY((*socket)->can_read_without_blocking()))
|
||||
return Error::from_errno(EAGAIN);
|
||||
|
||||
auto size = TRY((*socket)->template read_value<NetworkOrdered<u16>>());
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(size));
|
||||
TRY((*socket)->read_until_filled(buffer));
|
||||
FixedMemoryStream stream { static_cast<ReadonlyBytes>(buffer) };
|
||||
return Messages::Message::from_raw(stream);
|
||||
});
|
||||
}
|
||||
|
||||
void process_incoming_messages()
|
||||
{
|
||||
while (true) {
|
||||
if (auto result = m_socket.with_read_locked([](auto& socket) { return (*socket)->can_read_without_blocking(); }); result.is_error() || !result.value())
|
||||
break;
|
||||
auto message_or_err = parse_one_message();
|
||||
if (message_or_err.is_error()) {
|
||||
if (!message_or_err.error().is_errno() || message_or_err.error().code() != EAGAIN)
|
||||
dbgln("DNS: Failed to receive message: {}", message_or_err.error());
|
||||
break;
|
||||
}
|
||||
|
||||
auto message = message_or_err.release_value();
|
||||
auto result = m_pending_lookups.with_write_locked([&](auto& lookups) -> ErrorOr<void> {
|
||||
auto* lookup = lookups->find(message.header.id);
|
||||
if (!lookup)
|
||||
return Error::from_string_literal("No pending lookup found for this message");
|
||||
|
||||
if (lookup->result.is_null())
|
||||
return {}; // Message is a response to a lookup that's been purged from the cache, ignore it
|
||||
|
||||
auto result = lookup->result.strong_ref();
|
||||
for (auto& record : message.answers)
|
||||
result->add_record(move(record));
|
||||
|
||||
lookup->promise->resolve(*result);
|
||||
lookups->remove(message.header.id);
|
||||
return {};
|
||||
});
|
||||
if (result.is_error()) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Received a message with no pending lookup: {}", result.error());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_connection(bool attempt_restart = true)
|
||||
{
|
||||
auto result = m_socket.with_read_locked(
|
||||
[&](auto& socket) { return socket.has_value() && (*socket)->is_open(); });
|
||||
|
||||
if (attempt_restart && !result && !m_attempting_restart) {
|
||||
TemporaryChange change(m_attempting_restart, true);
|
||||
auto create_result = m_create_socket();
|
||||
if (create_result.is_error()) {
|
||||
dbgln_if(DNS_DEBUG, "DNS: Failed to create socket: {}", create_result.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto [socket, mode] = MUST(move(create_result));
|
||||
set_socket(move(socket), mode);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void set_socket(MaybeOwned<Core::Socket> socket, ConnectionMode mode = ConnectionMode::UDP)
|
||||
{
|
||||
m_mode = mode;
|
||||
m_socket.with_write_locked([&](auto& s) {
|
||||
s = move(socket);
|
||||
(*s)->on_ready_to_read = [this] {
|
||||
process_incoming_messages();
|
||||
};
|
||||
(*s)->set_notifications_enabled(true);
|
||||
});
|
||||
|
||||
for (auto& promise : m_socket_ready_promises)
|
||||
promise->resolve({});
|
||||
|
||||
m_socket_ready_promises.clear();
|
||||
}
|
||||
|
||||
void flush_cache()
|
||||
{
|
||||
m_cache.with_write_locked([&](auto& cache) {
|
||||
HashTable<ByteString> to_remove;
|
||||
for (auto& entry : cache) {
|
||||
entry.value->check_expiration();
|
||||
if (!entry.value->is_valid())
|
||||
to_remove.set(entry.key);
|
||||
}
|
||||
for (auto const& key : to_remove)
|
||||
cache.remove(key);
|
||||
});
|
||||
}
|
||||
|
||||
Threading::RWLockProtected<HashMap<ByteString, NonnullRefPtr<LookupResult>>> m_cache;
|
||||
Threading::RWLockProtected<NonnullOwnPtr<RedBlackTree<u16, PendingLookup>>> m_pending_lookups;
|
||||
Threading::RWLockProtected<Optional<MaybeOwned<Core::Socket>>> m_socket;
|
||||
Function<ErrorOr<SocketResult>()> m_create_socket;
|
||||
bool m_attempting_restart { false };
|
||||
ConnectionMode m_mode { ConnectionMode::UDP };
|
||||
Vector<NonnullRefPtr<Core::Promise<Empty>>> m_socket_ready_promises;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,7 +1,12 @@
|
|||
set(SOURCES
|
||||
FileSystem.cpp
|
||||
TempFile.cpp
|
||||
)
|
||||
set (SOURCES FileSystem.cpp)
|
||||
if (NOT WIN32)
|
||||
list(APPEND SOURCES TempFile.cpp)
|
||||
endif()
|
||||
|
||||
serenity_lib(LibFileSystem filesystem)
|
||||
target_link_libraries(LibFileSystem PRIVATE LibCoreMinimal)
|
||||
|
||||
if (WIN32)
|
||||
find_path(DIRENT_INCLUDE_DIR dirent.h REQUIRED)
|
||||
target_include_directories(LibFileSystem PRIVATE ${DIRENT_INCLUDE_DIR})
|
||||
endif()
|
||||
|
|
|
@ -10,12 +10,13 @@
|
|||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if !defined(AK_OS_IOS) && defined(AK_OS_BSD_GENERIC)
|
||||
# include <sys/disk.h>
|
||||
#elif defined(AK_OS_LINUX)
|
||||
# include <linux/fs.h>
|
||||
#elif defined(AK_OS_WINDOWS)
|
||||
# include <dirent.h>
|
||||
#endif
|
||||
|
||||
// On Linux distros that use glibc `basename` is defined as a macro that expands to `__xpg_basename`, so we undefine it
|
||||
|
@ -32,16 +33,19 @@ ErrorOr<ByteString> current_working_directory()
|
|||
|
||||
ErrorOr<ByteString> absolute_path(StringView path)
|
||||
{
|
||||
#ifndef AK_OS_WINDOWS
|
||||
if (exists(path))
|
||||
return real_path(path);
|
||||
#endif
|
||||
|
||||
if (path.starts_with("/"sv))
|
||||
if (LexicalPath::is_absolute_path(path))
|
||||
return LexicalPath::canonicalized_path(path);
|
||||
|
||||
auto working_directory = TRY(current_working_directory());
|
||||
return LexicalPath::absolute_path(working_directory, path);
|
||||
}
|
||||
|
||||
#ifndef AK_OS_WINDOWS
|
||||
ErrorOr<ByteString> real_path(StringView path)
|
||||
{
|
||||
if (path.is_null())
|
||||
|
@ -56,6 +60,13 @@ ErrorOr<ByteString> real_path(StringView path)
|
|||
|
||||
return ByteString { real_path, strlen(real_path) };
|
||||
}
|
||||
#else
|
||||
// NOTE: real_path on Windows does not resolve symlinks
|
||||
ErrorOr<ByteString> real_path(StringView path)
|
||||
{
|
||||
return absolute_path(path);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool exists(StringView path)
|
||||
{
|
||||
|
@ -67,60 +78,6 @@ bool exists(int fd)
|
|||
return !Core::System::fstat(fd).is_error();
|
||||
}
|
||||
|
||||
bool is_device(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::stat(path);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_device(int fd)
|
||||
{
|
||||
auto st_or_error = Core::System::fstat(fd);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_block_device(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::stat(path);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISBLK(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_block_device(int fd)
|
||||
{
|
||||
auto st_or_error = Core::System::fstat(fd);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISBLK(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_char_device(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::stat(path);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISCHR(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_char_device(int fd)
|
||||
{
|
||||
auto st_or_error = Core::System::fstat(fd);
|
||||
if (st_or_error.is_error())
|
||||
return false;
|
||||
auto st = st_or_error.release_value();
|
||||
return S_ISCHR(st.st_mode);
|
||||
}
|
||||
|
||||
bool is_regular_file(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::stat(path);
|
||||
|
@ -157,6 +114,16 @@ bool is_directory(int fd)
|
|||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
#ifdef AK_OS_WINDOWS
|
||||
bool is_link(StringView path)
|
||||
{
|
||||
ByteString string_path = path;
|
||||
auto attr = GetFileAttributes(string_path.characters());
|
||||
if (attr == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
return attr & FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
}
|
||||
#else
|
||||
bool is_link(StringView path)
|
||||
{
|
||||
auto st_or_error = Core::System::lstat(path);
|
||||
|
@ -338,6 +305,34 @@ ErrorOr<void> move_file(StringView destination_path, StringView source_path, Pre
|
|||
return Core::System::unlink(source_path);
|
||||
}
|
||||
|
||||
bool can_delete_or_move(StringView path)
|
||||
{
|
||||
VERIFY(!path.is_empty());
|
||||
auto directory = LexicalPath::dirname(path);
|
||||
auto directory_has_write_access = !Core::System::access(directory, W_OK).is_error();
|
||||
if (!directory_has_write_access)
|
||||
return false;
|
||||
|
||||
auto stat_or_empty = [](StringView path) {
|
||||
auto stat_or_error = Core::System::stat(path);
|
||||
if (stat_or_error.is_error()) {
|
||||
struct stat stat { };
|
||||
return stat;
|
||||
}
|
||||
return stat_or_error.release_value();
|
||||
};
|
||||
|
||||
auto directory_stat = stat_or_empty(directory);
|
||||
bool is_directory_sticky = (directory_stat.st_mode & S_ISVTX) != 0;
|
||||
if (!is_directory_sticky)
|
||||
return true;
|
||||
|
||||
// Directory is sticky, only the file owner, directory owner, and root can modify (rename, remove) it.
|
||||
auto user_id = geteuid();
|
||||
return user_id == 0 || directory_stat.st_uid == user_id || stat_or_empty(path).st_uid == user_id;
|
||||
}
|
||||
#endif // !AK_OS_WINDOWS
|
||||
|
||||
ErrorOr<void> remove(StringView path, RecursionMode mode)
|
||||
{
|
||||
if (is_directory(path) && mode == RecursionMode::Allowed) {
|
||||
|
@ -368,88 +363,4 @@ ErrorOr<off_t> size_from_fstat(int fd)
|
|||
return st.st_size;
|
||||
}
|
||||
|
||||
ErrorOr<off_t> block_device_size_from_ioctl(StringView path)
|
||||
{
|
||||
if (!path.characters_without_null_termination())
|
||||
return Error::from_syscall("ioctl"sv, -EFAULT);
|
||||
|
||||
ByteString path_string = path;
|
||||
int fd = open(path_string.characters(), O_RDONLY);
|
||||
|
||||
if (fd < 0)
|
||||
return Error::from_errno(errno);
|
||||
|
||||
off_t size = TRY(block_device_size_from_ioctl(fd));
|
||||
|
||||
if (close(fd) != 0)
|
||||
return Error::from_errno(errno);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
ErrorOr<off_t> block_device_size_from_ioctl(int fd)
|
||||
{
|
||||
#if defined(AK_OS_MACOS)
|
||||
u64 block_count = 0;
|
||||
u32 block_size = 0;
|
||||
TRY(Core::System::ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count));
|
||||
TRY(Core::System::ioctl(fd, DKIOCGETBLOCKSIZE, &block_size));
|
||||
return static_cast<off_t>(block_count * block_size);
|
||||
#elif defined(AK_OS_FREEBSD) || defined(AK_OS_NETBSD)
|
||||
off_t size = 0;
|
||||
TRY(Core::System::ioctl(fd, DIOCGMEDIASIZE, &size));
|
||||
return size;
|
||||
#elif defined(AK_OS_LINUX)
|
||||
u64 size = 0;
|
||||
TRY(Core::System::ioctl(fd, BLKGETSIZE64, &size));
|
||||
return static_cast<off_t>(size);
|
||||
#else
|
||||
// FIXME: Add support for more platforms.
|
||||
(void)fd;
|
||||
return Error::from_string_literal("Platform does not support getting block device size");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool can_delete_or_move(StringView path)
|
||||
{
|
||||
VERIFY(!path.is_empty());
|
||||
auto directory = LexicalPath::dirname(path);
|
||||
auto directory_has_write_access = !Core::System::access(directory, W_OK).is_error();
|
||||
if (!directory_has_write_access)
|
||||
return false;
|
||||
|
||||
auto stat_or_empty = [](StringView path) {
|
||||
auto stat_or_error = Core::System::stat(path);
|
||||
if (stat_or_error.is_error()) {
|
||||
struct stat stat { };
|
||||
return stat;
|
||||
}
|
||||
return stat_or_error.release_value();
|
||||
};
|
||||
|
||||
auto directory_stat = stat_or_empty(directory);
|
||||
bool is_directory_sticky = (directory_stat.st_mode & S_ISVTX) != 0;
|
||||
if (!is_directory_sticky)
|
||||
return true;
|
||||
|
||||
// Directory is sticky, only the file owner, directory owner, and root can modify (rename, remove) it.
|
||||
auto user_id = geteuid();
|
||||
return user_id == 0 || directory_stat.st_uid == user_id || stat_or_empty(path).st_uid == user_id;
|
||||
}
|
||||
|
||||
ErrorOr<ByteString> read_link(StringView link_path)
|
||||
{
|
||||
return Core::System::readlink(link_path);
|
||||
}
|
||||
|
||||
ErrorOr<void> link_file(StringView destination_path, StringView source_path)
|
||||
{
|
||||
return TRY(Core::System::symlink(source_path, TRY(get_duplicate_file_name(destination_path))));
|
||||
}
|
||||
|
||||
bool looks_like_shared_library(StringView path)
|
||||
{
|
||||
return path.ends_with(".so"sv) || path.contains(".so."sv);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <AK/Error.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace FileSystem {
|
||||
|
||||
|
@ -31,15 +30,6 @@ bool is_regular_file(int fd);
|
|||
bool is_directory(StringView path);
|
||||
bool is_directory(int fd);
|
||||
|
||||
bool is_device(StringView path);
|
||||
bool is_device(int fd);
|
||||
|
||||
bool is_block_device(StringView path);
|
||||
bool is_block_device(int fd);
|
||||
|
||||
bool is_char_device(StringView path);
|
||||
bool is_char_device(int fd);
|
||||
|
||||
bool is_link(StringView path);
|
||||
bool is_link(int fd);
|
||||
|
||||
|
@ -73,13 +63,6 @@ ErrorOr<void> move_file(StringView destination_path, StringView source_path, Pre
|
|||
ErrorOr<void> remove(StringView path, RecursionMode);
|
||||
ErrorOr<off_t> size_from_stat(StringView path);
|
||||
ErrorOr<off_t> size_from_fstat(int fd);
|
||||
ErrorOr<off_t> block_device_size_from_ioctl(StringView path);
|
||||
ErrorOr<off_t> block_device_size_from_ioctl(int fd);
|
||||
bool can_delete_or_move(StringView path);
|
||||
|
||||
ErrorOr<ByteString> read_link(StringView link_path);
|
||||
ErrorOr<void> link_file(StringView destination_path, StringView source_path);
|
||||
|
||||
bool looks_like_shared_library(StringView path);
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ set(SOURCES
|
|||
Cell.cpp
|
||||
CellAllocator.cpp
|
||||
ConservativeVector.cpp
|
||||
ForeignCell.cpp
|
||||
Root.cpp
|
||||
Heap.cpp
|
||||
HeapBlock.cpp
|
||||
|
@ -12,3 +13,12 @@ set(SOURCES
|
|||
|
||||
serenity_lib(LibGC gc)
|
||||
target_link_libraries(LibGC PRIVATE LibCore)
|
||||
|
||||
if (ENABLE_SWIFT)
|
||||
generate_clang_module_map(LibGC)
|
||||
target_sources(LibGC PRIVATE
|
||||
Heap+Swift.swift
|
||||
)
|
||||
target_link_libraries(LibGC PRIVATE AK)
|
||||
add_swift_target_properties(LibGC LAGOM_LIBRARIES AK)
|
||||
endif()
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Swift.h>
|
||||
#include <LibGC/Heap.h>
|
||||
|
||||
namespace GC {
|
||||
|
@ -25,6 +26,6 @@ public:
|
|||
|
||||
private:
|
||||
Heap& m_heap;
|
||||
};
|
||||
} SWIFT_NONCOPYABLE;
|
||||
|
||||
}
|
||||
|
|
61
Libraries/LibGC/ForeignCell.cpp
Normal file
61
Libraries/LibGC/ForeignCell.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibGC/DeferGC.h>
|
||||
#include <LibGC/ForeignCell.h>
|
||||
#include <LibGC/Heap.h>
|
||||
|
||||
namespace GC {
|
||||
|
||||
void* ForeignCell::foreign_data()
|
||||
{
|
||||
// !!!
|
||||
auto offset = round_up_to_power_of_two(sizeof(ForeignCell), m_vtable.alignment);
|
||||
return static_cast<void*>(reinterpret_cast<u8*>(this) + offset);
|
||||
}
|
||||
|
||||
ForeignCell::ForeignCell(ForeignCell::Vtable vtable)
|
||||
: m_vtable(move(vtable))
|
||||
{
|
||||
if (m_vtable.initialize)
|
||||
m_vtable.initialize(foreign_data(), m_vtable.class_metadata_pointer, *this);
|
||||
}
|
||||
|
||||
ForeignCell::~ForeignCell()
|
||||
{
|
||||
if (m_vtable.destroy)
|
||||
m_vtable.destroy(foreign_data(), m_vtable.class_metadata_pointer);
|
||||
}
|
||||
|
||||
Ref<ForeignCell> ForeignCell::create(Heap& heap, size_t size, ForeignCell::Vtable vtable)
|
||||
{
|
||||
// NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
|
||||
// up looking for the Cell pointer on the stack or in a register when it might only exist in the heap.
|
||||
// We can't guarantee that the ForeignCell will be stashed in a proper ForeignRef/ForeignPtr or similar
|
||||
// foreign type until after all the dust has settled on both sides of the FFI boundary.
|
||||
VERIFY(heap.is_gc_deferred());
|
||||
VERIFY(is_power_of_two(vtable.alignment));
|
||||
auto& allocator = heap.allocator_for_size(sizeof(ForeignCell) + round_up_to_power_of_two(size, vtable.alignment));
|
||||
auto* memory = allocator.allocate_cell(heap);
|
||||
auto* foreign_cell = new (memory) ForeignCell(move(vtable));
|
||||
return *foreign_cell;
|
||||
}
|
||||
|
||||
void ForeignCell::finalize()
|
||||
{
|
||||
Base::finalize();
|
||||
if (m_vtable.finalize)
|
||||
m_vtable.finalize(foreign_data(), m_vtable.class_metadata_pointer);
|
||||
}
|
||||
|
||||
void ForeignCell::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
if (m_vtable.visit_edges)
|
||||
m_vtable.visit_edges(foreign_data(), m_vtable.class_metadata_pointer, visitor);
|
||||
}
|
||||
|
||||
}
|
180
Libraries/LibGC/ForeignCell.h
Normal file
180
Libraries/LibGC/ForeignCell.h
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Swift.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGC/Cell.h>
|
||||
#include <LibGC/DeferGC.h>
|
||||
|
||||
namespace GC {
|
||||
|
||||
template<typename T>
|
||||
struct ForeignRef;
|
||||
|
||||
template<typename T>
|
||||
struct ForeignPtr;
|
||||
|
||||
#define FOREIGN_CELL(class_, base_class) \
|
||||
using Base = base_class; \
|
||||
friend class GC::Heap;
|
||||
|
||||
class ForeignCell : public Cell {
|
||||
FOREIGN_CELL(ForeignCell, Cell);
|
||||
|
||||
public:
|
||||
struct Vtable {
|
||||
// Holds a pointer to the foreign vtable information such as
|
||||
// a jclass in Java, or a Swift type metadata pointer
|
||||
void* class_metadata_pointer = nullptr;
|
||||
|
||||
// FIXME: FlyString? The class name must be owned by the ForeignCell so it can vend StringViews
|
||||
// We should properly cache the name and class info pointer to avoid string churn
|
||||
String class_name;
|
||||
|
||||
size_t alignment { 1 };
|
||||
|
||||
void (*initialize)(void* thiz, void* clazz, Ref<Cell>);
|
||||
void (*destroy)(void* thiz, void* clazz);
|
||||
void (*finalize)(void* thiz, void* clazz);
|
||||
void (*visit_edges)(void* thiz, void* clazz, Cell::Visitor&);
|
||||
};
|
||||
static Ref<ForeignCell> create(Heap&, size_t size, Vtable);
|
||||
|
||||
void* foreign_data() SWIFT_RETURNS_INDEPENDENT_VALUE; // technically lying to swift, but it's fiiiiine
|
||||
|
||||
// ^Cell
|
||||
virtual void finalize() override;
|
||||
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||
virtual StringView class_name() const override { return m_vtable.class_name; }
|
||||
|
||||
~ForeignCell();
|
||||
|
||||
private:
|
||||
ForeignCell(Vtable vtable);
|
||||
|
||||
Vtable m_vtable;
|
||||
} SWIFT_IMMORTAL_REFERENCE;
|
||||
|
||||
template<typename T>
|
||||
struct ForeignRef {
|
||||
friend struct ForeignPtr<T>;
|
||||
|
||||
template<typename... Args>
|
||||
static ForeignRef allocate(Heap& heap, Args... args)
|
||||
{
|
||||
DeferGC const defer_gc(heap);
|
||||
auto* cell = T::create(&heap, forward<Args>(args)...);
|
||||
if constexpr (IsSame<decltype(cell), Cell*>) {
|
||||
return ForeignRef(*verify_cast<ForeignCell>(cell));
|
||||
} else {
|
||||
static_assert(IsSame<decltype(cell), void*>);
|
||||
auto* cast_cell = static_cast<Cell*>(cell);
|
||||
return ForeignRef(*verify_cast<ForeignCell>(cast_cell));
|
||||
}
|
||||
}
|
||||
|
||||
ForeignRef() = delete;
|
||||
|
||||
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
|
||||
ForeignRef(ForeignCell& cell)
|
||||
: m_cell(cell)
|
||||
{
|
||||
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
|
||||
m_data = static_cast<T*>(m_cell->foreign_data());
|
||||
}
|
||||
|
||||
~ForeignRef() = default;
|
||||
ForeignRef(ForeignRef const& other) = default;
|
||||
ForeignRef& operator=(ForeignRef const& other) = default;
|
||||
|
||||
RETURNS_NONNULL T* operator->() const { return m_data; }
|
||||
[[nodiscard]] T& operator*() const { return *m_data; }
|
||||
|
||||
RETURNS_NONNULL T* ptr() const { return m_data; }
|
||||
RETURNS_NONNULL operator T*() const { return m_data; }
|
||||
|
||||
operator T&() const { return *m_data; }
|
||||
|
||||
Ref<ForeignCell> cell() const { return m_cell; }
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_cell);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<ForeignCell> m_cell;
|
||||
T* m_data { nullptr };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ForeignPtr {
|
||||
constexpr ForeignPtr() = default;
|
||||
|
||||
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
|
||||
ForeignPtr(ForeignCell& cell)
|
||||
: m_cell(&cell)
|
||||
{
|
||||
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
|
||||
m_data = static_cast<T*>(m_cell->foreign_data());
|
||||
}
|
||||
|
||||
// This constructor should only be called directly after allocating a foreign cell by calling an FFI create method
|
||||
ForeignPtr(ForeignCell* cell)
|
||||
: m_cell(cell)
|
||||
{
|
||||
// FIXME: This is super dangerous. How can we assert that the cell is actually a T?
|
||||
m_data = m_cell ? static_cast<T*>(m_cell->foreign_data()) : nullptr;
|
||||
}
|
||||
|
||||
ForeignPtr(ForeignRef<T> const& other)
|
||||
: m_cell(other.m_cell)
|
||||
, m_data(other.m_data)
|
||||
{
|
||||
}
|
||||
|
||||
ForeignPtr(nullptr_t)
|
||||
: m_cell(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ForeignPtr(ForeignPtr const& other) = default;
|
||||
ForeignPtr& operator=(ForeignPtr const& other) = default;
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
ASSERT(m_cell && m_data);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] T& operator*() const
|
||||
{
|
||||
ASSERT(m_cell && m_data);
|
||||
return *m_data;
|
||||
}
|
||||
|
||||
operator T*() const { return m_data; }
|
||||
T* ptr() const { return m_data; }
|
||||
|
||||
explicit operator bool() const { return !!m_cell; }
|
||||
bool operator!() const { return !m_cell; }
|
||||
|
||||
Ptr<ForeignCell> cell() const { return m_cell; }
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_cell);
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<ForeignCell> m_cell;
|
||||
T* m_data { nullptr };
|
||||
};
|
||||
|
||||
}
|
|
@ -6,11 +6,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace GC {
|
||||
|
||||
class Cell;
|
||||
class CellAllocator;
|
||||
class DeferGC;
|
||||
class ForeignCell;
|
||||
class RootImpl;
|
||||
class Heap;
|
||||
class HeapBlock;
|
||||
|
|
105
Libraries/LibGC/Heap+Swift.swift
Normal file
105
Libraries/LibGC/Heap+Swift.swift
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
import AK
|
||||
@_exported import GCCxx
|
||||
|
||||
extension GC.Heap {
|
||||
public func withDeferredGC<R, E>(_ body: () throws(E) -> R) throws(E) -> R {
|
||||
let deferredRAII = GC.DeferGC(self)
|
||||
_ = deferredRAII
|
||||
return try body()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Cell and Cell::Visitor are not imported properly, so we have to treat them as OpaquePointer
|
||||
public protocol HeapAllocatable {
|
||||
static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self>
|
||||
|
||||
init(cell: OpaquePointer)
|
||||
|
||||
func finalize()
|
||||
func visitEdges(_ visitor: OpaquePointer)
|
||||
|
||||
var cell: OpaquePointer { get }
|
||||
}
|
||||
|
||||
// FIXME: Figure out why other modules can't conform to HeapAllocatable
|
||||
public struct HeapString: HeapAllocatable {
|
||||
public var string: Swift.String
|
||||
|
||||
public init(cell: OpaquePointer) {
|
||||
self.cell = cell
|
||||
self.string = ""
|
||||
}
|
||||
|
||||
// FIXME: HeapAllocatable cannot be exposed to C++ yet, so we're off to void* paradise
|
||||
public static func create(on heap: GC.Heap, string: Swift.String) -> OpaquePointer {
|
||||
// NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
|
||||
// up looking for the Cell pointer on the stack or in a register when it might only exist in the heap
|
||||
precondition(heap.is_gc_deferred())
|
||||
let heapString = allocate(on: heap)
|
||||
heapString.pointee.string = string
|
||||
return heapString.pointee.cell
|
||||
}
|
||||
|
||||
public var cell: OpaquePointer
|
||||
}
|
||||
|
||||
// Here be dragons
|
||||
|
||||
func asTypeMetadataPointer(_ type: Any.Type) -> UnsafeMutableRawPointer {
|
||||
unsafeBitCast(type, to: UnsafeMutableRawPointer.self)
|
||||
}
|
||||
|
||||
func asHeapAllocatableType(_ typeMetadata: UnsafeMutableRawPointer) -> any HeapAllocatable.Type {
|
||||
let typeObject = unsafeBitCast(typeMetadata, to: Any.Type.self)
|
||||
guard let type = typeObject as? any HeapAllocatable.Type else {
|
||||
fatalError("Passed foreign class but it wasn't a Swift type!")
|
||||
}
|
||||
return type
|
||||
}
|
||||
|
||||
extension HeapAllocatable {
|
||||
fileprivate static func initializeFromFFI(at this: UnsafeMutableRawPointer, cell: OpaquePointer) {
|
||||
this.assumingMemoryBound(to: Self.self).initialize(to: Self.self.init(cell: cell))
|
||||
}
|
||||
|
||||
fileprivate static func destroyFromFFI(at this: UnsafeMutableRawPointer) {
|
||||
this.assumingMemoryBound(to: Self.self).deinitialize(count: 1)
|
||||
}
|
||||
|
||||
fileprivate static func finalizeFromFFI(at this: UnsafeMutableRawPointer) {
|
||||
this.assumingMemoryBound(to: Self.self).pointee.finalize()
|
||||
}
|
||||
|
||||
fileprivate static func visitEdgesFromFFI(at this: UnsafeMutableRawPointer, visitor: OpaquePointer) {
|
||||
this.assumingMemoryBound(to: Self.self).pointee.visitEdges(visitor)
|
||||
}
|
||||
|
||||
public static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self> {
|
||||
let vtable = GC.ForeignCell.Vtable(
|
||||
class_metadata_pointer: asTypeMetadataPointer(Self.self),
|
||||
class_name: AK.String(swiftString: Swift.String(describing: Self.self)),
|
||||
alignment: MemoryLayout<Self>.alignment,
|
||||
initialize: { this, typeMetadata, cell in
|
||||
asHeapAllocatableType(typeMetadata!).initializeFromFFI(at: this!, cell: cell.ptr())
|
||||
},
|
||||
destroy: { this, typeMetadata in
|
||||
asHeapAllocatableType(typeMetadata!).destroyFromFFI(at: this!)
|
||||
},
|
||||
finalize: { this, typeMetadata in
|
||||
asHeapAllocatableType(typeMetadata!).finalizeFromFFI(at: this!)
|
||||
},
|
||||
visit_edges: nil
|
||||
)
|
||||
let cell = GC.ForeignCell.create(heap, MemoryLayout<Self>.stride, vtable)
|
||||
return cell.pointee.foreign_data().assumingMemoryBound(to: Self.self)
|
||||
}
|
||||
|
||||
public func finalize() {}
|
||||
public func visitEdges(_ visitor: OpaquePointer) {}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/StackInfo.h>
|
||||
#include <AK/Swift.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/Forward.h>
|
||||
|
@ -73,10 +74,13 @@ public:
|
|||
|
||||
void uproot_cell(Cell* cell);
|
||||
|
||||
bool is_gc_deferred() const { return m_gc_deferrals > 0; }
|
||||
|
||||
private:
|
||||
friend class MarkingVisitor;
|
||||
friend class GraphConstructorVisitor;
|
||||
friend class DeferGC;
|
||||
friend class ForeignCell;
|
||||
|
||||
void defer_gc();
|
||||
void undefer_gc();
|
||||
|
@ -147,7 +151,7 @@ private:
|
|||
bool m_collecting_garbage { false };
|
||||
StackInfo m_stack_info;
|
||||
AK::Function<void(HashMap<Cell*, GC::HeapRoot>&)> m_gather_embedder_roots;
|
||||
};
|
||||
} SWIFT_IMMORTAL_REFERENCE;
|
||||
|
||||
inline void Heap::did_create_root(Badge<RootImpl>, RootImpl& impl)
|
||||
{
|
||||
|
|
|
@ -10,11 +10,7 @@
|
|||
# pragma GCC optimize("O3")
|
||||
#endif
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/Line.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/LineStyle.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -7,16 +7,8 @@
|
|||
*/
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibCore/MimeData.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
#include <LibGfx/ShareableBitmap.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -105,30 +97,6 @@ ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_wrapper(BitmapFormat format, Alpha
|
|||
return adopt_ref(*new Bitmap(format, alpha_type, size, pitch, data, move(destruction_callback)));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(StringView path, Optional<IntSize> ideal_size)
|
||||
{
|
||||
auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read));
|
||||
return load_from_file(move(file), path, ideal_size);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(NonnullOwnPtr<Core::File> file, StringView path, Optional<IntSize> ideal_size)
|
||||
{
|
||||
auto mapped_file = TRY(Core::MappedFile::map_from_file(move(file), path));
|
||||
auto mime_type = Core::guess_mime_type_based_on_filename(path);
|
||||
return load_from_bytes(mapped_file->bytes(), ideal_size, mime_type);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_bytes(ReadonlyBytes bytes, Optional<IntSize> ideal_size, Optional<ByteString> mine_type)
|
||||
{
|
||||
if (auto decoder = TRY(ImageDecoder::try_create_for_raw_bytes(bytes, mine_type))) {
|
||||
auto frame = TRY(decoder->frame(0, ideal_size));
|
||||
if (auto& bitmap = frame.image)
|
||||
return bitmap.release_nonnull();
|
||||
}
|
||||
|
||||
return Error::from_string_literal("Gfx::Bitmap unable to load from file");
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, IntSize size, size_t pitch, void* data, Function<void()>&& destruction_callback)
|
||||
: m_size(size)
|
||||
, m_data(data)
|
||||
|
|
|
@ -7,11 +7,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
|
@ -70,9 +68,6 @@ public:
|
|||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create(BitmapFormat, AlphaType, IntSize);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_shareable(BitmapFormat, AlphaType, IntSize);
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_wrapper(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function<void()>&& destruction_callback = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(StringView path, Optional<IntSize> ideal_size = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_file(NonnullOwnPtr<Core::File>, StringView path, Optional<IntSize> ideal_size = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> load_from_bytes(ReadonlyBytes, Optional<IntSize> ideal_size = {}, Optional<ByteString> mine_type = {});
|
||||
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_anonymous_buffer(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize);
|
||||
|
||||
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> clone() const;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/BitmapSequence.h>
|
||||
|
|
|
@ -73,6 +73,7 @@ set(SOURCES
|
|||
set(SWIFT_EXCLUDE_HEADERS
|
||||
MetalContext.h
|
||||
VulkanContext.h
|
||||
SkiaUtils.h
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <AK/Swift.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <ctype.h>
|
||||
|
@ -399,7 +398,9 @@ Vector<Color> Color::tints(u32 steps, float max) const
|
|||
Color Color::from_linear_srgb(float red, float green, float blue, float alpha)
|
||||
{
|
||||
auto linear_to_srgb = [](float c) {
|
||||
return c >= 0.0031308f ? 1.055f * pow(c, 0.4166666f) - 0.055f : 12.92f * c;
|
||||
if (c <= 0.04045 / 12.92)
|
||||
return c * 12.92;
|
||||
return pow(c, 10. / 24) * 1.055 - 0.055;
|
||||
};
|
||||
|
||||
red = linear_to_srgb(red) * 255.f;
|
||||
|
@ -413,24 +414,112 @@ Color Color::from_linear_srgb(float red, float green, float blue, float alpha)
|
|||
clamp(lroundf(alpha * 255.f), 0, 255));
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#predefined-a98-rgb
|
||||
Color Color::from_a98rgb(float r, float g, float b, float alpha)
|
||||
{
|
||||
auto to_linear = [](float c) {
|
||||
return pow(c, 563. / 256);
|
||||
};
|
||||
|
||||
auto linear_r = to_linear(r);
|
||||
auto linear_g = to_linear(g);
|
||||
auto linear_b = to_linear(b);
|
||||
|
||||
float x = 0.57666904 * linear_r + 0.18555824 * linear_g + 0.18822865 * linear_b;
|
||||
float y = 0.29734498 * linear_r + 0.62736357 * linear_g + 0.07529146 * linear_b;
|
||||
float z = 0.02703136 * linear_r + 0.07068885 * linear_g + 0.99133754 * linear_b;
|
||||
|
||||
return from_xyz65(x, y, z, alpha);
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#predefined-a98-rgb
|
||||
Color Color::from_display_p3(float r, float g, float b, float alpha)
|
||||
{
|
||||
auto to_linear = [](float c) {
|
||||
if (c < 0.04045)
|
||||
return c / 12.92;
|
||||
return pow((c + 0.055) / (1.055), 2.4);
|
||||
};
|
||||
|
||||
auto linear_r = to_linear(r);
|
||||
auto linear_g = to_linear(g);
|
||||
auto linear_b = to_linear(b);
|
||||
|
||||
float x = 0.48657095 * linear_r + 0.26566769 * linear_g + 0.19821729 * linear_b;
|
||||
float y = 0.22897456 * linear_r + 0.69173852 * linear_g + 0.07928691 * linear_b;
|
||||
float z = 0.00000000 * linear_r + 0.04511338 * linear_g + 1.04394437 * linear_b;
|
||||
|
||||
return from_xyz65(x, y, z, alpha);
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#predefined-prophoto-rgb
|
||||
Color Color::from_pro_photo_rgb(float r, float g, float b, float alpha)
|
||||
{
|
||||
auto to_linear = [](float c) -> float {
|
||||
u8 sign = c < 0 ? -1 : 1;
|
||||
float absolute = abs(c);
|
||||
|
||||
if (absolute <= 16. / 252)
|
||||
return c / 16;
|
||||
return sign * pow(c, 1.8);
|
||||
};
|
||||
|
||||
auto linear_r = to_linear(r);
|
||||
auto linear_g = to_linear(g);
|
||||
auto linear_b = to_linear(b);
|
||||
|
||||
float x = 0.79776664 * linear_r + 0.13518130 * linear_g + 0.03134773 * linear_b;
|
||||
float y = 0.28807483 * linear_r + 0.71183523 * linear_g + 0.00008994 * linear_b;
|
||||
float z = 0.00000000 * linear_r + 0.00000000 * linear_g + 0.82510460 * linear_b;
|
||||
|
||||
return from_xyz50(x, y, z, alpha);
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#predefined-rec2020
|
||||
Color Color::from_rec2020(float r, float g, float b, float alpha)
|
||||
{
|
||||
auto to_linear = [](float c) -> float {
|
||||
auto constexpr alpha = 1.09929682680944;
|
||||
auto constexpr beta = 0.018053968510807;
|
||||
|
||||
u8 sign = c < 0 ? -1 : 1;
|
||||
auto absolute = abs(c);
|
||||
|
||||
if (absolute < beta * 4.5)
|
||||
return c / 4.5;
|
||||
|
||||
return sign * (pow((absolute + alpha - 1) / alpha, 1 / 0.45));
|
||||
};
|
||||
|
||||
auto linear_r = to_linear(r);
|
||||
auto linear_g = to_linear(g);
|
||||
auto linear_b = to_linear(b);
|
||||
|
||||
float x = 0.63695805 * linear_r + 0.14461690 * linear_g + 0.16888098 * linear_b;
|
||||
float y = 0.26270021 * linear_r + 0.67799807 * linear_g + 0.05930172 * linear_b;
|
||||
float z = 0.00000000 * linear_r + 0.02807269 * linear_g + 1.06098506 * linear_b;
|
||||
|
||||
return from_xyz65(x, y, z, alpha);
|
||||
}
|
||||
|
||||
Color Color::from_xyz50(float x, float y, float z, float alpha)
|
||||
{
|
||||
// See commit description for these values
|
||||
float red = 3.13397926 * x - 1.61689519 * y - 0.49070587 * z;
|
||||
float green = -0.97840009 * x + 1.91589112 * y + 0.03339256 * z;
|
||||
float blue = 0.07200357 * x - 0.22897505 * y + 1.40517398 * z;
|
||||
// See commit description for these values.
|
||||
float r = +3.134136 * x - 1.617386 * y - 0.490662 * z;
|
||||
float g = -0.978795 * x + 1.916254 * y + 0.033443 * z;
|
||||
float b = +0.071955 * x - 0.228977 * y + 1.405386 * z;
|
||||
|
||||
return from_linear_srgb(red, green, blue, alpha);
|
||||
return from_linear_srgb(r, g, b, alpha);
|
||||
}
|
||||
|
||||
Color Color::from_xyz65(float x, float y, float z, float alpha)
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/SRGB#From_CIE_XYZ_to_sRGB
|
||||
float red = 3.2406 * x - 1.5372 * y - 0.4986 * z;
|
||||
float green = -0.9689 * x + 1.8758 * y + 0.0415 * z;
|
||||
float blue = 0.0557 * x - 0.2040 * y + 1.0570 * z;
|
||||
// See commit description for these values.
|
||||
float r = +3.240970 * x - 1.537383 * y - 0.498611 * z;
|
||||
float g = -0.969244 * x + 1.875968 * y + 0.041555 * z;
|
||||
float b = +0.055630 * x - 0.203977 * y + 1.056972 * z;
|
||||
|
||||
return from_linear_srgb(red, green, blue, alpha);
|
||||
return from_linear_srgb(r, g, b, alpha);
|
||||
}
|
||||
|
||||
Color Color::from_lab(float L, float a, float b, float alpha)
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <AK/Format.h>
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/SIMD.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
|
@ -182,10 +181,14 @@ public:
|
|||
return Color(r_u8, g_u8, b_u8, a_u8);
|
||||
}
|
||||
|
||||
static Color from_a98rgb(float r, float g, float b, float alpha = 1.0f);
|
||||
static Color from_display_p3(float r, float g, float b, float alpha = 1.0f);
|
||||
static Color from_lab(float L, float a, float b, float alpha = 1.0f);
|
||||
static Color from_linear_srgb(float r, float g, float b, float alpha = 1.0f);
|
||||
static Color from_pro_photo_rgb(float r, float g, float b, float alpha = 1.0f);
|
||||
static Color from_rec2020(float r, float g, float b, float alpha = 1.0f);
|
||||
static Color from_xyz50(float x, float y, float z, float alpha = 1.0f);
|
||||
static Color from_xyz65(float x, float y, float z, float alpha = 1.0f);
|
||||
static Color from_linear_srgb(float x, float y, float z, float alpha = 1.0f);
|
||||
|
||||
// https://bottosson.github.io/posts/oklab/
|
||||
static constexpr Color from_oklab(float L, float a, float b, float alpha = 1.0f)
|
||||
|
@ -397,7 +400,8 @@ public:
|
|||
|
||||
constexpr Color with_opacity(float opacity) const
|
||||
{
|
||||
return with_alpha(alpha() * opacity);
|
||||
VERIFY(opacity >= 0 && opacity <= 1);
|
||||
return with_alpha(static_cast<u8>(round(alpha() * opacity)));
|
||||
}
|
||||
|
||||
constexpr Color darkened(float amount = 0.5f) const
|
||||
|
@ -546,9 +550,9 @@ public:
|
|||
break;
|
||||
}
|
||||
|
||||
u8 out_r = (u8)(r * 255);
|
||||
u8 out_g = (u8)(g * 255);
|
||||
u8 out_b = (u8)(b * 255);
|
||||
auto out_r = static_cast<u8>(round(r * 255));
|
||||
auto out_g = static_cast<u8>(round(g * 255));
|
||||
auto out_b = static_cast<u8>(round(b * 255));
|
||||
return Color(out_r, out_g, out_b);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Math.h>
|
||||
#include <LibGfx/DeltaE.h>
|
||||
#include <math.h>
|
||||
|
|
|
@ -12,18 +12,13 @@
|
|||
|
||||
#include "DeprecatedPainter.h"
|
||||
#include "Bitmap.h"
|
||||
#include "Font/Font.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Math.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Stack.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(AK_COMPILER_GCC)
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
|
@ -16,8 +15,6 @@
|
|||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -6,12 +6,9 @@
|
|||
|
||||
#include <AK/Math.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibGfx/BoundingBox.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/DeprecatedPath.h>
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/IntegralMath.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
|
|
|
@ -7,15 +7,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/ByteReader.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/MappedFile.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Size.h>
|
||||
|
||||
struct hb_font_t;
|
||||
|
||||
|
@ -85,7 +80,7 @@ public:
|
|||
virtual float width(StringView) const = 0;
|
||||
virtual float width(Utf8View const&) const = 0;
|
||||
|
||||
virtual FlyString family() const = 0;
|
||||
virtual FlyString const& family() const = 0;
|
||||
|
||||
virtual NonnullRefPtr<Font> with_size(float point_size) const = 0;
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <AK/FlyString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
|
||||
|
@ -37,7 +35,7 @@ public:
|
|||
virtual u8 baseline() const override { return m_point_height; } // FIXME: Read from font
|
||||
virtual float width(StringView) const override;
|
||||
virtual float width(Utf8View const&) const override;
|
||||
virtual FlyString family() const override { return m_typeface->family(); }
|
||||
virtual FlyString const& family() const override { return m_typeface->family(); }
|
||||
|
||||
virtual NonnullRefPtr<ScaledFont> scaled_with_size(float point_size) const;
|
||||
virtual NonnullRefPtr<Font> with_size(float point_size) const override;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <core/SkTypeface.h>
|
||||
#include <harfbuzz/hb.h>
|
||||
|
||||
#include <LibGfx/Font/ScaledFont.h>
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/FontData.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
|
@ -47,7 +45,7 @@ public:
|
|||
virtual u32 glyph_count() const = 0;
|
||||
virtual u16 units_per_em() const = 0;
|
||||
virtual u32 glyph_id_for_code_point(u32 code_point) const = 0;
|
||||
virtual FlyString family() const = 0;
|
||||
virtual FlyString const& family() const = 0;
|
||||
virtual u16 weight() const = 0;
|
||||
virtual u16 width() const = 0;
|
||||
virtual u8 slope() const = 0;
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
#include <AK/LsanSuppressions.h>
|
||||
#include <LibGfx/Font/FontDatabase.h>
|
||||
#include <LibGfx/Font/Typeface.h>
|
||||
#include <LibGfx/Font/TypefaceSkia.h>
|
||||
|
||||
#include <core/SkData.h>
|
||||
#include <core/SkFontMgr.h>
|
||||
#include <core/SkRefCnt.h>
|
||||
#include <core/SkTypeface.h>
|
||||
#ifndef AK_OS_ANDROID
|
||||
# include <ports/SkFontMgr_fontconfig.h>
|
||||
|
@ -114,7 +112,7 @@ void TypefaceSkia::populate_glyph_page(GlyphPage& glyph_page, size_t page_index)
|
|||
}
|
||||
}
|
||||
|
||||
FlyString TypefaceSkia::family() const
|
||||
FlyString const& TypefaceSkia::family() const
|
||||
{
|
||||
if (!m_family.has_value()) {
|
||||
SkString family_name;
|
||||
|
|
|
@ -19,7 +19,7 @@ public:
|
|||
virtual u32 glyph_count() const override;
|
||||
virtual u16 units_per_em() const override;
|
||||
virtual u32 glyph_id_for_code_point(u32 code_point) const override;
|
||||
virtual FlyString family() const override;
|
||||
virtual FlyString const& family() const override;
|
||||
virtual u16 weight() const override;
|
||||
virtual u16 width() const override;
|
||||
virtual u8 slope() const override;
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
|
||||
namespace Gfx::ICC {
|
||||
|
||||
class Profile;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <LibGfx/ICC/Profile.h>
|
||||
#include <LibGfx/ICC/Tags.h>
|
||||
#include <LibGfx/ICC/WellKnownProfiles.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace Gfx::ICC {
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <LibCompress/Lzw.h>
|
||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||
#include <LibGfx/ImageFormats/ICOLoader.h>
|
||||
#include <LibGfx/ImageFormats/PNGLoader.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibGfx/ImageFormats/AVIFLoader.h>
|
||||
#include <LibGfx/ImageFormats/BMPLoader.h>
|
||||
#include <LibGfx/ImageFormats/GIFLoader.h>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Error.h>
|
||||
#include <LibGfx/ImageFormats/JPEGXLLoader.h>
|
||||
#include <jxl/decode.h>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <AK/LEB128.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/DeprecatedPainter.h>
|
||||
#include <LibGfx/ImageFormats/TinyVGLoader.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/ImageFormats/WebPWriterLossless.h>
|
||||
#include <LibGfx/Point.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
|
||||
#include <AK/BitStream.h>
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibCompress/DeflateTables.h>
|
||||
#include <LibCompress/Huffman.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageFormats/WebPSharedLossless.h>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
#include <LibGfx/SkiaUtils.h>
|
||||
|
||||
#include <core/SkBitmap.h>
|
||||
#include <core/SkImage.h>
|
||||
|
@ -60,23 +61,6 @@ Color ImmutableBitmap::get_pixel(int x, int y) const
|
|||
return m_impl->source.get<NonnullRefPtr<Gfx::Bitmap>>()->get_pixel(x, y);
|
||||
}
|
||||
|
||||
static SkColorType to_skia_color_type(Gfx::BitmapFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case Gfx::BitmapFormat::Invalid:
|
||||
return kUnknown_SkColorType;
|
||||
case Gfx::BitmapFormat::BGRA8888:
|
||||
case Gfx::BitmapFormat::BGRx8888:
|
||||
return kBGRA_8888_SkColorType;
|
||||
case Gfx::BitmapFormat::RGBA8888:
|
||||
return kRGBA_8888_SkColorType;
|
||||
case Gfx::BitmapFormat::RGBx8888:
|
||||
return kRGB_888x_SkColorType;
|
||||
default:
|
||||
return kUnknown_SkColorType;
|
||||
}
|
||||
}
|
||||
|
||||
static SkAlphaType to_skia_alpha_type(Gfx::AlphaType alpha_type)
|
||||
{
|
||||
switch (alpha_type) {
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <AK/Types.h>
|
||||
#include <initializer_list>
|
||||
|
||||
|
|
|
@ -13,20 +13,9 @@
|
|||
#include <LibGfx/PathSkia.h>
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <core/SkBitmap.h>
|
||||
#include <core/SkBlurTypes.h>
|
||||
#include <core/SkCanvas.h>
|
||||
#include <core/SkColorFilter.h>
|
||||
#include <core/SkMaskFilter.h>
|
||||
#include <core/SkPath.h>
|
||||
#include <core/SkPathBuilder.h>
|
||||
#include <core/SkRRect.h>
|
||||
#include <core/SkSurface.h>
|
||||
#include <effects/SkGradientShader.h>
|
||||
#include <effects/SkImageFilters.h>
|
||||
#include <gpu/GrDirectContext.h>
|
||||
#include <gpu/ganesh/SkSurfaceGanesh.h>
|
||||
#include <pathops/SkPathOps.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue