# HG changeset patch # User Paper # Date 1721178959 14400 # Node ID 99c961c91809d8ee536fc4c734ccd346fad2b213 # Parent f10507d8f686c8b90a5481850ff4d0b007e13daa core: refactor out byte stream into its own file easy dubs diff -r f10507d8f686 -r 99c961c91809 CMakeLists.txt --- a/CMakeLists.txt Mon Jul 15 01:33:51 2024 -0400 +++ b/CMakeLists.txt Tue Jul 16 21:15:59 2024 -0400 @@ -85,6 +85,7 @@ src/core/anime.cc src/core/anime_db.cc src/core/anime_season.cc + src/core/byte_stream.cc src/core/config.cc src/core/date.cc src/core/filesystem.cc @@ -204,6 +205,15 @@ ########################################################################### # Platform specific stuff +# Endianness +include (TestBigEndian) +TEST_BIG_ENDIAN(IS_BIG_ENDIAN) +if(IS_BIG_ENDIAN) + list(APPEND DEFINES BYTE_ORDER_BIG) +else() + list(APPEND DEFINES BYTE_ORDER_LITTLE) +endif() + # This is also used in the Win32 rc file set(RC_INFO_STRING "A lightweight anime tracker built with Qt.") diff -r f10507d8f686 -r 99c961c91809 dep/pugixml/pugixml-config-version.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/pugixml/pugixml-config-version.cmake Tue Jul 16 21:15:59 2024 -0400 @@ -0,0 +1,65 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version, +# but only if the requested major version is the same as the current one. +# The variable CVF_VERSION must be set before calling configure_file(). + + +set(PACKAGE_VERSION "1.14") + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + + if("1.14" MATCHES "^([0-9]+)\\.") + set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") + if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}") + endif() + else() + set(CVF_VERSION_MAJOR "1.14") + endif() + + if(PACKAGE_FIND_VERSION_RANGE) + # both endpoints of the range must have the expected major version + math (EXPR CVF_VERSION_MAJOR_NEXT "${CVF_VERSION_MAJOR} + 1") + if (NOT PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL CVF_VERSION_MAJOR_NEXT))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + else() + if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() + endif() +endif() + + +# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") + return() +endif() + +# check that the installed version has the same 32/64bit-ness as the one which is currently searching: +if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") + math(EXPR installedBits "8 * 8") + set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff -r f10507d8f686 -r 99c961c91809 dep/pugixml/pugixml-config.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/pugixml/pugixml-config.cmake Tue Jul 16 21:15:59 2024 -0400 @@ -0,0 +1,19 @@ + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was pugixml-config.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) + +#################################################################################### + +include("${CMAKE_CURRENT_LIST_DIR}/pugixml-targets.cmake") + +# If the user is not requiring 1.11 (either by explicitly requesting an older +# version or not requesting one at all), provide the old imported target name +# for compatibility. +if (NOT TARGET pugixml AND (NOT DEFINED PACKAGE_FIND_VERSION OR PACKAGE_FIND_VERSION VERSION_LESS "1.11")) + add_library(pugixml INTERFACE IMPORTED) + # Equivalent to target_link_libraries INTERFACE, but compatible with CMake 3.10 + set_target_properties(pugixml PROPERTIES INTERFACE_LINK_LIBRARIES pugixml::pugixml) +endif () diff -r f10507d8f686 -r 99c961c91809 dep/pugixml/pugixml.pc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/pugixml/pugixml.pc Tue Jul 16 21:15:59 2024 -0400 @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +includedir=/usr/include +libdir=/usr/lib + +Name: pugixml +Description: Light-weight, simple and fast XML parser for C++ with XPath support. +URL: https://pugixml.org/ +Version: 1.14 +Cflags: -I${includedir} +Libs: -L${libdir} -lpugixml diff -r f10507d8f686 -r 99c961c91809 include/core/bit_cast.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/core/bit_cast.h Tue Jul 16 21:15:59 2024 -0400 @@ -0,0 +1,26 @@ +#ifndef MINORI_CORE_BIT_CAST_H_ +#define MINORI_CORE_BIT_CAST_H_ + +/* XXX need to move more "core" stuff into the minori namespace */ + +#include +#include +#include + +namespace minori { + +/* C++17 doesn't have this unfortunately */ +template +To BitCast(From from) { + static_assert(sizeof(From) == sizeof(To), "Types must match sizes"); + static_assert(std::is_pod::value, "Requires POD input"); + static_assert(std::is_pod::value, "Requires POD output"); + + To to; + std::memcpy(std::addressof(to), std::addressof(from), sizeof(from)); + return to; +} + +} + +#endif /* MINORI_CORE_BIT_CAST_H_ */ diff -r f10507d8f686 -r 99c961c91809 include/core/byte_stream.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/core/byte_stream.h Tue Jul 16 21:15:59 2024 -0400 @@ -0,0 +1,75 @@ +#ifndef MINORI_CORE_BYTE_STREAM_H_ +#define MINORI_CORE_BYTE_STREAM_H_ + +#include "core/endian.h" + +#include +#include +#include +#include + +struct ByteStream { +public: + enum class ByteOrder { + Little, + Big, + }; + + ByteStream(std::uint8_t *bytes, std::size_t size); + + void ResetOffset(); + void SetEndianness(ByteOrder endian); + + template + bool ReadBinary(T& ret) { + if (offset_ + sizeof(T) >= size_) + return false; + + ret = *reinterpret_cast(bytes_ + offset_); + Advance(sizeof(T)); + return true; + } + + template + bool ReadInt(T& ret) { + static_assert(std::is_integral::value); + + if (!ReadBinary(ret)) + return false; + + switch (endian_) { + case ByteOrder::Little: + if constexpr (std::is_unsigned::value) { + ret = Endian::byteswap_little_to_host(ret); + } else { + ret = Endian::signed_byteswap_little_to_host(ret); + } + break; + case ByteOrder::Big: + if constexpr (std::is_unsigned::value) { + ret = Endian::byteswap_big_to_host(ret); + } else { + ret = Endian::signed_byteswap_big_to_host(ret); + } + break; + default: + /* can't know for sure. punt */ + return false; + } + + return true; + } + + bool ReadString(std::string& str, std::size_t size); + bool Advance(std::size_t amount); + +private: + /* raw data */ + std::uint8_t *bytes_ = nullptr; + std::size_t offset_ = 0; + std::size_t size_ = 0; + + ByteOrder endian_ = ByteOrder::Little; +}; + +#endif /* MINORI_CORE_BYTE_STREAM_H_ */ diff -r f10507d8f686 -r 99c961c91809 include/core/endian.h --- a/include/core/endian.h Mon Jul 15 01:33:51 2024 -0400 +++ b/include/core/endian.h Tue Jul 16 21:15:59 2024 -0400 @@ -1,16 +1,15 @@ #ifndef MINORI_CORE_ENDIAN_H_ #define MINORI_CORE_ENDIAN_H_ -/* definition of endian-related stuff. primarily used for*/ +/* definition of endian-related stuff. primarily used for x11 + * binary structures */ +#include "core/bit_cast.h" #include #include class Endian { private: - static constexpr uint32_t uint32_ = 0x01020304; - static constexpr uint8_t magic_ = static_cast(uint32_); - /* check for compiler builtins for byteswapping */ #ifdef __has_builtin # if __has_builtin(__builtin_bswap16) @@ -75,14 +74,20 @@ # undef COMPILER_BUILTIN_BSWAP64 #endif public: - static constexpr bool little = magic_ == 0x04; - static constexpr bool big = magic_ == 0x01; - static_assert(little || big, "unsupported endianness"); +#if defined(BYTE_ORDER_BIG) + static constexpr bool big = true; + static constexpr bool little = false; +#elif defined(BYTE_ORDER_LITTLE) + static constexpr bool big = false; + static constexpr bool little = true; +#else +#error "unsupported endianness" +#endif template static constexpr T byteswap(T x) { static_assert(std::is_integral::value); - static_assert(std::is_unsigned::value); + static_assert(std::is_unsigned::value, "use signed_byteswap"); if constexpr (std::is_same::value) { return x; @@ -97,6 +102,16 @@ } } + /* this can't be constexpr */ + template + static T signed_byteswap(T x) { + static_assert(std::is_integral::value); + static_assert(std::is_signed::value, "use regular byteswap"); + + using uT = typename std::make_unsigned::type; + return minori::BitCast(byteswap(minori::BitCast(x))); + } + template static constexpr T byteswap_little_to_host(T x) { if constexpr (little) { @@ -114,6 +129,24 @@ return byteswap(x); } } + + template + static T signed_byteswap_little_to_host(T x) { + if constexpr (little) { + return x; + } else if constexpr (big) { + return signed_byteswap(x); + } + } + + template + static T signed_byteswap_big_to_host(T x) { + if constexpr (big) { + return x; + } else if constexpr (little) { + return signed_byteswap(x); + } + } private: Endian() = delete; }; diff -r f10507d8f686 -r 99c961c91809 include/sys/x11/settings.h --- a/include/sys/x11/settings.h Mon Jul 15 01:33:51 2024 -0400 +++ b/include/sys/x11/settings.h Tue Jul 16 21:15:59 2024 -0400 @@ -17,7 +17,7 @@ /* could technically be a union */ struct Data { - std::uint32_t integer; + std::int32_t integer; std::string string; struct { std::uint16_t red, green, blue, alpha; diff -r f10507d8f686 -r 99c961c91809 src/core/byte_stream.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/core/byte_stream.cc Tue Jul 16 21:15:59 2024 -0400 @@ -0,0 +1,31 @@ +#include "core/byte_stream.h" + +ByteStream::ByteStream(std::uint8_t *bytes, std::size_t size) { + bytes_ = bytes; + size_ = size; +} + +void ByteStream::ResetOffset() { + offset_ = 0; +} + +void ByteStream::SetEndianness(ByteStream::ByteOrder endian) { + endian_ = endian; +} + +bool ByteStream::ReadString(std::string& str, std::size_t size) { + if (offset_ + size >= size_) + return false; + + str.assign(reinterpret_cast(bytes_ + offset_), size); + Advance(size); + return true; +} + +bool ByteStream::Advance(std::size_t amount) { + if (offset_ + amount >= size_) + return false; + + offset_ += amount; + return true; +} diff -r f10507d8f686 -r 99c961c91809 src/sys/glib/dark_theme.cc --- a/src/sys/glib/dark_theme.cc Mon Jul 15 01:33:51 2024 -0400 +++ b/src/sys/glib/dark_theme.cc Tue Jul 16 21:15:59 2024 -0400 @@ -5,7 +5,6 @@ #include #include #include -#include namespace glib { diff -r f10507d8f686 -r 99c961c91809 src/sys/x11/dark_theme.cc --- a/src/sys/x11/dark_theme.cc Mon Jul 15 01:33:51 2024 -0400 +++ b/src/sys/x11/dark_theme.cc Tue Jul 16 21:15:59 2024 -0400 @@ -2,8 +2,6 @@ #include "sys/x11/settings.h" #include "sys/glib/dark_theme.h" /* glib::IsGTKThemeDark */ -#include - namespace x11 { bool IsInDarkTheme() { diff -r f10507d8f686 -r 99c961c91809 src/sys/x11/settings.cc --- a/src/sys/x11/settings.cc Mon Jul 15 01:33:51 2024 -0400 +++ b/src/sys/x11/settings.cc Tue Jul 16 21:15:59 2024 -0400 @@ -1,6 +1,7 @@ #include "sys/x11/settings.h" -#include "core/endian.h" +#include "core/byte_stream.h" +#include #include #include #include @@ -8,7 +9,6 @@ #include #include #include -#include #include #include @@ -40,61 +40,20 @@ public: Parser(std::uint8_t *bytes, std::size_t size); - std::vector ParseAllItems(void); + bool ParseHeader(void); std::optional ParseNextItem(void); std::uint32_t GetTotalItems(void); private: - /* byte order values */ - enum { + enum ByteOrder { LSBFirst = 0, MSBFirst = 1, }; - template - bool ReadData(T& ret) { - if (offset_ + sizeof(T) >= size_) - return false; - - ret = *reinterpret_cast(bytes_ + offset_); - Advance(sizeof(T)); - return true; - } - - /* will fail on signed integers; xsettings has none of those though */ - template - bool ReadInt(T& ret) { - static_assert(std::is_integral::value); - - if (!ReadData(ret)) - return false; - - switch (byte_order_) { - case LSBFirst: - ret = Endian::byteswap_little_to_host(ret); - break; - case MSBFirst: - ret = Endian::byteswap_big_to_host(ret); - break; - default: - /* can't know for sure. punt */ - return false; - } - - return true; - } - - bool ReadString(std::string& str, std::size_t size); - bool Advance(std::size_t amount); - - /* raw data */ - std::uint8_t *bytes_ = nullptr; - std::size_t offset_ = 0; - std::size_t size_ = 0; + ByteStream stream; /* parsed in the constructor */ - std::uint8_t byte_order_ = 0; std::uint32_t serial_ = 0; std::uint32_t total_items_ = 0; }; @@ -103,95 +62,93 @@ return total_items_; } -bool Parser::ReadString(std::string& str, std::size_t size) { - if (offset_ + size >= size_) - return false; - - str.assign(reinterpret_cast(bytes_ + offset_), size); - Advance(size); - return true; +Parser::Parser(std::uint8_t *bytes, std::size_t size) : stream(bytes, size) { } -bool Parser::Advance(std::size_t amount) { - if (offset_ + amount >= size_) +bool Parser::ParseHeader(void) { + std::uint8_t byte_order; + if (!stream.ReadBinary(byte_order)) return false; - offset_ += amount; - return true; -} - -Parser::Parser(std::uint8_t *bytes, std::size_t size) { - bytes_ = bytes; - size_ = size; + switch (byte_order) { + case MSBFirst: + stream.SetEndianness(ByteStream::ByteOrder::Big); + break; + case LSBFirst: + stream.SetEndianness(ByteStream::ByteOrder::Little); + break; + default: + return false; /* errr */ + } - if (!ReadData(byte_order_)) - return; + stream.Advance(3); - Advance(3); + if (!stream.ReadInt(serial_)) + return false; - if (!ReadInt(serial_)) - return; + if (!stream.ReadInt(total_items_)) + return false; - if (!ReadInt(total_items_)) - return; + return true; } std::optional Parser::ParseNextItem(void) { SettingsItem item; /* read one byte */ - if (!ReadInt(item.type)) + if (!stream.ReadInt(item.type)) return std::nullopt; if (!item.VerifyType()) return std::nullopt; /* skip padding */ - if (!Advance(1)) + if (!stream.Advance(1)) return std::nullopt; /* parse the name */ std::uint16_t name_size; - if (!ReadInt(name_size)) + if (!stream.ReadInt(name_size)) return std::nullopt; - if (!ReadString(item.name, name_size)) + if (!stream.ReadString(item.name, name_size)) return std::nullopt; /* padding */ - if (!Advance(GetPadding(name_size, 4))) + if (!stream.Advance(GetPadding(name_size, 4))) return std::nullopt; - if (!ReadInt(item.serial)) + if (!stream.ReadInt(item.serial)) return std::nullopt; switch (item.type) { case SettingsItem::TypeInt: { - if (!ReadInt(item.data.integer)) + if (!stream.ReadInt(item.data.integer)) return std::nullopt; break; } case SettingsItem::TypeStr: { std::uint32_t size; - if (!ReadInt(size)) + if (!stream.ReadInt(size)) return std::nullopt; - if (!ReadString(item.data.string, size)) + if (!stream.ReadString(item.data.string, size)) return std::nullopt; - /* padding */ - if (!Advance(GetPadding(size, 4))) - return std::nullopt; + /* don't fail if advancing fails on this padding, + * because this causes parsing to fail for strings + * at the end of the data */ + stream.Advance(GetPadding(size, 4)); break; } case SettingsItem::TypeRgba: { - /* The order here is important!! */ - if (!ReadInt(item.data.rgba.red) - || !ReadInt(item.data.rgba.blue) - || !ReadInt(item.data.rgba.green) - || !ReadInt(item.data.rgba.alpha)) + /* it's actually RBGA, but whatever. */ + if (!stream.ReadInt(item.data.rgba.red) + || !stream.ReadInt(item.data.rgba.blue) + || !stream.ReadInt(item.data.rgba.green) + || !stream.ReadInt(item.data.rgba.alpha)) return std::nullopt; break; @@ -204,23 +161,6 @@ return item; } -std::vector Parser::ParseAllItems(void) { - offset_ = 0; - - std::uint32_t i; - std::vector items; - - for (i = 0; i < total_items_; i++) { - std::optional item = ParseNextItem(); - if (!item) - break; - - items.push_back(item.value()); - } - - return items; -} - /* ------------------------------------------------------------------------- */ /* real X11 code */ @@ -323,7 +263,18 @@ return false; Parser parser(xsettings_raw.data(), xsettings_raw.size()); - settings = parser.ParseAllItems(); + if (!parser.ParseHeader()) + return false; + + std::uint32_t total = parser.GetTotalItems(); + + while (total--) { + std::optional opt_item = parser.ParseNextItem(); + if (!opt_item) + break; + + settings.push_back(opt_item.value()); + } return true; } @@ -334,10 +285,12 @@ return false; Parser parser(xsettings_raw.data(), xsettings_raw.size()); + if (!parser.ParseHeader()) + return false; std::uint32_t total = parser.GetTotalItems(); - for (; total; total--) { + while (total--) { std::optional opt_item = parser.ParseNextItem(); if (!opt_item) return false;