Move folly/Checksum.h into folly/hash/
[folly.git] / folly / hash / Checksum.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/hash/Checksum.h>
18 #include <boost/crc.hpp>
19 #include <folly/CpuId.h>
20 #include <folly/hash/detail/ChecksumDetail.h>
21 #include <algorithm>
22 #include <stdexcept>
23
24 #if FOLLY_SSE_PREREQ(4, 2)
25 #include <nmmintrin.h>
26 #endif
27
28 namespace folly {
29
30 namespace detail {
31
32 uint32_t
33 crc32c_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum);
34 #if FOLLY_SSE_PREREQ(4, 2)
35
36 uint32_t
37 crc32_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum);
38
39 // Fast SIMD implementation of CRC-32 for x86 with pclmul
40 uint32_t
41 crc32_hw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
42   uint32_t sum = startingChecksum;
43   size_t offset = 0;
44
45   // Process unaligned bytes
46   if ((uintptr_t)data & 15) {
47     size_t limit = std::min(nbytes, -(uintptr_t)data & 15);
48     sum = crc32_sw(data, limit, sum);
49     offset += limit;
50     nbytes -= limit;
51   }
52
53   if (nbytes >= 16) {
54     sum = crc32_hw_aligned(sum, (const __m128i*)(data + offset), nbytes / 16);
55     offset += nbytes & ~15;
56     nbytes &= 15;
57   }
58
59   // Remaining unaligned bytes
60   return crc32_sw(data + offset, nbytes, sum);
61 }
62
63 bool crc32c_hw_supported() {
64   static folly::CpuId id;
65   return id.sse42();
66 }
67
68 bool crc32_hw_supported() {
69   static folly::CpuId id;
70   return id.sse42();
71 }
72
73 #else
74
75 uint32_t crc32_hw(const uint8_t *data, size_t nbytes,
76     uint32_t startingChecksum) {
77   throw std::runtime_error("crc32_hw is not implemented on this platform");
78 }
79
80 bool crc32c_hw_supported() {
81   return false;
82 }
83
84 bool crc32_hw_supported() {
85   return false;
86 }
87 #endif
88
89 template <uint32_t CRC_POLYNOMIAL>
90 uint32_t crc_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
91   // Reverse the bits in the starting checksum so they'll be in the
92   // right internal format for Boost's CRC engine.
93   //     O(1)-time, branchless bit reversal algorithm from
94   //     http://graphics.stanford.edu/~seander/bithacks.html
95   startingChecksum = ((startingChecksum >> 1) & 0x55555555) |
96       ((startingChecksum & 0x55555555) << 1);
97   startingChecksum = ((startingChecksum >> 2) & 0x33333333) |
98       ((startingChecksum & 0x33333333) << 2);
99   startingChecksum = ((startingChecksum >> 4) & 0x0f0f0f0f) |
100       ((startingChecksum & 0x0f0f0f0f) << 4);
101   startingChecksum = ((startingChecksum >> 8) & 0x00ff00ff) |
102       ((startingChecksum & 0x00ff00ff) << 8);
103   startingChecksum = (startingChecksum >> 16) |
104       (startingChecksum << 16);
105
106   boost::crc_optimal<32, CRC_POLYNOMIAL, ~0U, 0, true, true> sum(
107       startingChecksum);
108   sum.process_bytes(data, nbytes);
109   return sum.checksum();
110 }
111
112 uint32_t
113 crc32c_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
114   constexpr uint32_t CRC32C_POLYNOMIAL = 0x1EDC6F41;
115   return crc_sw<CRC32C_POLYNOMIAL>(data, nbytes, startingChecksum);
116 }
117
118 uint32_t
119 crc32_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
120   constexpr uint32_t CRC32_POLYNOMIAL = 0x04C11DB7;
121   return crc_sw<CRC32_POLYNOMIAL>(data, nbytes, startingChecksum);
122 }
123
124 } // namespace detail
125
126 uint32_t crc32c(const uint8_t *data, size_t nbytes,
127     uint32_t startingChecksum) {
128   if (detail::crc32c_hw_supported()) {
129     return detail::crc32c_hw(data, nbytes, startingChecksum);
130   } else {
131     return detail::crc32c_sw(data, nbytes, startingChecksum);
132   }
133 }
134
135 uint32_t crc32(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
136   if (detail::crc32_hw_supported()) {
137     return detail::crc32_hw(data, nbytes, startingChecksum);
138   } else {
139     return detail::crc32_sw(data, nbytes, startingChecksum);
140   }
141 }
142
143 uint32_t
144 crc32_type(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
145   return ~crc32(data, nbytes, startingChecksum);
146 }
147
148 } // namespace folly