File: highwayhash.h

package info (click to toggle)
highwayhash 0~git20200803.9490b14-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 924 kB
  • sloc: cpp: 7,804; ansic: 326; java: 271; makefile: 145; sh: 16
file content (209 lines) | stat: -rw-r--r-- 8,430 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef HIGHWAYHASH_HIGHWAYHASH_H_
#define HIGHWAYHASH_HIGHWAYHASH_H_

// This header's templates are useful for inlining into other CPU-specific code:
// template<TargetBits Target> CodeUsingHash() { HighwayHashT<Target>(...); },
// and can also be instantiated with HH_TARGET when callers don't care about the
// exact implementation. Otherwise, they are implementation details of the
// highwayhash_target wrapper. Use that instead if you need to detect the best
// available implementation at runtime.

// WARNING: this is a "restricted" header because it is included from
// translation units compiled with different flags. This header and its
// dependencies must not define any function unless it is static inline and/or
// within namespace HH_TARGET_NAME. See arch_specific.h for details.

#include "highwayhash/arch_specific.h"
#include "highwayhash/compiler_specific.h"
#include "highwayhash/hh_types.h"

#if HH_ARCH_X64
#include "highwayhash/iaca.h"
#endif

// Include exactly one (see arch_specific.h) header, which defines a state
// object in a target-specific namespace, e.g. AVX2::HHStateAVX2.
// Attempts to use "computed includes" (#define MACRO "path/or_just_filename",
// #include MACRO) fail with 'file not found', so we need an #if chain.
#if HH_TARGET == HH_TARGET_AVX2
#include "highwayhash/hh_avx2.h"
#elif HH_TARGET == HH_TARGET_SSE41
#include "highwayhash/hh_sse41.h"
#elif HH_TARGET == HH_TARGET_VSX
#include "highwayhash/hh_vsx.h"
#elif HH_TARGET == HH_TARGET_NEON
#include "highwayhash/hh_neon.h"
#elif HH_TARGET == HH_TARGET_Portable
#include "highwayhash/hh_portable.h"
#else
#error "Unknown target, add its hh_*.h include here."
#endif

#ifndef HH_DISABLE_TARGET_SPECIFIC
namespace highwayhash {

// Alias templates (HHStateT) cannot be specialized, so we need a helper struct.
// Note that hh_*.h don't just specialize HHStateT directly because vector128.h
// must reside in a distinct namespace (to allow including it from multiple
// translation units), and it is easier if its users, i.e. the concrete HHState,
// also reside in that same namespace, which precludes specialization.
template <TargetBits Target>
struct HHStateForTarget {};

template <>
struct HHStateForTarget<HH_TARGET> {
  // (The namespace is sufficient and the additional HH_TARGET_NAME suffix is
  // technically redundant, but it makes searching easier.)
  using type = HH_TARGET_NAME::HH_ADD_TARGET_SUFFIX(HHState);
};

// Typically used as HHStateT<HH_TARGET>. It would be easier to just have a
// concrete type HH_STATE, but this alias template is required by the
// templates in highwayhash_target.cc.
template <TargetBits Target>
using HHStateT = typename HHStateForTarget<Target>::type;

// Computes HighwayHash of "bytes" using the implementation chosen by "State".
//
// "state" is a HHStateT<> initialized with a key.
// "bytes" is the data to hash (possibly unaligned).
// "size" is the number of bytes to hash; we do not read any additional bytes.
// "hash" is a HHResult* (either 64, 128 or 256 bits).
//
// HighwayHash is a strong pseudorandom function with security claims
// [https://arxiv.org/abs/1612.06257]. It is intended as a safer general-purpose
// hash, about 4x faster than SipHash and 10x faster than BLAKE2.
//
// This template allows callers (e.g. tests) to invoke a specific
// implementation. It must be compiled with the flags required by the desired
// implementation. If the entire program cannot be built with these flags, use
// the wrapper in highwayhash_target.h instead.
//
// Callers wanting to hash multiple pieces of data should duplicate this
// function, calling HHStateT::Update for each input and only Finalizing once.
template <class State, typename Result>
HH_INLINE void HighwayHashT(State* HH_RESTRICT state,
                            const char* HH_RESTRICT bytes, const size_t size,
                            Result* HH_RESTRICT hash) {
  // BeginIACA();
  const size_t remainder = size & (sizeof(HHPacket) - 1);
  const size_t truncated = size & ~(sizeof(HHPacket) - 1);
  for (size_t offset = 0; offset < truncated; offset += sizeof(HHPacket)) {
    state->Update(*reinterpret_cast<const HHPacket*>(bytes + offset));
  }

  if (remainder != 0) {
    state->UpdateRemainder(bytes + truncated, remainder);
  }

  state->Finalize(hash);
  // EndIACA();
}

// Wrapper class for incrementally hashing a series of data ranges. The final
// result is the same as HighwayHashT of the concatenation of all the ranges.
// This is useful for computing the hash of cords, iovecs, and similar
// data structures.
template <TargetBits Target>
class HighwayHashCatT {
 public:
  HH_INLINE HighwayHashCatT(const HHKey& key) : state_(key) {
    // Avoids msan uninitialized-memory warnings.
    HHStateT<Target>::ZeroInitialize(buffer_);
  }

  // Resets the state of the hasher so it can be used to hash a new string.
  HH_INLINE void Reset(const HHKey& key) {
    state_.Reset(key);
    buffer_usage_ = 0;
  }

  // Adds "bytes" to the internal buffer, feeding it to HHStateT::Update as
  // required. Call this as often as desired. Only reads bytes within the
  // interval [bytes, bytes + num_bytes). "num_bytes" == 0 has no effect.
  // There are no alignment requirements.
  HH_INLINE void Append(const char* HH_RESTRICT bytes, size_t num_bytes) {
    // BeginIACA();
    const size_t capacity = sizeof(HHPacket) - buffer_usage_;
    // New bytes fit within buffer, but still not enough to Update.
    if (HH_UNLIKELY(num_bytes < capacity)) {
      HHStateT<Target>::AppendPartial(bytes, num_bytes, buffer_, buffer_usage_);
      buffer_usage_ += num_bytes;
      return;
    }

    // HACK: ensures the state is kept in SIMD registers; otherwise, Update
    // constantly load/stores its operands, which is much slower.
    // Restrict-qualified pointers to external state or the state_ member are
    // not sufficient for keeping this in registers.
    HHStateT<Target> state_copy = state_;

    // Have prior bytes to flush.
    const size_t buffer_usage = buffer_usage_;
    if (HH_LIKELY(buffer_usage != 0)) {
      // Calls update with prior buffer contents plus new data. Does not modify
      // the buffer because some implementations can load into SIMD registers
      // and Append to them directly.
      state_copy.AppendAndUpdate(bytes, capacity, buffer_, buffer_usage);
      bytes += capacity;
      num_bytes -= capacity;
    }

    // Buffer currently empty => Update directly from the source.
    while (num_bytes >= sizeof(HHPacket)) {
      state_copy.Update(*reinterpret_cast<const HHPacket*>(bytes));
      bytes += sizeof(HHPacket);
      num_bytes -= sizeof(HHPacket);
    }

    // Unconditionally assign even if zero because we didn't reset to zero
    // after the AppendAndUpdate above.
    buffer_usage_ = num_bytes;

    state_ = state_copy;

    // Store any remainders in buffer, no-op if multiple of a packet.
    if (HH_LIKELY(num_bytes != 0)) {
      HHStateT<Target>::CopyPartial(bytes, num_bytes, buffer_);
    }
    // EndIACA();
  }

  // Stores the resulting 64, 128 or 256-bit hash of all data passed to Append.
  // Must be called exactly once, or after a prior Reset.
  template <typename Result>  // HHResult*
  HH_INLINE void Finalize(Result* HH_RESTRICT hash) {
    // BeginIACA();
    HHStateT<Target> state_copy = state_;
    const size_t buffer_usage = buffer_usage_;
    if (HH_LIKELY(buffer_usage != 0)) {
      state_copy.UpdateRemainder(buffer_, buffer_usage);
    }
    state_copy.Finalize(hash);
    // EndIACA();
  }

 private:
  HHPacket buffer_ HH_ALIGNAS(64);
  HHStateT<Target> state_ HH_ALIGNAS(32);
  // How many bytes in buffer_ (starting with offset 0) are valid.
  size_t buffer_usage_ = 0;
};

}  // namespace highwayhash
#endif  // HH_DISABLE_TARGET_SPECIFIC
#endif  // HIGHWAYHASH_HIGHWAYHASH_H_