Просмотр исходного кода

Share the hex log helper and document the static values

- core::log::append_hex replaces six identical hex append loops in the
  Web Service action and runtime reporters
- give the twenty undocumented constexpr values a nearby reason
- lift the socket-plug rule comparator out of a lambda, matching the other
  comparators in that file, which also clears a clang-tidy false positive
  raised inside the STL's own invalid-comparator check
stan 3 недель назад
Родитель
Сommit
0633790b70

+ 1 - 0
Sunrise/src/client/content/items/packages/package_build_report.cpp

@@ -47,6 +47,7 @@ void report_ability_failure(const char* stage,
                             std::size_t character,
                             std::size_t first,
                             std::size_t second) noexcept {
+    // Extraction retries on every boot pass, so the count is capped to keep one failure readable.
     constexpr std::size_t kReportLimit = 12;
     if (g_abilityFailureReports.fetch_add(1, std::memory_order_relaxed) >= kReportLimit) {
         return;

+ 2 - 0
Sunrise/src/client/content/items/packages/package_socket_plug_build.h

@@ -53,7 +53,9 @@ private:
         std::uint32_t poolIndex{UINT32_MAX};
     };
 
+    /** A plug set names three categories: reusable, randomized, and the socket's own default. */
     static constexpr std::size_t kCategoryCount = 3;
+    /** Power-of-two open-addressed table, sized so the largest package's pools stay sparse. */
     static constexpr std::size_t kLookupCapacity = 1U << 19U;
 
     std::vector<socket_plugs::Rule> rules_{};

+ 19 - 0
Sunrise/src/core/logging/log.cpp

@@ -31,6 +31,10 @@ constexpr std::string_view kLineEnding = "\r\n";
 constexpr std::size_t kLineTerminatorBytes = 1;
 /** An elapsed line is one event, a duration and an outcome, so it needs less than a full line. */
 constexpr std::size_t kElapsedCapacity = 128;
+/** Uppercase digits for the hex traces, which are read beside hex dumps of the same bytes. */
+constexpr std::string_view kHexDigits = "0123456789ABCDEF";
+/** One byte prints as two hex digits, which is the room each appended byte needs. */
+constexpr std::size_t kHexDigitsPerByte = 2;
 /** Event text stops before the CRLF and the trailing null. */
 constexpr std::size_t kEventTextCapacity =
     kLineCapacity - kLineEnding.size() - kLineTerminatorBytes;
@@ -274,6 +278,21 @@ void write_elapsed(Channel channel,
     write(channel, Level::debug, {line.data(), length});
 }
 
+/** Appends bytes as uppercase hex to a line that already holds its key prefix. */
+bool append_hex(std::span<char> line,
+                std::size_t& length,
+                std::span<const std::byte> bytes) noexcept {
+    for (const std::byte byte : bytes) {
+        if (length + kHexDigitsPerByte >= line.size()) {
+            return false;
+        }
+        const auto value = std::to_integer<unsigned>(byte);
+        line[length++] = kHexDigits[(value >> 4U) & 0xFU];
+        line[length++] = kHexDigits[value & 0xFU];
+    }
+    return true;
+}
+
 /** @return True while a sink write is in progress. */
 bool writers_active() noexcept {
     return g_writers.load(std::memory_order_acquire) != 0;

+ 13 - 0
Sunrise/src/core/logging/log.h

@@ -2,6 +2,7 @@
 
 #include <array>
 #include <cstddef>
+#include <span>
 #include <string_view>
 
 namespace sunrise::core::log {
@@ -63,6 +64,18 @@ void write_elapsed(Channel channel,
                    unsigned long long startedTick,
                    std::string_view result) noexcept;
 
+/**
+ * Appends bytes as uppercase hex to a line that already holds its key prefix.
+ * Stops before the first pair that would not fit, so a long payload truncates.
+ * @param line Line storage holding the prefix.
+ * @param length Bytes already written, raised by two for each encoded byte.
+ * @param bytes Borrowed payload to encode.
+ * @return True when every byte fit.
+ */
+bool append_hex(std::span<char> line,
+                std::size_t& length,
+                std::span<const std::byte> bytes) noexcept;
+
 /**
  * Reports whether any thread is inside a sink write.
  * A sink holds an operating system lock this process shares, so a thread suspended there

+ 2 - 0
Sunrise/src/server/bap/encrypted/push/snapshot/family4_inventory_updates.cpp

@@ -543,6 +543,8 @@ bool prepare_item_dismantle(Scratch& scratch,
             return report_failure("dismantle_account_change_state");
         }
 
+        // Kind 1 is the ordinary acquisition path, and clear policy bits leave it enabled. The
+        // native observer skips a record with any other pair.
         constexpr std::uint8_t kRewardChangeKind = 1;
         constexpr std::uint16_t kRewardChangeFlags = 0;
         for (std::size_t rewardIndex = 0; rewardIndex < mutation.rewardCount; ++rewardIndex) {

+ 1 - 0
Sunrise/src/server/bap/encrypted/push/snapshot/family4_selection_move.cpp

@@ -30,6 +30,7 @@ void report_equipment_object(const queuez::EquipmentSwap& swap,
                              const state::PendingEquipmentSwap& mutation,
                              const Resolved& selected,
                              const family4_datagen::character::layout::Object& object) noexcept {
+    // Row sentinel for the log line only. Zero is a real row, so it cannot stand for "not found".
     constexpr std::size_t kMissing = (std::numeric_limits<std::size_t>::max)();
     std::size_t requestedRow = kMissing;
     std::size_t previousRow = kMissing;

+ 9 - 45
Sunrise/src/server/web_service/web_service_actions.cpp

@@ -21,8 +21,11 @@ namespace sunrise::server::web_service {
 
 namespace {
 
+/** Socket kind the shader model occupies, which is the only kind a shader swap may target. */
 constexpr std::uint8_t kEquippedShaderModelSocketKind = 0;
+/** Index stored when no definition resolves. The catalog is u16-indexed, so this cannot be one. */
 constexpr std::uint32_t kUnavailableDefinitionIndex = (std::numeric_limits<std::uint16_t>::max)();
+
 } // namespace
 
 /** Logs one exact correlated equipment response after its Queuez update is staged. */
@@ -41,16 +44,8 @@ void report_equip_response(const middleware::web_service::Message& message,
     if (prefix <= 0 || static_cast<std::size_t>(prefix) >= line.size()) {
         return;
     }
-    constexpr char kHex[] = "0123456789ABCDEF";
     std::size_t length = static_cast<std::size_t>(prefix);
-    for (const std::byte byte : response) {
-        if (length + 2 >= line.size()) {
-            break;
-        }
-        const unsigned value = std::to_integer<unsigned>(byte);
-        line[length++] = kHex[(value >> 4U) & 0xFU];
-        line[length++] = kHex[value & 0xFU];
-    }
+    (void)core::log::append_hex(line, length, response);
     core::log::write(core::log::Channel::server, core::log::Level::debug, {line.data(), length});
 }
 
@@ -73,16 +68,8 @@ void report_item_acquisition_response(const middleware::web_service::Message& me
     if (prefix <= 0 || static_cast<std::size_t>(prefix) >= line.size()) {
         return;
     }
-    constexpr char kHex[] = "0123456789ABCDEF";
     std::size_t length = static_cast<std::size_t>(prefix);
-    for (const std::byte byte : response) {
-        if (length + 2 >= line.size()) {
-            break;
-        }
-        const unsigned value = std::to_integer<unsigned>(byte);
-        line[length++] = kHex[(value >> 4U) & 0xFU];
-        line[length++] = kHex[value & 0xFU];
-    }
+    (void)core::log::append_hex(line, length, response);
     core::log::write(core::log::Channel::server, core::log::Level::debug, {line.data(), length});
 }
 
@@ -107,16 +94,8 @@ void report_profile_item_acquisition_response(const middleware::web_service::Mes
     if (prefix <= 0 || static_cast<std::size_t>(prefix) >= line.size()) {
         return;
     }
-    constexpr char kHex[] = "0123456789ABCDEF";
     std::size_t length = static_cast<std::size_t>(prefix);
-    for (const std::byte byte : response) {
-        if (length + 2 >= line.size()) {
-            break;
-        }
-        const unsigned value = std::to_integer<unsigned>(byte);
-        line[length++] = kHex[(value >> 4U) & 0xFU];
-        line[length++] = kHex[value & 0xFU];
-    }
+    (void)core::log::append_hex(line, length, response);
     core::log::write(core::log::Channel::server, core::log::Level::debug, {line.data(), length});
 }
 
@@ -139,16 +118,8 @@ void report_item_dismantle_response(const middleware::web_service::Message& mess
     if (prefix <= 0 || static_cast<std::size_t>(prefix) >= line.size()) {
         return;
     }
-    constexpr char kHex[] = "0123456789ABCDEF";
     std::size_t length = static_cast<std::size_t>(prefix);
-    for (const std::byte byte : response) {
-        if (length + 2 >= line.size()) {
-            break;
-        }
-        const unsigned value = std::to_integer<unsigned>(byte);
-        line[length++] = kHex[(value >> 4U) & 0xFU];
-        line[length++] = kHex[value & 0xFU];
-    }
+    (void)core::log::append_hex(line, length, response);
     core::log::write(core::log::Channel::server, core::log::Level::debug, {line.data(), length});
 }
 
@@ -175,16 +146,8 @@ void report_socket_plug_response(const middleware::web_service::Message& message
     if (prefix <= 0 || static_cast<std::size_t>(prefix) >= line.size()) {
         return;
     }
-    constexpr char kHex[] = "0123456789ABCDEF";
     std::size_t length = static_cast<std::size_t>(prefix);
-    for (const std::byte byte : response) {
-        if (length + 2 >= line.size()) {
-            break;
-        }
-        const unsigned value = std::to_integer<unsigned>(byte);
-        line[length++] = kHex[(value >> 4U) & 0xFU];
-        line[length++] = kHex[value & 0xFU];
-    }
+    (void)core::log::append_hex(line, length, response);
     core::log::write(core::log::Channel::server, core::log::Level::debug, {line.data(), length});
 }
 
@@ -568,6 +531,7 @@ void dismantle_item(const middleware::web_service::Message& message, Outcome& ou
     }
     const std::uint64_t instanceSoid = request.instanceSoid;
     const std::uint16_t definitionIndex = request.definitionIndex;
+    // The codec owns the value; this alias keeps the dismantle checks below readable.
     constexpr std::uint32_t kSingleQuantity =
         middleware::web_service::messages::opcode402::kSingleQuantity;
 

+ 9 - 15
Sunrise/src/server/web_service/web_service_runtime.cpp

@@ -1,5 +1,6 @@
 #include "web_service_runtime.h"
 
+#include <algorithm>
 #include <array>
 #include <chrono>
 #include <cstdio>
@@ -32,6 +33,8 @@ namespace sunrise::server::web_service {
 constexpr std::size_t kOpcodeLineCapacity = 64;
 /** A request trace keeps enough payload to identify an item-action descriptor. */
 constexpr std::size_t kRequestPayloadTraceBytes = 192;
+/** Marks a trace that stopped at the cap, so a short hex string is not read as a short payload. */
+constexpr std::string_view kTruncated = " truncated=1";
 /** Web Service opcode used by the Character screen's Equip action. */
 constexpr std::uint16_t kEquipOpcode = 403;
 /** Web Service opcode used by the Character screen's Unequip action. */
@@ -69,22 +72,13 @@ void report_request(const middleware::web_service::Message& message) noexcept {
         return;
     }
 
-    constexpr char kHex[] = "0123456789ABCDEF";
     std::size_t length = static_cast<std::size_t>(prefix);
-    const std::size_t traced = message.payload.size() < kRequestPayloadTraceBytes
-                                   ? message.payload.size()
-                                   : kRequestPayloadTraceBytes;
-    for (std::size_t index = 0; index < traced && length + 2 < line.size(); ++index) {
-        const unsigned value = std::to_integer<unsigned>(message.payload[index]);
-        line[length++] = kHex[(value >> 4U) & 0xFU];
-        line[length++] = kHex[value & 0xFU];
-    }
-    if (traced != message.payload.size()) {
-        constexpr std::string_view kTruncated = " truncated=1";
-        if (length + kTruncated.size() < line.size()) {
-            std::memcpy(line.data() + length, kTruncated.data(), kTruncated.size());
-            length += kTruncated.size();
-        }
+    const std::size_t traced =
+        (std::min)(message.payload.size(), static_cast<std::size_t>(kRequestPayloadTraceBytes));
+    (void)core::log::append_hex(line, length, message.payload.first(traced));
+    if (traced != message.payload.size() && length + kTruncated.size() < line.size()) {
+        std::memcpy(line.data() + length, kTruncated.data(), kTruncated.size());
+        length += kTruncated.size();
     }
     if (length != 0) {
         core::log::write(core::log::Channel::server, core::log::Level::info, {line.data(), length});

+ 8 - 7
Sunrise/src/state/build_data/cache/records/cache_domain_validation.cpp

@@ -67,6 +67,13 @@ namespace {
     return left.bucketId < right.bucketId;
 }
 
+/** @return Item then lane order, which is the order the rules are published in. */
+[[nodiscard]] bool socket_plug_rule_less(const items::socket_plugs::Rule& left,
+                                         const items::socket_plugs::Rule& right) noexcept {
+    return left.itemDefinitionIndex < right.itemDefinitionIndex
+           || (left.itemDefinitionIndex == right.itemDefinitionIndex && left.lane < right.lane);
+}
+
 /** @return Native definition-index order for item rows. */
 [[nodiscard]] bool item_less(const items::Definition& left,
                              const items::Definition& right) noexcept {
@@ -173,13 +180,7 @@ bool canonicalize(MutableDomains domains, const DomainCounts& counts) noexcept {
     std::sort(itemDetails.begin(), itemDetails.end(), detail_less);
     // Rules are published in exact item/lane order. Pools and members are an indexed relation,
     // so reordering either would invalidate every pool reference and range.
-    if (!std::is_sorted(socketPlugRules.begin(),
-                        socketPlugRules.end(),
-                        [](const auto& left, const auto& right) {
-                            return left.itemDefinitionIndex < right.itemDefinitionIndex
-                                   || (left.itemDefinitionIndex == right.itemDefinitionIndex
-                                       && left.lane < right.lane);
-                        })) {
+    if (!std::is_sorted(socketPlugRules.begin(), socketPlugRules.end(), socket_plug_rule_less)) {
         return false;
     }
     std::sort(inventoryBuckets.begin(), inventoryBuckets.end(), bucket_less);

+ 5 - 0
Sunrise/src/state/runtime/state_account_runtime.cpp

@@ -205,6 +205,7 @@ same_profile_inventory(const AccountState& account,
     if (account.profileItemCount > account.profileItems.size()) {
         return false;
     }
+    // The bucket identity is one byte on the wire, so 256 covers every value one can carry.
     constexpr std::size_t kBucketIdentityCapacity = 256;
     std::array<std::uint16_t, kBucketIdentityCapacity> taken{};
     std::array<bool, inventory_buckets::kProfileSlotCapacity> occupied{};
@@ -888,6 +889,7 @@ finalize_equipment_transition(const AccountState& account,
         }
     }
 
+    // The serial is signed on the wire, so it must stay inside the positive int32 range.
     constexpr std::uint32_t kMaximumInventorySerial =
         static_cast<std::uint32_t>((std::numeric_limits<std::int32_t>::max)());
     if (movedItemCount == 0 || after.nextInventorySerial > kMaximumInventorySerial
@@ -1349,6 +1351,8 @@ character_item_at(CharacterState& character, const CharacterItemLocation& locati
                                     std::uint32_t flags,
                                     PendingItemState& mutation) noexcept {
     mutation = {};
+    // Bits 0 and 1 are the two states the client sends. Any other bit is a request we cannot
+    // honour.
     constexpr std::uint32_t kSupportedItemStateMask = 0x3U;
     if (!account::valid(snapshot) || characterIndex >= snapshot.characterCount
         || targetInstanceSoid == 0 || (flags & ~kSupportedItemStateMask) != 0) {
@@ -1763,6 +1767,7 @@ apply_dismantle_rewards(const AccountState& before,
         movedItemCount += static_cast<std::size_t>(beforeRow != afterRow);
     }
 
+    // The serial is signed on the wire, so it must stay inside the positive int32 range.
     constexpr std::uint32_t kMaximumInventorySerial =
         static_cast<std::uint32_t>((std::numeric_limits<std::int32_t>::max)());
     if (after.nextInventorySerial > kMaximumInventorySerial