@@ -34,7 +34,7 @@ public struct DecodedField: Equatable, Hashable, Sendable, Identifiable {
3434}
3535
3636/// A typed decoded value. Mirrors the value types carried across the WASM ABI (CBOR).
37- public enum DecodedValue : Equatable , Hashable , Sendable {
37+ public enum DecodedValue : Sendable {
3838 case string( String )
3939 case int( Int64 )
4040 case uint( UInt64 )
@@ -45,6 +45,53 @@ public enum DecodedValue: Equatable, Hashable, Sendable {
4545 case uuid( UUID )
4646}
4747
48+ extension DecodedValue : Equatable , Hashable {
49+
50+ /// Integers compare numerically across `int` and `uint`.
51+ ///
52+ /// CBOR has no notion of signedness: major type 0 encodes *any* non-negative integer, so a
53+ /// plugin field that is semantically signed arrives as `uint` whenever its value happens to be
54+ /// non-negative. The cases therefore mean "negative" and "non-negative", not "signed" and
55+ /// "unsigned", and two producers of the same number — a native parser and a WASM plugin — must
56+ /// compare equal. The UI renders both identically.
57+ public static func == ( lhs: DecodedValue , rhs: DecodedValue ) -> Bool {
58+ switch ( lhs, rhs) {
59+ case let ( . string( a) , . string( b) ) : return a == b
60+ case let ( . int( a) , . int( b) ) : return a == b
61+ case let ( . uint( a) , . uint( b) ) : return a == b
62+ case let ( . int( a) , . uint( b) ) : return a >= 0 && UInt64 ( a) == b
63+ case let ( . uint( a) , . int( b) ) : return b >= 0 && a == UInt64 ( b)
64+ case let ( . double( a) , . double( b) ) : return a == b
65+ case let ( . bool( a) , . bool( b) ) : return a == b
66+ case let ( . bytes( a) , . bytes( b) ) : return a == b
67+ case let ( . uuid( a) , . uuid( b) ) : return a == b
68+ default : return false
69+ }
70+ }
71+
72+ public func hash( into hasher: inout Hasher ) {
73+ switch self {
74+ case let . string( value) :
75+ hasher. combine ( 0 ) ; hasher. combine ( value)
76+ // Both integer cases share a discriminator, and non-negative values hash through the same
77+ // UInt64 path, so numerically equal values hash equally as Hashable requires.
78+ case let . int( value) :
79+ hasher. combine ( 1 )
80+ if value >= 0 { hasher. combine ( UInt64 ( value) ) } else { hasher. combine ( value) }
81+ case let . uint( value) :
82+ hasher. combine ( 1 ) ; hasher. combine ( value)
83+ case let . double( value) :
84+ hasher. combine ( 2 ) ; hasher. combine ( value)
85+ case let . bool( value) :
86+ hasher. combine ( 3 ) ; hasher. combine ( value)
87+ case let . bytes( value) :
88+ hasher. combine ( 4 ) ; hasher. combine ( value)
89+ case let . uuid( value) :
90+ hasher. combine ( 5 ) ; hasher. combine ( value)
91+ }
92+ }
93+ }
94+
4895/// The result of a plugin decoding one advertisement field or attribute value.
4996public struct DecodedResult : Equatable , Hashable , Sendable {
5097
0 commit comments