Compare commits

...

1 Commits

Author SHA1 Message Date
92516db383
Added comments in russian 2021-12-27 17:14:36 +03:00
18 changed files with 241 additions and 193 deletions

View File

@ -1,31 +1,36 @@
#include <iostream>
#include <locale>
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#include <Windows.h>
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32)
#define IS_WINDOWS
#endif
#ifdef IS_WINDOWS
#include <IS_WINDOWSows.h>
#endif
#include "../QRCodeLibrary/QRCode.hpp"
using namespace std;
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#ifdef IS_WINDOWS
// Переводит строку из кодировки CP 1251 в UTF-8
std::string cp1251_to_utf8(const char* str)
{
std::string res;
WCHAR* ures = NULL;
char* cres = NULL;
int result_u = MultiByteToWideChar(1251, 0, str, -1, 0, 0);
int result_u = MultiByteToWideChar(1251, 0, str, -1, 0, 0); // определяем сколько символов будет иметь широкосимвольная строка (UTF-16)
if (result_u != 0)
{
ures = new WCHAR[result_u];
ures = new WCHAR[result_u]; // инициализируем временную строку для символов в UTF-16
if (MultiByteToWideChar(1251, 0, str, -1, ures, result_u))
{
int result_c = WideCharToMultiByte(CP_UTF8, 0, ures, -1, 0, 0, 0, 0);
int result_c = WideCharToMultiByte(CP_UTF8, 0, ures, -1, 0, 0, 0, 0); // определяем сколько символов будет иметь строка в кодировке UTF-8
if (result_c != 0)
{
cres = new char[result_c];
cres = new char[result_c]; // инициализируем временную строку для символов в UTF-8
if (WideCharToMultiByte(CP_UTF8, 0, ures, -1, cres, result_c, 0, 0))
{
res = cres;
@ -40,6 +45,7 @@ std::string cp1251_to_utf8(const char* str)
return res;
}
#else
// создаёт строку, состоящую из k раз повторённых строк input
string str_of(unsigned k, const string& input) {
string res;
for (;k > 0; k--)
@ -49,19 +55,20 @@ string str_of(unsigned k, const string& input) {
#endif
int main() {
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
SetConsoleCP(1251);
#ifdef IS_WINDOWS
SetConsoleCP(1251); // устанавливаем кодировку в консоли CP 1251
#endif
string input, buff;
string input; // строка, содержащая пользовательский ввод
string buff; // буфер для ввода строк
while (getline(cin, buff)) {
input += buff + '\n';
while (getline(cin, buff)) { // пока пользователь не ввёл символ EOF
input += buff + '\n'; // сохраняем ввод с символом переноса строки в конце
}
input.pop_back();
input.pop_back(); // удаляем последний добавленный символ переноса строки
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
input = cp1251_to_utf8(input.c_str());
#ifdef IS_WINDOWS
input = cp1251_to_utf8(input.c_str()); // переводим ввод в UTF-8
#endif
QRCode qr(input, CorrectionLevel::H);
@ -70,26 +77,27 @@ int main() {
#define SQUARE_WIDTH 2
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
const string long_sep = string((res.size() + 8) * SQUARE_WIDTH, 219),
short_sep = string(4 * SQUARE_WIDTH, 219),
black = string(SQUARE_WIDTH, ' '),
white = string(SQUARE_WIDTH, 219);
#ifdef IS_WINDOWS
const string long_sep = string((res.size() + 8) * SQUARE_WIDTH, 219), // строка из блоков на всю ширину QR кода для рамок над и под ним
short_sep = string(4 * SQUARE_WIDTH, 219), // строка из 4 блоков для боковых рамок справа и слева вокруг QR кода
black = string(SQUARE_WIDTH, ' '), // строка для чёрного квадрата в QR коде
white = string(SQUARE_WIDTH, 219); // строка для белого квадрата в QR коде
SetConsoleCP(855);
#else
const string long_sep = str_of((res.size() + 8) * SQUARE_WIDTH, ""),
short_sep = str_of(4 * SQUARE_WIDTH, ""),
black = string(SQUARE_WIDTH, ' '),
white = str_of(SQUARE_WIDTH, "");
const string long_sep = str_of((res.size() + 8) * SQUARE_WIDTH, ""), // строка из блоков на всю ширину QR кода для рамок над и под ним
short_sep = str_of(4 * SQUARE_WIDTH, ""), // строка из 4 блоков для боковых рамок справа и слева вокруг QR кода
black = string(SQUARE_WIDTH, ' '), // строка для чёрного квадрата в QR коде
white = str_of(SQUARE_WIDTH, ""); // строка для белого квадрата в QR коде
#endif
// Вывод четырёх строк белых квадратов в качестве фона
for (int i = 0; i < 4; i++)
cout << long_sep << endl;
for (unsigned i = 0; i < res.size(); i++) {
cout << short_sep;
for (auto cell : res[i])
for (unsigned i = 0; i < res.size(); i++) { // для каждой строки QR кода
cout << short_sep; // вывод белого фона справа от строки
for (auto cell : res[i]) // для каждой клетки QR кода
switch (cell) {
case Trit::T:
cout << black;
@ -100,13 +108,14 @@ int main() {
default:
throw std::runtime_error("Empty cell is not allowed. Your QR code is corrupted.");
}
cout << short_sep << endl;
cout << short_sep << endl; // вывод белого фона слева от строки
}
// Вывод четырёх строк белых квадратов в качестве фона
for (int i = 0; i < 4; i++)
cout << long_sep << endl;
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#ifdef IS_WINDOWS
system("pause");
#endif
}
}

View File

@ -8,50 +8,50 @@ bool BitArray::get(unsigned index) const
{
if (index >= size) throw std::out_of_range("No such element in array");
return (v[index / 8] >> (8 - 1 - index % 8)) & 1;
return (v[index / 8] >> (8 - index % 8 - 1)) & 1; // получаем байт, в котором хранится искомый бит, смещаем его вправо на то, сколько бит стоят справа от индекса и применяем маску 00000001
}
BitArray::operator std::string() const
{
std::stringstream res;
for (unsigned i = 0; i < size; i++)
res << static_cast<int>((*this).get(i));
for (unsigned i = 0; i < size; i++) // для каждого бита в массиве
res << static_cast<int>(get(i)); // выводим его как число в строковый поток
return res.str();
return res.str(); // возвращаем строку, полученную из строкового потока
}
void BitArray::set(unsigned index, bool val)
{
if (index >= size) throw std::out_of_range("No such element in array");
if (val)
v[index / 8] |= 1 << (8 - 1 - index % 8);
if (val == 1)
v[index / 8] |= 1 << (8 - index % 8 - 1); // к байту, где нужно установить бит прибавляем при помощи битового или единицу, сменённую на то, сколько бит стоят справа от индекса
else
v[index / 8] &= ~(1 << (8 - 1 - index % 8));
v[index / 8] &= ~(1 << (8 - index % 8 - 1)); // к байту, где нужно установить бит применяем битовую маску, где включены все биты кроме того, который соответсвует данному индексу
}
unsigned BitArray::set(unsigned index, int32_t val, unsigned size)
{
if (index >= this->size) throw std::out_of_range("No such element in array");
unsigned shift = index % 8, written = 0;
unsigned shift = index % 8; // смещение слева внутри байта
unsigned written = 0; // количество записанных байт
if (size > this->size - index) throw std::out_of_range("Number of bits to write from this index is more than BitArray capability");
index /= 8;
// Цикл для каждого изменяемого в процессе записи байта массива пока не записали все входные данные
for (int i = index / 8; written < size; i++) {
int input_shift = size - (8 - shift) - written; // смещение справа для входных данных данных
int right_rem = input_shift < 0 ? -input_shift : 0; // то, сколько бит нужно взять из исходного массива справа от записываемых данных
while (written < size) {
int input_shift = size - (8 - shift) - written;
int right_rem = input_shift < 0 ? -input_shift : 0;
v[i] = (v[i] & (-1 << (8 - shift))) | // данные исходного байта слева от записываемых. Битовая маска зануляет те биты, которые будут браться из записываемых данных
(((1 << (8 - shift)) - 1) & // битовая маска, оставляющая 8-shift бит слева
((input_shift >= 0) ? (val >> input_shift) : (val << -input_shift))) | // если положительное, тогда смещаемся вправо, иначе - влево
(v[i] & ((1 << right_rem) - 1)); // берём последние right_rem бит исходного байта
v[index] = ((v[index] >> (8 - shift)) << (8 - shift)) |
(unsigned char)(((1 << (8 - shift)) - 1) & ((input_shift >= 0) ? (val >> input_shift) : (val << -input_shift))) |
(v[index] & ((1 << right_rem) - 1));
written += 8 - shift;
index++;
shift = 0;
written += 8 - shift; // наращиваем количество записанных бит на то, сколько бит было записано на этой итерации
shift = 0; // сбрасываем левое смещение после первой итерации
}
return written;

View File

@ -8,7 +8,8 @@
using namespace std;
constexpr unsigned ceil_div(unsigned a, unsigned b) {
// Деление a на b с округлением в большую сторону
constexpr static unsigned ceil_div(unsigned a, unsigned b) {
if (b == 0) throw runtime_error("Dividion by zero not possible");
return a / b + (a % b > 0);
}
@ -16,18 +17,24 @@ constexpr unsigned ceil_div(unsigned a, unsigned b) {
class BitArray
{
public:
// Создаёт массив заполненный нулями размера size
BitArray(unsigned size_ = 0) : size{ size_ }, v(ceil_div(size_, 8), 0) {};
BitArray(const vector<unsigned char>& input) : size(to_U(input.size()) * 8), v{ input } {};
// Создаёт массив из байт input
BitArray(const byte_list& input) : size(to_U(input.size()) * 8), v{ input } {};
operator std::string() const;
unsigned size;
vector<unsigned char> v;
unsigned size; // размер массива в битах
byte_list v;
// Принимает index - индекс в массиве, возвращает соответствующий index-ый бит
bool get(unsigned index) const;
// Принимает индекс и булевую переменную (0 или 1). Устанавливает соответствующий бит массива
void set(unsigned index, bool val);
// Принимает индекс, 32-х битное число и количество бит из числа. Устанавливает начиная с индекса size бит соответствующими младшими двоичными разрядами числа val
unsigned set(unsigned index, int32_t val, unsigned size);
// Принимает новый размер для массива в битах
void resize(unsigned new_size);
};

View File

@ -4,20 +4,19 @@
#include "Tables.hpp"
#include "utils.hpp"
vector<unsigned char>& DataBlocks::compose_joined_data_and_EC_blocks()
DataBlocks::DataBlocks(const byte_list& e_data_, CorrectionLevel corr_lvl_, char version_) : e_data{ e_data_ }, corr_lvl{ corr_lvl_ }, version{ version_ }
{
vector<pair<unsigned, unsigned>>data_block_sizes;
vector<pair<unsigned, unsigned>>data_block_sizes; // массив из пар, состоящих из размера соответствующего блока и его смещения от начала данных
divide_to_blocks(data_block_sizes, to_U(e_data.size()), Tables::data_blocks_number.at(corr_lvl).at(version));
unsigned EC_bytes_number = Tables::correction_bytes_num.at(corr_lvl).at(version);
vector<vector<unsigned char>> EC_blocks(data_block_sizes.size(), vector<unsigned char>());
for (unsigned i = 0; i < data_block_sizes.size(); i++)
vector<byte_list> EC_blocks(data_block_sizes.size(), byte_list()); // массив для наборов соответствующих блокам данных байтов коррекции
for (unsigned i = 0; i < data_block_sizes.size(); i++) // для каждого блока данных
compose_EC_bytes(EC_blocks[i], e_data.cbegin() + data_block_sizes[i].second, EC_bytes_number, data_block_sizes[i].first);
join_data_and_EC_blocks(data, e_data, data_block_sizes, EC_blocks, EC_bytes_number);
return data;
}
void DataBlocks::divide_to_blocks(vector<pair<unsigned, unsigned>>& db_sizes, unsigned data_size, unsigned db_number)
@ -27,31 +26,34 @@ void DataBlocks::divide_to_blocks(vector<pair<unsigned, unsigned>>& db_sizes, un
for (unsigned i = 0; i < db_number; i++)
db_sizes.push_back(std::make_pair(data_size / db_number, data_size / db_number * i));
for (unsigned i = 0; i < data_size % db_number; i++) {
db_sizes[db_number - 1 - i].first++;
db_sizes[db_number - 1 - i].second += (data_size % db_number - 1 - i);
for (unsigned i = 0; i < data_size % db_number; i++) { // если количество данных не делится на количество блоков
db_sizes[db_number - 1 - i].first++; // с конца дополняем блоки на 1
db_sizes[db_number - 1 - i].second += (data_size % db_number - 1 - i); // и увеличиваем смещение
}
}
void DataBlocks::compose_EC_bytes(vector<unsigned char>& res, const vector<unsigned char>::const_iterator& src, unsigned corr_bytes_num, unsigned db_size)
void DataBlocks::compose_EC_bytes(byte_list& res, const byte_list::const_iterator& src, unsigned corr_bytes_num, unsigned db_size)
{
res.reserve(max(db_size, corr_bytes_num));
res.insert(res.end(), src, src + db_size);
res.resize(res.capacity(), 0);
res.reserve(max(db_size, corr_bytes_num)); // подготавливаем массив коэффициентов
res.insert(res.end(), src, src + db_size); // записываем туда блок данных
res.resize(res.capacity(), 0); // дополняем нулями если кодов коррекции нужно больше, чем есть данных
for (unsigned j = 0; j < db_size; j++) {
unsigned char A = res[0];
auto gen_poly = Tables::reed_solomon_generative_polynomial.at(corr_bytes_num);
for (unsigned j = 0; j < db_size; j++) { // повторяем столько, сколько есть байт данных
// Сохраняем первый коэффициент и удаляем его из массива
unsigned char A = res[0];
res.erase(res.begin());
res.push_back(0);
if (A == 0)
continue;
unsigned char B = Tables::reverse_galois_field.at(A);
unsigned char B = Tables::reverse_galois_field.at(A); // переводим его в логарифм
for (unsigned k = 0; k < corr_bytes_num; k++) {
unsigned char C = (Tables::reed_solomon_generative_polynomial.at(corr_bytes_num).at(k) + B) % 255;
res[k] ^= Tables::galois_field[C];
for (unsigned k = 0; k < corr_bytes_num; k++) { // для каждого коэффициента порождающего многочлена
unsigned char C = (gen_poly.at(k) + B) % 255; // складываем степени
res[k] ^= Tables::galois_field[C]; // переводим обратно и вычиатем
}
}
}
@ -60,20 +62,16 @@ unsigned get_db_byte_index(unsigned block_index, unsigned byte_index, const vect
return db_sizes[block_index].second + byte_index;
}
void DataBlocks::join_data_and_EC_blocks(vector<unsigned char>& res, const vector<unsigned char>& e_data, const vector<pair<unsigned, unsigned>>& db_sizes, const vector<vector<unsigned char>>& ec_codes, unsigned ec_bytes_number)
void DataBlocks::join_data_and_EC_blocks(byte_list& res, const byte_list& e_data, const vector<pair<unsigned, unsigned>>& db_sizes, const vector<byte_list>& ec_codes, unsigned ec_bytes_number)
{
if (ec_codes.size())
res.reserve(e_data.size() + ec_codes.at(0).size() * ec_codes.size());
else
res.reserve(e_data.size());
res.reserve(e_data.size() + ec_codes.at(0).size() * ec_codes.size()); // резервируем память для результирующего массива
for (unsigned i = 0; i < db_sizes[db_sizes.size() - 1].first; i++)
for (unsigned j = 0; j < db_sizes.size(); j++)
if (i < db_sizes[j].first)
res.push_back(e_data[get_db_byte_index(j, i, db_sizes)]);
if (i < db_sizes[j].first) // если этот блок данных ещё не закончился
res.push_back(e_data[get_db_byte_index(j, i, db_sizes)]); // записываем в результирующий масив i-тый байт j-того блока данных
if (ec_codes.size())
for (unsigned i = 0; i < ec_bytes_number; i++)
for (unsigned j = 0; j < ec_codes.size(); j++)
res.push_back(ec_codes[j][i]);
for (unsigned i = 0; i < ec_bytes_number; i++)
for (unsigned j = 0; j < ec_codes.size(); j++)
res.push_back(ec_codes[j][i]); // записываем в результирующий массив i-тый байт j-того блока кодов коррекции
}

View File

@ -7,22 +7,27 @@
using namespace std;
// Разделяет данные на блоки, генерирует для них коды коррекции и объединяет их
class DataBlocks
{
public:
DataBlocks(const vector<unsigned char>& e_data_, CorrectionLevel corr_lvl_, char version_) : e_data{ e_data_ }, corr_lvl{ corr_lvl_ }, version{ version_ } {};
DataBlocks(const byte_list& e_data_, CorrectionLevel corr_lvl_, char version_);
vector<unsigned char>& compose_joined_data_and_EC_blocks();
// Возвращает массив из соединённых блоков
byte_list& get_joined_data_and_EC_blocks() { return data; };
// Считает размеры блоков данных
static void divide_to_blocks(vector<pair<unsigned, unsigned>>& db_sizes, unsigned data_size, unsigned db_number);
static void compose_EC_bytes(vector<unsigned char>& res, const vector<unsigned char>::const_iterator& src, unsigned corr_bytes_num, unsigned db_size);
static void join_data_and_EC_blocks(vector<unsigned char>&res, const vector<unsigned char>& e_data, const vector<pair<unsigned, unsigned>>& db_sizes, const vector<vector<unsigned char>>& ec_codes, unsigned ec_bytes_number);
// Генерирует коды коррекции для данного блока данных
static void compose_EC_bytes(byte_list& res, const byte_list::const_iterator& src, unsigned corr_bytes_num, unsigned db_size);
// Объединяет данные и коды коррекции
static void join_data_and_EC_blocks(byte_list&res, const byte_list& e_data, const vector<pair<unsigned, unsigned>>& db_sizes, const vector<byte_list>& ec_codes, unsigned ec_bytes_number);
private:
const vector<unsigned char>& e_data;
const byte_list& e_data;
CorrectionLevel corr_lvl;
char version;
vector<unsigned char> data;
byte_list data;
};

View File

@ -6,12 +6,12 @@
#include <stdexcept>
BitArray& Encoder::encode()
Encoder::Encoder(const byte_list& input_, CorrectionLevel corr_lvl_, QRCodeMethod method_, char version_): input{ input_ }, corr_lvl{ corr_lvl_ }, method{ method_ }, version{ version_ }
{
unsigned encoded_bit_num = calculate_encoded_input_size(to_U(input.size()), method);
unsigned metadata_bit_num = calculate_metadata_size(method, ((version < 0) ? 0 : version));
if (version < 0) {
if (version < 0) { // если передана отрицательная версия, вычисляем минимальную, в которую поместится это количество данных
version = 0;
while (metadata_bit_num + encoded_bit_num >= Tables::max_capability.at(corr_lvl).at(version)) {
version = determite_version(metadata_bit_num + encoded_bit_num, corr_lvl);
@ -27,8 +27,6 @@ BitArray& Encoder::encode()
encode_input(metadata_bit_num);
pad_data(e, metadata_bit_num + encoded_bit_num);
return e;
}
char Encoder::determite_version(unsigned size, CorrectionLevel corr_lvl)
@ -52,7 +50,7 @@ unsigned Encoder::calculate_metadata_size(QRCodeMethod method, char version)
for (int i = 0; i < 3 && lengths[i].first <= version; i++)
size = lengths[i].second;
return size + 4;
return size + 4; // четыре бита для метода кодирования
}
void Encoder::write_metadata(unsigned input_size, unsigned input_bits_amount_size, QRCodeMethod method, BitArray& out)
@ -65,17 +63,20 @@ void Encoder::write_metadata(unsigned input_size, unsigned input_bits_amount_siz
void Encoder::encode_numeric(const string& input, BitArray& out, unsigned offset)
{
int bin;
for (unsigned i = 0; i < input.size() / 3; i++) {
int bin = stoi(input.substr(i * 3, 3));
bin = stoi(input.substr(i * 3, 3));
out.set(offset + i * 10, bin, 10);
}
if (input.size() % 3 == 2) {
int bin = stoi(input.substr(input.size() - 2, 2));
bin = stoi(input.substr(input.size() - 2, 2));
out.set(offset + to_U(input.size()) / 3 * 10, bin, 7);
}
else if (input.size() % 3 == 1)
out.set(offset + to_U(input.size()) / 3 * 10, input[input.size() - 1] - '0', 4);
else if (input.size() % 3 == 1) {
bin = input[input.size() - 1] - '0';
out.set(offset + to_U(input.size()) / 3 * 10, bin, 4);
}
}
void Encoder::encode_alphabetic(const string& input, BitArray& out, unsigned offset)
@ -118,7 +119,7 @@ void Encoder::pad_data(BitArray& arr, unsigned bits_written)
arr.v[i] = ((i - encoded_bytes) % 2 == 0) ? 0b11101100 : 0b00010001;
}
BitArray Encoder::get_data() const
BitArray& Encoder::get_data()
{
if (e.size == 0) throw std::runtime_error("Data is not calculated yet");

View File

@ -10,12 +10,11 @@
using namespace std;
// Кодирует данные и добавляет метаданные
class Encoder
{
public:
Encoder(const byte_list& input_, CorrectionLevel corr_lvl_ = CorrectionLevel::M, QRCodeMethod method_ = QRCodeMethod::Dynamic, char version_ = -1) : input{ input_ }, corr_lvl{ corr_lvl_ }, method{ method_ }, version{ version_ } {};
BitArray& encode();
Encoder(const byte_list& input_, CorrectionLevel corr_lvl_ = CorrectionLevel::M, QRCodeMethod method_ = QRCodeMethod::Dynamic, char version_ = -1);
static char determite_version(unsigned size, CorrectionLevel corr_lvl);
@ -42,10 +41,11 @@ public:
static void encode_alphabetic(const string& input, BitArray& out, unsigned offset);
static void encode_byte(const byte_list& input, BitArray& out, unsigned offset);
// Дополняет данные определёнными символами если их меньше, чем предполагает версия
static void pad_data(BitArray& arr, unsigned bits_written);
constexpr char get_version() const { return version; };
BitArray get_data() const;
BitArray& get_data();
private:
static constexpr unsigned char encode_char(char ch);

View File

@ -7,7 +7,7 @@ QRCodeMethod Method::determite_method(const byte_list& input)
{
QRCodeMethod type = QRCodeMethod::Numeric;
for (auto ch : input) {
for (auto ch : input) { // перебирая байты данных
if (type == QRCodeMethod::Numeric)
if (!is_num(ch))
type = QRCodeMethod::Alphabetic;

View File

@ -9,24 +9,24 @@ QRCode::QRCode(const byte_list& input_, CorrectionLevel corr_lvl_, QRCodeMethod
input{ input_ }, corr_lvl{ corr_lvl_ }, method{ method_ }, version{ version_ }, matrix(0)
{
if (method == QRCodeMethod::Dynamic) {
if (input.size() > Tables::max_capability.at(corr_lvl).at(0) / 8)
if (input.size() > Tables::max_capability.at(corr_lvl).at(0) / 8) // Если данных мало, можно не пытаться кодировать данные, а просто использовать байты
method = Method::determite_method(input);
else
method = QRCodeMethod::Byte;
}
/* Кодирование данных */
Encoder encoder(input, corr_lvl, method, version);
const BitArray& encoded_data = encoder.encode();
const BitArray& encoded_data = encoder.get_data();
version = encoder.get_version();
/* Создание байтов коррекции и объединение данных */
DataBlocks data_blocks(encoded_data.v, corr_lvl, version);
const BitArray final_message(data_blocks.compose_joined_data_and_EC_blocks());
const BitArray final_message(data_blocks.get_joined_data_and_EC_blocks());
/* Компоновка рисунка QR кода */
matrix.set_version(version);
matrix.draw_patterns();
matrix.place_metadata(corr_lvl, mask_n);
matrix.place_data(final_message, mask_n);
}

View File

@ -19,11 +19,11 @@ public:
vector<vector<Trit>> to_vector() const { return matrix.to_vector(); };
protected:
byte_list input;
byte_list input; // данные для кодирования в QR код
CorrectionLevel corr_lvl;
QRCodeMethod method;
char version;
QRMatrix matrix;
QRMatrix matrix; // матрица, содержащая QR код
};

View File

@ -14,9 +14,9 @@ void QRMatrix::draw_patterns()
void QRMatrix::draw_finder_patterns()
{
draw_finder_square(0, 0);
draw_finder_square(to_U(c.size()) - 7, 0);
draw_finder_square(0, to_U(c.size()) - 7);
draw_finder_square(0, 0); // левый верхний
draw_finder_square(to_U(c.size()) - 7, 0); // левый нижний
draw_finder_square(0, to_U(c.size()) - 7); // правый верхний
draw_finder_square_separators();
}
@ -34,29 +34,31 @@ void QRMatrix::draw_finder_square(unsigned y, unsigned x)
void QRMatrix::draw_finder_square_separators()
{
set(7, 0, 0b0000000, 7);
set(7, to_U(c.size()) - 7, 0b0000000, 7);
set(to_U(c.size()) - 8, 0, 0b0000000, 7);
set(7, 0, 0b0000000, 7); // низ левого верхнего
set(7, to_U(c.size()) - 7, 0b0000000, 7); // низ правого верхнего
set(to_U(c.size()) - 8, 0, 0b0000000, 7); // верх левого нижнего
for (unsigned i = 0; i < 8; i++) {
set(i, 7, 0);
set(i, to_U(c.size()) - 8, 0);
set(to_U(c.size()) - 8 + i, 7, 0);
set(i, 7, 0); // право левого верхнего
set(i, to_U(c.size()) - 8, 0); // лево верхнего правого
set(to_U(c.size()) - 8 + i, 7, 0); // право нижнего левого
}
}
void QRMatrix::draw_alignment_patters()
{
auto& coordinates = Tables::alignment_patterns_coordinates.at(version);
// Нужно поставить выравнивающий узор в каждой парной комбинации координат за исключением тех, где происходит пересечение с поисковыми узорами
for (unsigned i = 0; i < coordinates.size(); i++) {
unsigned s = i, e = to_U(coordinates.size());
if (coordinates[i] == 6)
s++, e--;
unsigned s = i, e = to_U(coordinates.size()); // начинаем выбирать вторую координату с i-того индекса, така как комбинации до него уже поставлены отражением относительно главной диагонали
if (coordinates[i] == 6) // если одна из координат равна 6, значит крайние координаты (6 и ближняя к другому краю) будут давать пересечение с поисковыми паттернами
s++, e--; // а значит их неужно исключить
for (unsigned j = s; j < e; j++) {
draw_alignment_square(coordinates[i], coordinates[j]);
if (i != j)
draw_alignment_square(coordinates[j], coordinates[i]);
if (i != j) // если координаты различны
draw_alignment_square(coordinates[j], coordinates[i]); // рисуем симметрично относительно главной диагонали
}
}
}
@ -73,8 +75,10 @@ void QRMatrix::draw_alignment_square(unsigned y, unsigned x)
void QRMatrix::draw_timing_patterns()
{
for (unsigned i = 8; i < c.size() - 8; i += 2) {
// вертикальная полоса синхронизации
set(6, i, 1);
set(6, i + 1, 0);
// горизонтальная
set(i, 6, 1);
set(i + 1, 6, 0);
}
@ -87,35 +91,43 @@ void QRMatrix::draw_dark_module()
void QRMatrix::place_metadata(CorrectionLevel corr_lvl, unsigned char mask_n)
{
if (version >= 6) {
if (version >= 6) { // для версий начиная с седьмой
const auto& v_codes = Tables::version_codes.at(version-6);
for (unsigned i = 0; i < 3; i++)
for (unsigned j = 0; j < 6; j++) {
set(to_U(c.size()) - 11 + i, j, (v_codes.at(i) >> (6 - 1 - j)) & 1);
set(j, to_U(c.size()) - 11 + i, (v_codes.at(i) >> (6 - 1 - j)) & 1);
// побитово наносим информацию
set(to_U(c.size()) - 11 + i, j, (v_codes.at(i) >> (6 - 1 - j)) & 1); // горизонтально и
set(j, to_U(c.size()) - 11 + i, (v_codes.at(i) >> (6 - 1 - j)) & 1); // вертикально
// соответствующие биты информации получаются путём смещения и применения единичной битовой маски
}
}
unsigned code = Tables::corr_lvl_and_mask_codes.at(corr_lvl)[mask_n];
// Используются два "курсора" с координатами (y1, x2), (y2, x2). Первый огибает против часововй стрелки левый верхний поисковой квадрат, второй поднимается по правой стороне левого нижнего квадрата и слева направо под правым верхним квадратом
unsigned y1 = 8, y2 = to_U(c.size()) - 1, x1 = 0, x2 = 8;
for (unsigned i = 0; i < 15; i++) {
set(y1, x1, (code >> (15 - 1 - i)) & 1);
set(y2, x2, (code >> (15 - 1 - i)) & 1);
if (x1 < 8) {
x1++;
if (x1 == 6) x1++;
// Первый курсор
if (x1 < 8) { // пока не достигли правого нижнего угла квадрата
x1++; // наращиваем горизонтальную координату
if (x1 == 6) x1++; // пропускаем столбец с синхронизирующей линией
}
else {
if (x1 == 8) y1--;
if (y1 == 6) y1--;
else { // если достигли правой стороны
y1--; // уменьшаем вертикальную координату
if (y1 == 6) y1--; // пропускаем строку с синхронизирующей линией
}
if (y2 > c.size() - 8) y2--;
if (y2 == to_U(c.size()) - 8) { y2 = 8; x2 = to_U(c.size()) - 8; }
else if (y2 == 8)
x2++;
// Второй курсор
if (y2 > c.size() - 8) // пока не достигли верха левого нижнего квадрата
y2--; // уменьшаем вертикальую координату
if (y2 == to_U(c.size()) - 8) { // когда достигли, "перепрыгиваем" к правому верхнему квадрату
y2 = 8;
x2 = to_U(c.size()) - 8;
} else if (y2 == 8) // если находимся под правым верхним квадратом
x2++; // наращиваем горизонтальную координату
}
@ -126,25 +138,25 @@ void QRMatrix::place_data(const BitArray& data, unsigned char mask_n)
{
unsigned y = to_U(c.size()) - 1;
unsigned x = y;
unsigned step = 0;
bool horiz_dir = true;
bool up = true;
unsigned step = 0; // то, сколько бит данных было записано и индекс по совместительству
bool horiz_dir = true; // флаг обозначающий то, что на этом шаге нужно двигаться горизонтально
bool up = true; // флаг, обозначающий направление вертикального движения (true => вверх)
const auto& mask = Tables::mask_functions.at(mask_n);
while (true) {
if (x == 6)
x--;
if (x == 6) // если попадаем на вертикальную линию синхронизации
x--; // сразу пропускаем её и перемещаемся на столбец влево
if (get(y, x) == Trit::EMPTY) {
if (step < data.size) {
set(y, x, mask(y, x) ? data.get(step) : !data.get(step));
if (get(y, x) == Trit::EMPTY) { // если клетка не заполнена
if (step < data.size) { // если ещё не закончились данные
set(y, x, mask(y, x) ? data.get(step) : !data.get(step)); // устанавливаем клетке значение и инвертируем его если маска равно нулю
step++;
}
else set(y, x, !mask(y, x));
else set(y, x, !mask(y, x)); // иначе считаем, что значение равно 0 и применяем маску
}
if (horiz_dir)
if (horiz_dir)
x--;
else {
x++;
@ -158,8 +170,8 @@ void QRMatrix::place_data(const BitArray& data, unsigned char mask_n)
}
else {
if (y == c.size() - 1) {
if (x == 1) // т.к. сделали шаг "вправо" на строчке 130
break;
if (x == 1) // если достигли точки (c.size() - 1, 0) но x проверяется 1, так как уже на этой итерации сделали шаг "вправо" на строчке 162 (x++)
break; // можно прерывать цикл записи. запись всегда будет заканчиваться в левом нижнем углу, так как это так для первой версси, а потом размеры QR кода увеличиваются на 4 с каждой версией, а значит будет успевать пройти весь "цикл смены наравления" (визуализация представлена на изображениях в п. "Расположение данных")
x -= 2;
up = !up;

View File

@ -6,6 +6,7 @@
#include "BitArray.hpp"
#include "TritMatrix.hpp"
// Переводит версию в соответствующий ей размер QR кода
constexpr unsigned v_to_size(char version) { return version * 4 + 21; };
class QRMatrix : public TritMatrix
@ -13,22 +14,47 @@ class QRMatrix : public TritMatrix
public:
QRMatrix(char version_) : TritMatrix(v_to_size(version_)), version{ version_ } {};
// Рисует стандартные обязательные элементы QR кода
void draw_patterns();
// Рисует поисковые узоры
void draw_finder_patterns();
/* Рисует поисковой квадрат с координатами (y, x)
#######
# #
# ### #
# ### #
# ### #
# #
#######
*/
void draw_finder_square(unsigned y, unsigned x);
// Рисует контуры вокруг поисковых квадратов
void draw_finder_square_separators();
// Рисует выравнивающие узоры
void draw_alignment_patters();
/* Рисует выравнивающий квадрат с координатами центра (y, x)
#####
# #
# # #
# #
#####
*/
void draw_alignment_square(unsigned y, unsigned x);
// Рисует синхронизирующие полосы
void draw_timing_patterns();
// Рисует чёрный пиксель возле левого нижнего квадрата
void draw_dark_module();
// Размещает метаданные на QR коде (уровень коррекции, номер маски, начиная с 7-ой версии, версию)
void place_metadata(CorrectionLevel corr_lvl, unsigned char mask_n);
// Размещает данные с применённой маской mask_n
void place_data(const BitArray& data, unsigned char mask_n);
// Устанавливает версию и соответственно размер матрицы
void set_version(char version);
private:

View File

@ -8,6 +8,7 @@
#include "Method.hpp"
namespace Tables {
// массив из символов, расположенных так, чтобы их индекс соответствовал коду при алфавитном кодировании
static constexpr std::array<char, 45> alphabetic{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
@ -25,6 +26,7 @@ namespace Tables {
return false;
}
// максимальное количество бит, которое можно закодировать при данном уровне коррекции и соответствующей индексу массива версии
static const std::map<CorrectionLevel, const std::array<unsigned, 40>>max_capability{
{ CorrectionLevel::L, {{ 152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192, 2592, 2960, 3424, 3688, 4184, 4712, 5176, 5768, 6360, 6888, 7456, 8048, 8752, 9392, 10208, 10960, 11744, 12248, 13048, 13880, 14744, 15640, 16568, 17528, 18448, 19472, 20528, 21616, 22496, 23648 }} },
{ CorrectionLevel::M, {{ 128, 224, 352, 512, 688, 864, 992, 1232, 1456, 1728, 2032, 2320, 2672, 2920, 3320, 3624, 4056, 4504, 5016, 5352, 5712, 6256, 6880, 7312, 8000, 8496, 9024, 9544, 10136, 10984, 11640, 12328, 13048, 13800, 14496, 15312, 15936, 16816, 17728, 18672 }} },
@ -32,17 +34,21 @@ namespace Tables {
{ CorrectionLevel::H, {{ 72, 128, 208, 288, 368, 480, 528, 688, 800, 976, 1120, 1264, 1440, 1576, 1784, 2024, 2264, 2504, 2728, 3080, 3248, 3536, 3712, 4112, 4304, 4768, 5024, 5288, 5608, 5960, 6344, 6760, 7208, 7688, 7888, 8432, 8768, 9136, 9776, 10208 }} }
};
// четырёхбитный код режима кодирования, записываемый в метаданных
static const std::map<QRCodeMethod, unsigned char>mode_indicator{
{ QRCodeMethod::Numeric, 0b0001 },
{ QRCodeMethod::Alphabetic, 0b0010 },
{ QRCodeMethod::Byte, 0b0100 }
};
// то, сколько бит должно занимать количество символов в метаданных
static const std::map<QRCodeMethod, const std::array<const std::pair<unsigned char, unsigned char>, 3>>data_amount_lengths{
{ QRCodeMethod::Numeric, {{ {0, 10}, {9, 12}, {26, 14} }} },
{ QRCodeMethod::Alphabetic, {{ {0, 9}, {9, 11}, {26, 13} }} },
{ QRCodeMethod::Byte, {{ {0, 8}, {9, 16}, {26, 16} }} }
};
// то, насколько блоков нужно разделить данные
static const std::map<CorrectionLevel, const std::array<unsigned char, 40>>data_blocks_number{
{ CorrectionLevel::L, {{ 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25 }} },
{ CorrectionLevel::M, {{ 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49 }} },
@ -50,6 +56,7 @@ namespace Tables {
{ CorrectionLevel::H, {{ 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81 }} }
};
// количество байт коррекции для одного блока данных
static const std::map<CorrectionLevel, const std::array<unsigned, 40>>correction_bytes_num{
{ CorrectionLevel::L, {{ 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 }} },
{ CorrectionLevel::M, {{ 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 }} },
@ -57,6 +64,7 @@ namespace Tables {
{ CorrectionLevel::H, {{ 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 }} }
};
// предпосчитанные коэффициенты для порождающего многочлена Рида-Соломона для данной длины
static const std::map<unsigned char, const std::vector<unsigned>>reed_solomon_generative_polynomial{
{ 7, {{ 87, 229, 146, 149, 238, 102, 21 }}},
{ 10, {{ 251, 67, 46, 61, 118, 70, 64, 94, 32, 45 }}},
@ -73,6 +81,7 @@ namespace Tables {
{ 30, {{ 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 42, 173, 226, 193, 224, 130, 156, 37, 251, 216, 238, 40, 192, 180 }}}
};
// таблица перевода чисел в двоичные логарифмы
static constexpr std::array<unsigned char, 256>galois_field{
1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38,
76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192,
@ -92,6 +101,7 @@ namespace Tables {
44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1,
};
// таблица перевода логарифмов в числа
static constexpr std::array<unsigned char, 256>reverse_galois_field{
0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75,
4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
@ -111,6 +121,7 @@ namespace Tables {
79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175
};
//
static const array<const vector<unsigned>, 40>alignment_patterns_coordinates{
vector<unsigned>(), {18,}, {22,}, {26,}, {30,},
{34,}, {6, 22, 38}, {6, 24, 42}, {6, 26, 46}, {6, 28, 50},
@ -123,6 +134,7 @@ namespace Tables {
{6, 28, 54, 80, 106, 132, 158}, {6, 32, 58, 84, 110, 136, 162}, {6, 26, 54, 82, 110, 138, 166}, {6, 30, 58, 86, 114, 142, 170}
};
// коды версий (для версий начиная с 7), наносимые на матрицу
static constexpr array<const array<unsigned char, 3>, 34>version_codes{ {
{ 0b000010, 0b011110, 0b100110 },
{ 0b010001, 0b011100, 0b111000 },
@ -160,6 +172,7 @@ namespace Tables {
{ 0b111001, 0b000100, 0b010101 }
} };
// коды уровней коррекции и масок, наносимые на матрицу
static const map<CorrectionLevel, array<unsigned short, 8>>corr_lvl_and_mask_codes{
{ CorrectionLevel::L,
{ 0b111011111000100, 0b111001011110011, 0b111110110101010, 0b111100010011101, 0b110011000101111, 0b110001100011000, 0b110110001000001, 0b110100101110110 }
@ -175,6 +188,7 @@ namespace Tables {
}
};
// Массив лямбд, масок, принимающих координаты и возвращающие булевое значение
static const array<std::function<bool(unsigned, unsigned)>, 8>mask_functions{ {
[](unsigned y, unsigned x) { return (x + y) % 2; },
[](unsigned y, unsigned x) { return y % 2; },

View File

@ -17,7 +17,7 @@ void TritMatrix::set(unsigned y, unsigned x, int32_t val, unsigned char size)
{
if (x + size > c.at(0).size()) throw std::out_of_range("Value to write is out of matrix range");
for (unsigned char i = 0; i < size; i++)
set(y, x + i, ((val >> (size - 1 - i)) & 1) ? Trit::T : Trit::F);
set(y, x + i, (val >> (size - 1 - i)) & 1); // побитово записываем последние size бит из val в матрицу в прямом порядке
}
void TritMatrix::set(unsigned y, unsigned x, bool val)
@ -50,31 +50,6 @@ string TritMatrix::to_ascii(char black, char white, char empty) const
return res;
}
string TritMatrix::to_string() const
{
string res;
for (unsigned i = 0; i < c.size(); i++) {
for (unsigned j = 0; j < c.at(0).size(); j++)
switch (c[i][j])
{
case Trit::T:
res.push_back('1');
break;
case Trit::F:
res.push_back('0');
break;
case Trit::EMPTY:
res.push_back('E');
break;
}
if (i != c.size() - 1)
res.push_back('\n');
}
return res;
}
void TritMatrix::resize(unsigned width, unsigned height)
{
c.resize(height);

View File

@ -14,19 +14,22 @@ enum Trit {
class TritMatrix
{
public:
// Создаёт матрицу heigt x width
TritMatrix(unsigned width, unsigned height) : c{ height, vector<Trit>(width, Trit::EMPTY) } {};
// Создаёт матрицу size x size
TritMatrix(unsigned size) : c{ size, vector<Trit>(size, Trit::EMPTY) } {};
Trit get(unsigned y, unsigned x) const;
void set(unsigned y, unsigned x, Trit val);
void set(unsigned y, unsigned x, bool val);
// записывает size последних бит val в строку y матрицы. 1 передодится в Trit.T, 0 в Trit.F
void set(unsigned y, unsigned x, int32_t val, unsigned char size);
void resize(unsigned width, unsigned height);
string to_ascii(char black = '#', char white = ' ', char empty = 'E') const;
string to_string() const;
string to_string() const { return to_ascii('1', '0', 'E'); };
vector<vector<Trit>> to_vector() const { return c; };
protected:

View File

@ -6,6 +6,7 @@
#include <vector>
#include <string>
// Функция для приведения типа size_t в unsigned
constexpr static unsigned to_U(size_t val) {
if (val > (std::numeric_limits<unsigned int>::max)())
throw std::runtime_error("Too big number to convert it to unsigned int" + std::to_string(val));

View File

@ -15,25 +15,25 @@ TEST(DataBlocksTests, ComposesSizesOfDatablocks) {
}
TEST(DataBlocksTests, GeneratesECBytes) {
const vector<unsigned char> input{ 64, 196, 132, 84, 196, 196, 242, 194, 4, 132, 20, 37, 34, 16, 236, 17 };
vector<unsigned char> tmp;
const byte_list input{ 64, 196, 132, 84, 196, 196, 242, 194, 4, 132, 20, 37, 34, 16, 236, 17 };
byte_list tmp;
DataBlocks::compose_EC_bytes(tmp, input.cbegin(), 28, 16);
const vector<unsigned char> res{ 16, 85, 12, 231, 54, 54, 140, 70, 118, 84, 10, 174, 235, 197, 99, 218, 12, 254, 246, 4, 190, 56, 39, 217, 115, 189, 193, 24 };
const byte_list res{ 16, 85, 12, 231, 54, 54, 140, 70, 118, 84, 10, 174, 235, 197, 99, 218, 12, 254, 246, 4, 190, 56, 39, 217, 115, 189, 193, 24 };
EXPECT_EQ(tmp, res);
}
TEST(DataBlocksTests, JoinsDataAndECBlocks) {
vector<unsigned char>joined;
const vector<unsigned char>e_data{
byte_list joined;
const byte_list e_data{
67, 85, 70, 134, 87, 38, 85, 194, 119, 50, 6, 18, 6, 103, 38,
246, 246, 66, 7, 118, 134, 242, 7, 38, 86, 22, 198, 199, 146, 6,
182, 230, 247, 119, 50, 7, 118, 134, 87, 38, 82, 6, 134, 151, 50, 7,
70, 247, 118, 86, 194, 6, 151, 50, 16, 236, 17, 236, 17, 236, 17, 236
};
const vector<pair<unsigned, unsigned>> d_b_sizes{ {15, 0}, {15, 15}, {16, 30}, {16, 46} };
const vector<vector<unsigned char>> EC_blocks{
const vector<byte_list> EC_blocks{
{ 213, 199, 11, 45, 115, 247, 241, 223, 229, 248, 154, 117, 154, 111, 86, 161, 111, 39 },
{ 87, 204, 96, 60, 202, 182, 124, 157, 200, 134, 27, 129, 209, 17, 163, 163, 120, 133 },
{ 148, 116, 177, 212, 76, 133, 75, 242, 238, 76, 195, 230, 189, 10, 108, 240, 192, 141 },
@ -42,7 +42,7 @@ TEST(DataBlocksTests, JoinsDataAndECBlocks) {
DataBlocks::join_data_and_EC_blocks(joined, e_data, d_b_sizes, EC_blocks, 18);
const vector<unsigned char>res{
const byte_list res{
67, 246, 182, 70, 85, 246, 230, 247, 70, 66, 247, 118, 134, 7, 119,
86, 87, 118, 50, 194, 38, 134, 7, 6, 85, 242, 118, 151, 194, 7,
134, 50, 119, 38, 87, 16, 50, 86, 38, 236, 6, 22, 82, 17, 18,
@ -57,7 +57,7 @@ TEST(DataBlocksTests, JoinsDataAndECBlocks) {
}
TEST(DataBlocksTests, ComposesJoinedDataAndECBlocks) {
const vector<unsigned char>e_data{
const byte_list e_data{
67, 85, 70, 134, 87, 38, 85, 194, 119, 50, 6, 18, 6, 103, 38,
246, 246, 66, 7, 118, 134, 242, 7, 38, 86, 22, 198, 199, 146, 6,
182, 230, 247, 119, 50, 7, 118, 134, 87, 38, 82, 6, 134, 151, 50, 7,
@ -66,7 +66,7 @@ TEST(DataBlocksTests, ComposesJoinedDataAndECBlocks) {
DataBlocks db(e_data, CorrectionLevel::Q, 4);
const vector<unsigned char> joined{
const byte_list joined{
67, 246, 182, 70, 85, 246, 230, 247, 70, 66, 247, 118, 134, 7, 119,
86, 87, 118, 50, 194, 38, 134, 7, 6, 85, 242, 118, 151, 194, 7,
134, 50, 119, 38, 87, 16, 50, 86, 38, 236, 6, 22, 82, 17, 18,
@ -78,5 +78,5 @@ TEST(DataBlocksTests, ComposesJoinedDataAndECBlocks) {
108, 131, 161, 163, 240, 32, 111, 120, 192, 178, 39, 133, 141, 236
};
EXPECT_EQ(db.compose_joined_data_and_EC_blocks(), joined);
EXPECT_EQ(db.get_joined_data_and_EC_blocks(), joined);
}

View File

@ -106,14 +106,11 @@ TEST(EncoderTests, PadsData) {
TEST(EncoderTests, EncodesInput) {
Encoder e1(str_to_bytes("8675309"), CorrectionLevel::Q, QRCodeMethod::Numeric);
e1.encode();
EXPECT_EQ(std::string(e1.get_data()), "00010000000111110110001110000100101001001110110000010001111011000001000111101100000100011110110000010001");
Encoder e2(str_to_bytes("HELLO WORLD"), CorrectionLevel::M, QRCodeMethod::Alphabetic);
e2.encode();
EXPECT_EQ(std::string(e2.get_data()), "00100000010110110000101101111000110100010111001011011100010011010100001101000000111011000001000111101100000100011110110000010001");
Encoder e3(str_to_bytes(u8"Дмитрий Шишков"), CorrectionLevel::Q, QRCodeMethod::Byte);
e3.encode();
EXPECT_EQ(std::string(e3.get_data()), "01000001101111010000100101001101000010111100110100001011100011010001100000101101000110000000110100001011100011010000101110010010000011010000101010001101000010111000110100011000100011010000101110101101000010111110110100001011001000001110110000010001111011000001000111101100");
}