Mercurial > public > cpp-enigma
view enigma/machine.h @ 13:b9d124a15926
To improve cache performance, the enigma machine rotors are now stored
together with the reflector in a vector.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Mon, 02 Jul 2012 19:14:36 -0500 |
parents | 424111a36ed7 |
children | 919b7a0d1802 |
line wrap: on
line source
#ifndef CPP_ENIGMA_MACHINE_H #define CPP_ENIGMA_MACHINE_H // Copyright (C) 2012 by Brian Neal. // This file is part of Cpp-Enigma, the Enigma Machine simulation. // Cpp-Enigma is released under the MIT License (see License.txt). // // machine.h - This file contains the main Enigma machine class. #include <memory> #include <string> #include <vector> #include <cassert> #include "enigma_types.h" #include "rotor.h" #include "plugboard.h" namespace enigma { typedef std::vector<std::shared_ptr<rotor>> rotor_vector; class enigma_machine_error : public enigma_error { public: explicit enigma_machine_error(const std::string& what_arg) : enigma_error(what_arg) {} }; class enigma_machine { public: // construct an Enigma machine from component parts: enigma_machine(const rotor_vector& rv, std::shared_ptr<rotor> reflector, const plugboard& pb); // construct an Enigma machine with a default plugboard (no cables connected): enigma_machine(const rotor_vector& rv, std::shared_ptr<rotor> reflector); // key-sheet style constructors: enigma_machine(const std::vector<std::string>& rotor_types, const std::vector<int>& ring_settings, const std::string& reflector_name = "B", const std::string& plugboard_settings = ""); // set the rotor display (starting position) - 3 rotor version void set_display(char left, char mid, char right) { assert(rotors.size() == 4); rotors[1].set_display(left); rotors[2].set_display(mid); rotors[3].set_display(right); } // set the rotor display (starting position) - 4 rotor version void set_display(char c0, char c1, char c2, char c3) { assert(rotors.size() == 5); rotors[1].set_display(c0); rotors[2].set_display(c1); rotors[3].set_display(c2); rotors[4].set_display(c3); } // Set the rotor display (starting position) using a string; the // string length must match the number of rotors in use or a // enigma_machine_error exception will be thrown: void set_display(const std::string& val) { if (val.size() == 3 && rotors.size() == 4) { set_display(val[0], val[1], val[2]); } else if (val.size() == 4 && rotors.size() == 5) { set_display(val[0], val[1], val[2], val[3]); } else { throw enigma_machine_error("set_display invalid size"); } } // return the rotor display (starting position) as a string std::string get_display() const { std::string result; for (std::size_t i = 1; i < rotors.size(); ++i) { result += rotors[i].get_display(); } return result; } // simulate front panel key press; returns the lamp character that is lit char key_press(char c) { step_rotors(); return electric_signal(c - 'A') + 'A'; } // Process a buffer of text of length n, placing the result in an output buffer. void process_text(const char* input, char* output, std::size_t n) { for (std::size_t i = 0; i < n; ++i) { *output++ = key_press(*input++); } } std::string process_text(const std::string& input) { std::string result; result.reserve(input.size()); for (const auto& c : input) { result += key_press(c); } return result; } // Process a buffer of pre-processed text of length n, placing the result in an output buffer. void process_data(const char* input, char* output, std::size_t n) { for (std::size_t i = 0; i < n; ++i) { step_rotors(); *output++ = electric_signal(*input++) + 'A'; } } // for access to the plugboard for hill-climbing, etc plugboard& get_plugboard() { return pb; } // Returns a string representation of the enigma machine's state. Useful // for logging, etc: // std::string army_str() const { return str(true); } std::string navy_str() const { return str(false); } private: // Note that to improve cache performance, the rotors and reflectors are stored // in a contiguous vector. std::vector<rotor> rotors; // rotor & reflector array plugboard pb; rotor* r_rotor; // rightmost rotor rotor* m_rotor; // 2nd to right rotor rotor* l_rotor; // 3rd to right rotor void rotor_count_check(); void step_rotors() { // The right-most rotor's right-side ratchet is always over a pawl, and // it has no neighbor to the right, so it always rotates. // // The middle rotor will rotate if either: // 1) The right-most rotor's left side notch is over the 2nd pawl // or // 2) It has a left-side notch over the 3rd pawl // // The third rotor (from the right) will rotate only if the middle rotor // has a left-side notch over the 3rd pawl. // // Kriegsmarine model M4 has 4 rotors, but the 4th rotor (the leftmost) // does not rotate (they did not add a 4th pawl to the mechanism). const bool l_rotate = m_rotor->notch_over_pawl(); const bool m_rotate = l_rotate || r_rotor->notch_over_pawl(); r_rotor->rotate(); if (m_rotate) { m_rotor->rotate(); } if (l_rotate) { l_rotor->rotate(); } } // Simulate running an electric signal through the machine in order to // perform an encrypt or decrypt operation // signal_num - the wire (0-25) that the simulated current occurs on // Returns a lamp number to light (an integer 0-25). int electric_signal(int signal_num) { int n = pb.signal(signal_num); if (rotors.size() == 4) // 3 rotors + reflector { n = rotors[3].signal_in(n); n = rotors[2].signal_in(n); n = rotors[1].signal_in(n); n = rotors[0].signal_in(n); // reflector n = rotors[1].signal_out(n); n = rotors[2].signal_out(n); n = rotors[3].signal_out(n); } else // Kriegsmarine 4 rotor + reflector { n = rotors[4].signal_in(n); n = rotors[3].signal_in(n); n = rotors[2].signal_in(n); n = rotors[1].signal_in(n); n = rotors[0].signal_in(n); // reflector n = rotors[1].signal_out(n); n = rotors[2].signal_out(n); n = rotors[3].signal_out(n); n = rotors[4].signal_out(n); } return pb.signal(n); } std::string str(bool army) const; }; } #endif