Coverage Report

Created: 2021-09-03 06:06

/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: 2166136261)
19
private let i_16777619 = Int(16777619)
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
#if swift(>=4.2)
31
  internal private(set) var hasher: Hasher
32
#else  // swift(>=4.2)
33
  // Roughly based on FNV hash: http://tools.ietf.org/html/draft-eastlake-fnv-03
34
  private(set) var hashValue = i_2166136261
35
36
  private mutating func mix(_ hash: Int) {
37
    hashValue = (hashValue ^ hash) &* i_16777619
38
  }
39
40
  private mutating func mixMap<K, V: Hashable>(map: Dictionary<K,V>) {
41
    var mapHash = 0
42
    for (k, v) in map {
43
      // Note: This calculation cannot depend on the order of the items.
44
      mapHash = mapHash &+ (k.hashValue ^ v.hashValue)
45
    }
46
    mix(mapHash)
47
  }
48
#endif // swift(>=4.2)
49
50
#if swift(>=4.2)
51
0
  init(_ hasher: Hasher) {
52
0
    self.hasher = hasher
53
0
  }
54
#else
55
  init() {}
56
#endif
57
58
0
  mutating func visitUnknown(bytes: Data) throws {
59
0
    #if swift(>=4.2)
60
0
      hasher.combine(bytes)
61
0
    #else
62
0
      mix(bytes.hashValue)
63
0
    #endif
64
0
  }
65
66
0
  mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
67
0
    #if swift(>=4.2)
68
0
      hasher.combine(fieldNumber)
69
0
      hasher.combine(value)
70
0
    #else
71
0
      mix(fieldNumber)
72
0
      mix(value.hashValue)
73
0
   #endif
74
0
  }
75
76
0
  mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
77
0
    #if swift(>=4.2)
78
0
      hasher.combine(fieldNumber)
79
0
      hasher.combine(value)
80
0
    #else
81
0
      mix(fieldNumber)
82
0
      mix(value.hashValue)
83
0
    #endif
84
0
  }
85
86
0
  mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
87
0
    #if swift(>=4.2)
88
0
      hasher.combine(fieldNumber)
89
0
      hasher.combine(value)
90
0
    #else
91
0
      mix(fieldNumber)
92
0
      mix(value.hashValue)
93
0
    #endif
94
0
  }
95
96
0
  mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
97
0
    #if swift(>=4.2)
98
0
      hasher.combine(fieldNumber)
99
0
      hasher.combine(value)
100
0
    #else
101
0
      mix(fieldNumber)
102
0
      mix(value.hashValue)
103
0
    #endif
104
0
  }
105
106
0
  mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
107
0
    #if swift(>=4.2)
108
0
      hasher.combine(fieldNumber)
109
0
      hasher.combine(value)
110
0
    #else
111
0
      mix(fieldNumber)
112
0
      mix(value.hashValue)
113
0
    #endif
114
0
  }
115
116
0
  mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
117
0
    #if swift(>=4.2)
118
0
      hasher.combine(fieldNumber)
119
0
      hasher.combine(value)
120
0
    #else
121
0
      mix(fieldNumber)
122
0
      mix(value.hashValue)
123
0
    #endif
124
0
  }
125
126
  mutating func visitSingularEnumField<E: Enum>(value: E,
127
0
                                                fieldNumber: Int) {
128
0
    #if swift(>=4.2)
129
0
      hasher.combine(fieldNumber)
130
0
      hasher.combine(value)
131
0
    #else
132
0
      mix(fieldNumber)
133
0
      mix(value.hashValue)
134
0
    #endif
135
0
  }
136
137
0
  mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) {
138
0
    #if swift(>=4.2)
139
0
      hasher.combine(fieldNumber)
140
0
      value.hash(into: &hasher)
141
0
    #else
142
0
      mix(fieldNumber)
143
0
      mix(value.hashValue)
144
0
    #endif
145
0
  }
146
147
0
  mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
148
0
    assert(!value.isEmpty)
149
0
    #if swift(>=4.2)
150
0
      hasher.combine(fieldNumber)
151
0
      hasher.combine(value)
152
0
    #else
153
0
      mix(fieldNumber)
154
0
      for v in value {
155
0
        mix(v.hashValue)
156
0
      }
157
0
    #endif
158
0
  }
159
160
0
  mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
161
0
    assert(!value.isEmpty)
162
0
    #if swift(>=4.2)
163
0
      hasher.combine(fieldNumber)
164
0
      hasher.combine(value)
165
0
    #else
166
0
      mix(fieldNumber)
167
0
      for v in value {
168
0
        mix(v.hashValue)
169
0
      }
170
0
    #endif
171
0
  }
172
173
0
  mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
174
0
    assert(!value.isEmpty)
175
0
    #if swift(>=4.2)
176
0
      hasher.combine(fieldNumber)
177
0
      hasher.combine(value)
178
0
    #else
179
0
      mix(fieldNumber)
180
0
      for v in value {
181
0
        mix(v.hashValue)
182
0
      }
183
0
    #endif
184
0
  }
185
186
0
  mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
187
0
    assert(!value.isEmpty)
188
0
    #if swift(>=4.2)
189
0
      hasher.combine(fieldNumber)
190
0
      hasher.combine(value)
191
0
    #else
192
0
      mix(fieldNumber)
193
0
      for v in value {
194
0
        mix(v.hashValue)
195
0
      }
196
0
    #endif
197
0
  }
198
199
0
  mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
200
0
    assert(!value.isEmpty)
201
0
    #if swift(>=4.2)
202
0
      hasher.combine(fieldNumber)
203
0
      hasher.combine(value)
204
0
    #else
205
0
      mix(fieldNumber)
206
0
      for v in value {
207
0
        mix(v.hashValue)
208
0
      }
209
0
    #endif
210
0
  }
211
212
0
  mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
213
0
    assert(!value.isEmpty)
214
0
    #if swift(>=4.2)
215
0
      hasher.combine(fieldNumber)
216
0
      hasher.combine(value)
217
0
    #else
218
0
      mix(fieldNumber)
219
0
      for v in value {
220
0
        mix(v.hashValue)
221
0
      }
222
0
    #endif
223
0
  }
224
225
0
  mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
226
0
    assert(!value.isEmpty)
227
0
    #if swift(>=4.2)
228
0
      hasher.combine(fieldNumber)
229
0
      hasher.combine(value)
230
0
    #else
231
0
      mix(fieldNumber)
232
0
      for v in value {
233
0
        mix(v.hashValue)
234
0
      }
235
0
    #endif
236
0
  }
237
238
0
  mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
239
0
    assert(!value.isEmpty)
240
0
    #if swift(>=4.2)
241
0
      hasher.combine(fieldNumber)
242
0
      hasher.combine(value)
243
0
    #else
244
0
      mix(fieldNumber)
245
0
      for v in value {
246
0
        mix(v.hashValue)
247
0
      }
248
0
    #endif
249
0
  }
250
251
0
  mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
252
0
    assert(!value.isEmpty)
253
0
    #if swift(>=4.2)
254
0
      hasher.combine(fieldNumber)
255
0
      hasher.combine(value)
256
0
    #else
257
0
      mix(fieldNumber)
258
0
      for v in value {
259
0
        mix(v.hashValue)
260
0
      }
261
0
    #endif
262
0
  }
263
264
0
  mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
265
0
    assert(!value.isEmpty)
266
0
    #if swift(>=4.2)
267
0
      hasher.combine(fieldNumber)
268
0
      hasher.combine(value)
269
0
    #else
270
0
      mix(fieldNumber)
271
0
      for v in value {
272
0
        mix(v.hashValue)
273
0
      }
274
0
    #endif
275
0
  }
276
277
0
  mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
278
0
    assert(!value.isEmpty)
279
0
    #if swift(>=4.2)
280
0
      hasher.combine(fieldNumber)
281
0
      hasher.combine(value)
282
0
    #else
283
0
      mix(fieldNumber)
284
0
      for v in value {
285
0
        mix(v.hashValue)
286
0
      }
287
0
    #endif
288
0
  }
289
290
0
  mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
291
0
    assert(!value.isEmpty)
292
0
    #if swift(>=4.2)
293
0
      hasher.combine(fieldNumber)
294
0
      hasher.combine(value)
295
0
    #else
296
0
      mix(fieldNumber)
297
0
      for v in value {
298
0
        mix(v.hashValue)
299
0
      }
300
0
    #endif
301
0
  }
302
303
0
  mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
304
0
    assert(!value.isEmpty)
305
0
    #if swift(>=4.2)
306
0
      hasher.combine(fieldNumber)
307
0
      hasher.combine(value)
308
0
    #else
309
0
      mix(fieldNumber)
310
0
      for v in value {
311
0
        mix(v.hashValue)
312
0
      }
313
0
    #endif
314
0
  }
315
316
0
  mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
317
0
    assert(!value.isEmpty)
318
0
    #if swift(>=4.2)
319
0
      hasher.combine(fieldNumber)
320
0
      hasher.combine(value)
321
0
    #else
322
0
      mix(fieldNumber)
323
0
      for v in value {
324
0
        mix(v.hashValue)
325
0
      }
326
0
    #endif
327
0
  }
328
329
0
  mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
330
0
    assert(!value.isEmpty)
331
0
    #if swift(>=4.2)
332
0
      hasher.combine(fieldNumber)
333
0
      hasher.combine(value)
334
0
    #else
335
0
      mix(fieldNumber)
336
0
      for v in value {
337
0
        mix(v.hashValue)
338
0
      }
339
0
    #endif
340
0
  }
341
342
0
  mutating func visitRepeatedEnumField<E: Enum>(value: [E], fieldNumber: Int) throws {
343
0
    assert(!value.isEmpty)
344
0
    #if swift(>=4.2)
345
0
      hasher.combine(fieldNumber)
346
0
      hasher.combine(value)
347
0
    #else
348
0
      mix(fieldNumber)
349
0
      for v in value {
350
0
        mix(v.hashValue)
351
0
      }
352
0
    #endif
353
0
  }
354
355
0
  mutating func visitRepeatedMessageField<M: Message>(value: [M], fieldNumber: Int) throws {
356
0
    assert(!value.isEmpty)
357
0
    #if swift(>=4.2)
358
0
      hasher.combine(fieldNumber)
359
0
      for v in value {
360
0
        v.hash(into: &hasher)
361
0
      }
362
0
    #else
363
0
      mix(fieldNumber)
364
0
      for v in value {
365
0
        mix(v.hashValue)
366
0
      }
367
0
    #endif
368
0
  }
369
370
0
  mutating func visitRepeatedGroupField<G: Message>(value: [G], fieldNumber: Int) throws {
371
0
    assert(!value.isEmpty)
372
0
    #if swift(>=4.2)
373
0
      hasher.combine(fieldNumber)
374
0
      for v in value {
375
0
        v.hash(into: &hasher)
376
0
      }
377
0
    #else
378
0
      mix(fieldNumber)
379
0
      for v in value {
380
0
        mix(v.hashValue)
381
0
      }
382
0
    #endif
383
0
  }
384
385
  mutating func visitMapField<KeyType, ValueType: MapValueType>(
386
    fieldType: _ProtobufMap<KeyType, ValueType>.Type,
387
    value: _ProtobufMap<KeyType, ValueType>.BaseType,
388
    fieldNumber: Int
389
0
  ) throws {
390
0
    #if swift(>=4.2)
391
0
      hasher.combine(fieldNumber)
392
0
      hasher.combine(value)
393
0
    #else
394
0
      mix(fieldNumber)
395
0
      mixMap(map: value)
396
0
    #endif
397
0
  }
398
399
  mutating func visitMapField<KeyType, ValueType>(
400
    fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
401
    value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
402
    fieldNumber: Int
403
0
  ) throws where ValueType.RawValue == Int {
404
0
    #if swift(>=4.2)
405
0
      hasher.combine(fieldNumber)
406
0
      hasher.combine(value)
407
0
    #else
408
0
      mix(fieldNumber)
409
0
      mixMap(map: value)
410
0
    #endif
411
0
  }
412
413
  mutating func visitMapField<KeyType, ValueType>(
414
    fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
415
    value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
416
    fieldNumber: Int
417
0
  ) throws {
418
0
    #if swift(>=4.2)
419
0
      hasher.combine(fieldNumber)
420
0
      hasher.combine(value)
421
0
    #else
422
0
      mix(fieldNumber)
423
0
      mixMap(map: value)
424
0
    #endif
425
0
  }
426
}