Files
cpu-check/pattern_generator.cc
2021-06-18 09:44:00 -07:00

227 lines
6.9 KiB
C++

// Copyright 2020 Google LLC
//
// 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.
#include "pattern_generator.h"
#include <unistd.h>
#include <cmath>
#include <cstdint>
#include <fstream>
#include "log.h"
namespace cpu_check {
namespace {
// So-called Logistic Map with parameter 4.0.
// Floating point approximation aside, if v is in the closed unit interval than
// ChaoticF1(v) is in the closed unit interval.
template <typename T>
T ChaoticF1(T v) {
return 4.0 * v * (1.0 - v);
}
// Reciprocal-like function valid over closed unit interval.
template <typename T>
T Recip(T v) {
return 1.0 / (v + 0.1);
}
// Inverse of Recip for v in closed unit interval.
template <typename T>
T Unrecip(T v) {
return (1.0 / v) - 0.1;
}
template <typename T>
T ReciprocatedChaos(T v) {
return Recip(ChaoticF1(Unrecip(v)));
}
std::vector<std::string> ReadDict() {
// Dictionary search paths
static const char* dicts[] = {
"/usr/share/dict/words",
"words",
};
std::vector<std::string> words;
std::ifstream f;
for (const auto& d : dicts) {
f.open(d, std::ifstream::in);
if (f.is_open()) break;
f.clear();
}
if (!f.is_open()) return words;
LOG(DEBUG) << "Reading words.";
std::string word;
while (!f.eof()) {
std::getline(f, word);
words.push_back(word);
}
f.close();
LOG(DEBUG) << "Read " << words.size() << " words.";
std::sort(words.begin(), words.end(),
[](const std::string& a, const std::string& b) {
return a.size() < b.size();
});
return words;
}
} // namespace
FloatingPointResults FillBufferSystematic::Generate(
uint64_t round, MalignBuffer::CopyMethod copy_method, bool use_repstos,
bool exercise_floating_point, MalignBuffer* b) const {
const uint64_t pid = getpid();
// Format: 2 bytes of PID, 3 bytes of round number, 3 bytes of offset.
// Note: Perhaps should be AC-modulated. Perhaps should be absolute aligned
// for easier recognition.
// Note: appropriate for LE machines only.
FloatingPointResults fp;
fp.d = std::max<uint64_t>(round, 2);
for (size_t i = 0; i * 8 < b->size(); i++) {
const size_t p = 8 * i;
const size_t k = std::min<size_t>(8, b->size() - p);
const uint64_t v =
((pid & 0xffff) << 48) | ((round & 0xffffff) << 24) | (i & 0xffffff);
for (size_t m = 0; m < k; m++) {
(b->data())[p + m] = *(reinterpret_cast<const char*>(&v) + m);
}
if (exercise_floating_point) {
fp.d = ReciprocatedChaos<double>(fp.d);
}
}
return fp;
}
FloatingPointResults FillBufferRandom::Generate(
uint64_t round, MalignBuffer::CopyMethod copy_method, bool use_repstos,
bool exercise_floating_point, MalignBuffer* b) const {
std::knuth_b rng(round);
std::uniform_int_distribution<uint64_t> dist(
0, std::numeric_limits<uint64_t>::max());
FloatingPointResults fp;
fp.f = std::max<uint64_t>(round, 2);
size_t p = 0;
const size_t length = b->size();
// Repeatedly append random number (one to eight) random bytes.
while (p < length) {
const size_t max_span = std::min<size_t>(length - p, 8);
const size_t z = std::uniform_int_distribution<size_t>(1, max_span)(rng);
const uint64_t v = dist(rng);
b->CopyFrom(p, absl::string_view(reinterpret_cast<const char*>(&v), z),
copy_method);
p += z;
if (exercise_floating_point) {
fp.f = ReciprocatedChaos<float>(fp.f);
}
}
return fp;
}
FloatingPointResults FillBufferText::Generate(
uint64_t round, MalignBuffer::CopyMethod copy_method, bool use_repstos,
bool exercise_floating_point, MalignBuffer* b) const {
std::knuth_b rng(round);
std::exponential_distribution<double> dist(20);
FloatingPointResults fp;
fp.ld = std::max<uint64_t>(round, 2);
const size_t bufsize = b->size();
size_t pos = 0;
while (pos < bufsize) {
const size_t r = std::min(static_cast<size_t>(dist(rng) * words_.size()),
words_.size() - 1);
const auto& word = words_[r];
const size_t wordlen = word.size();
if (pos + wordlen >= bufsize) {
break;
}
b->CopyFrom(pos, word, copy_method);
pos += wordlen;
if (pos < bufsize) {
b->Memset(pos, ' ', 1, use_repstos);
pos++;
}
if (exercise_floating_point) {
fp.ld = ReciprocatedChaos<long double>(fp.ld);
}
}
// Pad with spaces
b->Memset(pos, ' ', bufsize - pos, use_repstos);
return fp;
}
FloatingPointResults FillBufferGrilledCheese::Generate(
uint64_t round, MalignBuffer::CopyMethod copy_method, bool use_repstos,
bool exercise_floating_point, MalignBuffer* b) const {
std::knuth_b rng(round);
FloatingPointResults fp;
fp.f = std::max<uint64_t>(round, 2);
fp.d = std::max<uint64_t>(round, 2);
const size_t kAdvance = 15;
const size_t kWindow = 64;
unsigned char flavor = 0;
b->Memset(0, 0, b->size(), use_repstos);
for (int base = kWindow; base < b->size(); base += kAdvance) {
if (std::uniform_int_distribution<int>(0, 1)(rng)) continue;
flavor++;
const size_t start =
std::uniform_int_distribution<size_t>(base - kWindow, base)(rng);
const size_t end = std::uniform_int_distribution<int>(start, base)(rng);
b->Memset(start, flavor, 1 + end - start, use_repstos);
if (exercise_floating_point) {
fp.f = ReciprocatedChaos<float>(fp.f);
fp.d = ReciprocatedChaos<double>(fp.d);
}
}
return fp;
}
PatternGenerators::PatternGenerators() : words_(ReadDict()) {
generators_.emplace_back(new FillBufferSystematic());
generators_.emplace_back(new FillBufferRandom());
if (!words_.empty()) {
generators_.emplace_back(new FillBufferText(words_));
} else {
LOG(WARN) << "No word list found, skipping Text patterns";
}
generators_.emplace_back(new FillBufferGrilledCheese());
}
const PatternGenerator& PatternGenerators::RandomGenerator(
uint64_t round) const {
std::knuth_b rng(round);
const size_t k =
std::uniform_int_distribution<size_t>(0, generators_.size() - 1)(rng);
return *generators_[k];
}
FloatingPointResults PatternGenerators::Generate(
const PatternGenerator& generator, const MalignBuffer::PunchedHole& hole,
uint64_t round, MalignBuffer::CopyMethod copy_method, bool use_repstos,
bool exercise_floating_point, MalignBuffer* b) const {
const FloatingPointResults f = generator.Generate(
round, copy_method, use_repstos, exercise_floating_point, b);
b->PunchHole(hole, use_repstos);
return f;
}
} // namespace cpu_check