| 206 | // used. |
| 207 | template <typename T> |
| 208 | std::pair<std::vector<T>, std::vector<uint8_t>> |
| 209 | DictEncode(const std::vector<T>& input, int bit_width) { |
| 210 | // Create dictionary and indices. |
| 211 | std::unordered_map<T, std::size_t> index_map; |
| 212 | |
| 213 | std::vector<T> dict; |
| 214 | std::vector<uint64_t> indices; |
| 215 | |
| 216 | for (const T value : input) { |
| 217 | auto it = index_map.find(value); |
| 218 | if (it != index_map.end()) { |
| 219 | indices.push_back(it->second); |
| 220 | } else { |
| 221 | const std::size_t next_index = dict.size(); |
| 222 | index_map[value] = next_index; |
| 223 | dict.push_back(value); |
| 224 | indices.push_back(next_index); |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | // Bit pack the indices. |
| 229 | const int bytes_required = BitUtil::RoundUpNumBytes(bit_width * input.size()); |
| 230 | std::vector<uint8_t> out_data(bytes_required); |
| 231 | |
| 232 | // We do not write data if we do not have any. Doing it could also lead to undefined |
| 233 | // behaviour as out_data.data() may be nullptr and BitWriter uses memcpy internally, and |
| 234 | // passing a null pointer to memcpy is undefined behaviour. |
| 235 | if (bytes_required > 0) { |
| 236 | BitWriter writer(out_data.data(), bytes_required); |
| 237 | if (bit_width > 0) { |
| 238 | for (const uint64_t index : indices) { |
| 239 | EXPECT_TRUE(writer.PutValue(index, bit_width)); |
| 240 | } |
| 241 | } |
| 242 | writer.Flush(); |
| 243 | } |
| 244 | |
| 245 | return std::make_pair(dict, out_data); |
| 246 | } |
| 247 | |
| 248 | template <typename T> |
| 249 | void ExpectEqualsWithStride(const T* expected, int num_values, |