Coverage Report

Created: 2025-09-08 06:08

/src/swift-protobuf/Sources/SwiftProtobuf/HashVisitor.swift
Line
Count
Source (jump to first uncovered line)
1
// Sources/SwiftProtobuf/HashVisitor.swift - Hashing support
2
//
3
// Copyright (c) 2014 - 2016 Apple Inc. and the project authors
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
//
6
// See LICENSE.txt for license information:
7
// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
8
//
9
// -----------------------------------------------------------------------------
10
///
11
/// Hashing is basically a serialization problem, so we can leverage the
12
/// generated traversal methods for that.
13
///
14
// -----------------------------------------------------------------------------
15
16
import Foundation
17
18
private let i_2166136261 = Int(bitPattern: 2_166_136_261)
19
private let i_16777619 = Int(16_777_619)
20
21
/// Computes the hash of a message by visiting its fields recursively.
22
///
23
/// Note that because this visits every field, it has the potential to be slow
24
/// for large or deeply nested messages. Users who need to use such messages as
25
/// dictionary keys or set members can use a wrapper struct around the message
26
/// and use a custom Hashable implementation that looks at the subset of the
27
/// message fields they want to include.
28
internal struct HashVisitor: Visitor {
29
30
    internal private(set) var hasher: Hasher
31
32
0
    init(_ hasher: Hasher) {
33
0
        self.hasher = hasher
34
0
    }
35
36
0
    mutating func visitUnknown(bytes: Data) throws {
37
0
        hasher.combine(bytes)
38
0
    }
39
40
0
    mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
41
0
        hasher.combine(fieldNumber)
42
0
        hasher.combine(value)
43
0
    }
44
45
0
    mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
46
0
        hasher.combine(fieldNumber)
47
0
        hasher.combine(value)
48
0
    }
49
50
0
    mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
51
0
        hasher.combine(fieldNumber)
52
0
        hasher.combine(value)
53
0
    }
54
55
0
    mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
56
0
        hasher.combine(fieldNumber)
57
0
        hasher.combine(value)
58
0
    }
59
60
0
    mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
61
0
        hasher.combine(fieldNumber)
62
0
        hasher.combine(value)
63
0
    }
64
65
0
    mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
66
0
        hasher.combine(fieldNumber)
67
0
        hasher.combine(value)
68
0
    }
69
70
    mutating func visitSingularEnumField<E: Enum>(
71
        value: E,
72
        fieldNumber: Int
73
0
    ) {
74
0
        hasher.combine(fieldNumber)
75
0
        hasher.combine(value)
76
0
    }
77
78
0
    mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) {
79
0
        hasher.combine(fieldNumber)
80
0
        value.hash(into: &hasher)
81
0
    }
82
83
0
    mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
84
0
        assert(!value.isEmpty)
85
0
        hasher.combine(fieldNumber)
86
0
        hasher.combine(value)
87
0
    }
88
89
0
    mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
90
0
        assert(!value.isEmpty)
91
0
        hasher.combine(fieldNumber)
92
0
        hasher.combine(value)
93
0
    }
94
95
0
    mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
96
0
        assert(!value.isEmpty)
97
0
        hasher.combine(fieldNumber)
98
0
        hasher.combine(value)
99
0
    }
100
101
0
    mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
102
0
        assert(!value.isEmpty)
103
0
        hasher.combine(fieldNumber)
104
0
        hasher.combine(value)
105
0
    }
106
107
0
    mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
108
0
        assert(!value.isEmpty)
109
0
        hasher.combine(fieldNumber)
110
0
        hasher.combine(value)
111
0
    }
112
113
0
    mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
114
0
        assert(!value.isEmpty)
115
0
        hasher.combine(fieldNumber)
116
0
        hasher.combine(value)
117
0
    }
118
119
0
    mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
120
0
        assert(!value.isEmpty)
121
0
        hasher.combine(fieldNumber)
122
0
        hasher.combine(value)
123
0
    }
124
125
0
    mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
126
0
        assert(!value.isEmpty)
127
0
        hasher.combine(fieldNumber)
128
0
        hasher.combine(value)
129
0
    }
130
131
0
    mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
132
0
        assert(!value.isEmpty)
133
0
        hasher.combine(fieldNumber)
134
0
        hasher.combine(value)
135
0
    }
136
137
0
    mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
138
0
        assert(!value.isEmpty)
139
0
        hasher.combine(fieldNumber)
140
0
        hasher.combine(value)
141
0
    }
142
143
0
    mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
144
0
        assert(!value.isEmpty)
145
0
        hasher.combine(fieldNumber)
146
0
        hasher.combine(value)
147
0
    }
148
149
0
    mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
150
0
        assert(!value.isEmpty)
151
0
        hasher.combine(fieldNumber)
152
0
        hasher.combine(value)
153
0
    }
154
155
0
    mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
156
0
        assert(!value.isEmpty)
157
0
        hasher.combine(fieldNumber)
158
0
        hasher.combine(value)
159
0
    }
160
161
0
    mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
162
0
        assert(!value.isEmpty)
163
0
        hasher.combine(fieldNumber)
164
0
        hasher.combine(value)
165
0
    }
166
167
0
    mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
168
0
        assert(!value.isEmpty)
169
0
        hasher.combine(fieldNumber)
170
0
        hasher.combine(value)
171
0
    }
172
173
0
    mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
174
0
        assert(!value.isEmpty)
175
0
        hasher.combine(fieldNumber)
176
0
        hasher.combine(value)
177
0
    }
178
179
0
    mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
180
0
        assert(!value.isEmpty)
181
0
        hasher.combine(fieldNumber)
182
0
        for v in value {
183
0
            v.hash(into: &hasher)
184
0
        }
185
0
    }
186
187
0
    mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
188
0
        assert(!value.isEmpty)
189
0
        hasher.combine(fieldNumber)
190
0
        for v in value {
191
0
            v.hash(into: &hasher)
192
0
        }
193
0
    }
194
195
    mutating func visitMapField<KeyType, ValueType: MapValueType>(
196
        fieldType: _ProtobufMap<KeyType, ValueType>.Type,
197
        value: _ProtobufMap<KeyType, ValueType>.BaseType,
198
        fieldNumber: Int
199
0
    ) throws {
200
0
        hasher.combine(fieldNumber)
201
0
        hasher.combine(value)
202
0
    }
203
204
    mutating func visitMapField<KeyType, ValueType>(
205
        fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
206
        value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
207
        fieldNumber: Int
208
0
    ) throws where ValueType.RawValue == Int {
209
0
        hasher.combine(fieldNumber)
210
0
        hasher.combine(value)
211
0
    }
212
213
    mutating func visitMapField<KeyType, ValueType>(
214
        fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
215
        value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
216
        fieldNumber: Int
217
0
    ) throws {
218
0
        hasher.combine(fieldNumber)
219
0
        hasher.combine(value)
220
0
    }
221
}