From 304185d1a2785b788de8fba8b2c0a09a51869a04 Mon Sep 17 00:00:00 2001 From: dm1sh Date: Sat, 11 Dec 2021 20:46:21 +0300 Subject: [PATCH] Added upper_index and input encoding with numeric, alphabetic and byte methods --- QRCodeLibrary/Encode.cpp | 29 ------- QRCodeLibrary/Encode.hpp | 14 ---- QRCodeLibrary/Encoder.cpp | 84 +++++++++++++++++++++ QRCodeLibrary/Encoder.hpp | 69 +++++++++++++++++ QRCodeLibrary/QRCodeLibrary.vcxproj | 4 +- QRCodeLibrary/QRCodeLibrary.vcxproj.filters | 4 +- QRCodeLibrary/Tables.cpp | 7 ++ QRCodeLibrary/Tables.hpp | 5 ++ tests/Encode_test.cpp | 21 ------ tests/Encoder_test.cpp | 67 ++++++++++++++++ tests/tests.vcxproj | 2 +- 11 files changed, 237 insertions(+), 69 deletions(-) delete mode 100644 QRCodeLibrary/Encode.cpp delete mode 100644 QRCodeLibrary/Encode.hpp create mode 100644 QRCodeLibrary/Encoder.cpp create mode 100644 QRCodeLibrary/Encoder.hpp delete mode 100644 tests/Encode_test.cpp create mode 100644 tests/Encoder_test.cpp diff --git a/QRCodeLibrary/Encode.cpp b/QRCodeLibrary/Encode.cpp deleted file mode 100644 index 19e24fd..0000000 --- a/QRCodeLibrary/Encode.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "pch.h" - -#include "Encode.hpp" - -unsigned Encode::calculate_encoded_input_size(unsigned input_size, QRCodeMethod method) -{ - unsigned bit_num = 0; - - switch (method) { - case QRCodeMethod::Numeric: - bit_num = 10 * (input_size / 3); - if (input_size % 3 == 2) - bit_num += 7; - else if (input_size % 3 == 1) - bit_num += 4; - break; - case QRCodeMethod::Alphabetic: - bit_num = 11 * (input_size / 2); - if (input_size % 2 == 1) - bit_num += 6; - break; - case QRCodeMethod::Byte: - bit_num = input_size * 8; - break; - } - - return bit_num; -} - diff --git a/QRCodeLibrary/Encode.hpp b/QRCodeLibrary/Encode.hpp deleted file mode 100644 index 1578cb3..0000000 --- a/QRCodeLibrary/Encode.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -#include "Method.hpp" - -using namespace std; - -class Encode -{ -public: - static unsigned calculate_encoded_input_size(unsigned input_size, QRCodeMethod method); -}; - diff --git a/QRCodeLibrary/Encoder.cpp b/QRCodeLibrary/Encoder.cpp new file mode 100644 index 0000000..0b5b3c9 --- /dev/null +++ b/QRCodeLibrary/Encoder.cpp @@ -0,0 +1,84 @@ +#include "pch.h" + +#include "Encoder.hpp" +#include "Tables.hpp" + +unsigned char Encoder::determite_version() +{ + const auto& sizes = Tables::max_capability.at(corr_lvl); + + return upper_index(sizes, input.size()); +} + +void Encoder::encode() +{ + unsigned bit_num = calculate_encoded_input_size(input.size(), method); +} + +unsigned Encoder::calculate_encoded_input_size(unsigned input_size, QRCodeMethod method) +{ + unsigned bit_num = 0; + + switch (method) { + case QRCodeMethod::Numeric: + bit_num = 10 * (input_size / 3); + if (input_size % 3 == 2) + bit_num += 7; + else if (input_size % 3 == 1) + bit_num += 4; + break; + case QRCodeMethod::Alphabetic: + bit_num = 11 * (input_size / 2); + if (input_size % 2 == 1) + bit_num += 6; + break; + case QRCodeMethod::Byte: + bit_num = input_size * 8; + break; + } + + return bit_num; +} + +void Encoder::encode_numeric(const string& input, BitArray& out, unsigned offset) +{ + for (unsigned i = 0; i < input.size() / 3; i++) { + int 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)); + out.set(offset + input.size() / 3 * 10, bin, 7); + } + else if (input.size() % 3 == 1) + out.set(offset + input.size() / 3 * 10, input[input.size() - 1] - '0', 4); +} + +void Encoder::encode_alphabetic(const string& input, BitArray& out, unsigned offset) +{ + for (unsigned i = 0; i < input.size() / 2; i++) { + int bin = encode_char(input[i * 2]) * 45 + encode_char(input[i * 2 + 1]); + out.set(offset + i * 11, bin, 11); + } + + if (input.size() % 2 == 1) { + int bin = encode_char(input[input.size() - 1]); + out.set(offset + input.size() / 2 * 11, bin, 6); + } +} + +void Encoder::encode_byte(const string& input, BitArray& out, unsigned offset) +{ + for (unsigned i = 0; i < input.size(); i++) + out.set(offset + i * 8, input[i], 8); +} + +unsigned char Encoder::encode_char(char ch) +{ + for (unsigned i = 0; i < Tables::alphabetic.size(); i++) + if (ch == Tables::alphabetic.at(i)) + return i; + + throw std::runtime_error("No such character in alphabet. Use bytes QR code method."); +} diff --git a/QRCodeLibrary/Encoder.hpp b/QRCodeLibrary/Encoder.hpp new file mode 100644 index 0000000..be6d033 --- /dev/null +++ b/QRCodeLibrary/Encoder.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#include "Method.hpp" +#include "BitArray.hpp" + +using namespace std; + +class Encoder +{ +public: + Encoder(const string& input_, CorrectionLevel corr_lvl_, QRCodeMethod method_, unsigned char version_) : input{ input_ }, corr_lvl{ corr_lvl_ }, method{ method_ }, version{ version_ } {}; + + unsigned char determite_version(); + + void encode(); + + static unsigned calculate_encoded_input_size(unsigned input_size, QRCodeMethod method); + + constexpr void encode_input() { + switch (method) { + case QRCodeMethod::Numeric: + encode_numeric(input, e, offset); + break; + case QRCodeMethod::Alphabetic: + encode_alphabetic(input, e, offset); + break; + case QRCodeMethod::Byte: + encode_byte(input, e, offset); + break; + } + }; + + static void encode_numeric(const string& input, BitArray& out, unsigned offset); + static void encode_alphabetic(const string& input, BitArray& out, unsigned offset); + static void encode_byte(const string& input, BitArray& out, unsigned offset); + + static unsigned char encode_char(char ch); + +private: + const string& input; + CorrectionLevel corr_lvl; + const QRCodeMethod method; + unsigned char version; + + BitArray e; + unsigned offset = 0; +}; + +template +constexpr unsigned char upper_index(const array arr, T val) { + unsigned count = arr.size(), s = 0, e, step; + + while (count > 0) { + step = count / 2; + e = s + step; + + if (arr[e] < val) { + s = e + 1; + count -= step + 1; + } + else + count = step; + } + + return s; +} \ No newline at end of file diff --git a/QRCodeLibrary/QRCodeLibrary.vcxproj b/QRCodeLibrary/QRCodeLibrary.vcxproj index de4fc7f..7462e57 100644 --- a/QRCodeLibrary/QRCodeLibrary.vcxproj +++ b/QRCodeLibrary/QRCodeLibrary.vcxproj @@ -152,7 +152,7 @@ - + @@ -161,7 +161,7 @@ - + Create diff --git a/QRCodeLibrary/QRCodeLibrary.vcxproj.filters b/QRCodeLibrary/QRCodeLibrary.vcxproj.filters index 61415b4..f504f0f 100644 --- a/QRCodeLibrary/QRCodeLibrary.vcxproj.filters +++ b/QRCodeLibrary/QRCodeLibrary.vcxproj.filters @@ -30,7 +30,7 @@ Header Files - + Header Files @@ -50,7 +50,7 @@ Source Files - + Source Files diff --git a/QRCodeLibrary/Tables.cpp b/QRCodeLibrary/Tables.cpp index 7868619..ab12379 100644 --- a/QRCodeLibrary/Tables.cpp +++ b/QRCodeLibrary/Tables.cpp @@ -8,4 +8,11 @@ const std::array Tables::alphabetic{ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' +}; + +const std::map> Tables::max_capability{ + { CorrectionLevel::L, {{152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192, 2592, 2960, 3424, 3688, 4184, 4712, 5176, 5768, 6360, 6888}} }, + { CorrectionLevel::M, {{128, 224, 352, 512, 688, 864, 992, 1232, 1456, 1728, 2032, 2320, 2672, 2920, 3320, 3624, 4056, 4504, 5016, 5352}} }, + { CorrectionLevel::Q, {{104, 176, 272, 384, 496, 608, 704, 880, 1056, 1232, 1440, 1648, 1952, 2088, 2360, 2600, 2936, 3176, 3560, 3880}} }, + { CorrectionLevel::H, {{72, 128, 208, 288, 368, 480, 528, 688, 800, 976, 1120, 1264, 1440, 1576, 1784, 2024, 2264, 2504, 2728, 3080}} } }; \ No newline at end of file diff --git a/QRCodeLibrary/Tables.hpp b/QRCodeLibrary/Tables.hpp index a1f88bb..40b7c65 100644 --- a/QRCodeLibrary/Tables.hpp +++ b/QRCodeLibrary/Tables.hpp @@ -1,8 +1,13 @@ #pragma once #include +#include + +#include "Method.hpp" class Tables { public: static const std::arrayalphabetic; + + static const std::map> max_capability; }; \ No newline at end of file diff --git a/tests/Encode_test.cpp b/tests/Encode_test.cpp deleted file mode 100644 index bc932b4..0000000 --- a/tests/Encode_test.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "pch.h" - -#define protected public -#define private public - -#include "../QRCodeLibrary/Encode.hpp" - -TEST(EncodeTests, CalculatesEncodedInputSize) { - EXPECT_EQ(Encode::calculate_encoded_input_size(3, QRCodeMethod::Numeric), 10); - EXPECT_EQ(Encode::calculate_encoded_input_size(3 + 1, QRCodeMethod::Numeric), 10 + 4); - EXPECT_EQ(Encode::calculate_encoded_input_size(3*3 + 1, QRCodeMethod::Numeric), 10*3 + 4); - EXPECT_EQ(Encode::calculate_encoded_input_size(3 + 2, QRCodeMethod::Numeric), 10 + 7); - - EXPECT_EQ(Encode::calculate_encoded_input_size(2, QRCodeMethod::Alphabetic), 11); - EXPECT_EQ(Encode::calculate_encoded_input_size(2*3, QRCodeMethod::Alphabetic), 11*3); - EXPECT_EQ(Encode::calculate_encoded_input_size(2 + 1, QRCodeMethod::Alphabetic), 11 + 6); - EXPECT_EQ(Encode::calculate_encoded_input_size(2*3 + 1, QRCodeMethod::Alphabetic), 11*3 + 6); - - EXPECT_EQ(Encode::calculate_encoded_input_size(5, QRCodeMethod::Byte), 5 * 8); - EXPECT_EQ(Encode::calculate_encoded_input_size(10, QRCodeMethod::Byte), 10*8); -} \ No newline at end of file diff --git a/tests/Encoder_test.cpp b/tests/Encoder_test.cpp new file mode 100644 index 0000000..c4acd88 --- /dev/null +++ b/tests/Encoder_test.cpp @@ -0,0 +1,67 @@ +#include "pch.h" + +#define private public + +#include "../QRCodeLibrary/Encoder.hpp" + +TEST(UpperIndexTests, FindsExactMatch) { + array arr1{ 1, 2, 3, 4, 5 }; + + for (int i = 0; i < 5; i++) + EXPECT_EQ(upper_index(arr1, i+1), i); + + array arr2{ 1, 2, 3, 4 }; + + for (int i = 0; i < 4; i++) + EXPECT_EQ(upper_index(arr2, i + 1), i); +} + +TEST(UpperIndexTests, FindsClosestUpper) { + array arr{ 1, 3, 5, 7, 9 }; + + for (int i = 0; i < 5; i++) + EXPECT_EQ(upper_index(arr, i * 2), i); +} + +TEST(UpperIndexTests, IfNothingFoundReturnsArrSize) { + array arr{ 0, 1, 2, 3, 4 }; + + EXPECT_EQ(upper_index(arr, 5), 5); + EXPECT_EQ(upper_index(arr, 10), 5); +} + +TEST(EncoderTests, CalculatesEncodedInputSize) { + EXPECT_EQ(Encoder::calculate_encoded_input_size(3, QRCodeMethod::Numeric), 10); + EXPECT_EQ(Encoder::calculate_encoded_input_size(3 + 1, QRCodeMethod::Numeric), 10 + 4); + EXPECT_EQ(Encoder::calculate_encoded_input_size(3*3 + 1, QRCodeMethod::Numeric), 10*3 + 4); + EXPECT_EQ(Encoder::calculate_encoded_input_size(3 + 2, QRCodeMethod::Numeric), 10 + 7); + + EXPECT_EQ(Encoder::calculate_encoded_input_size(2, QRCodeMethod::Alphabetic), 11); + EXPECT_EQ(Encoder::calculate_encoded_input_size(2*3, QRCodeMethod::Alphabetic), 11*3); + EXPECT_EQ(Encoder::calculate_encoded_input_size(2 + 1, QRCodeMethod::Alphabetic), 11 + 6); + EXPECT_EQ(Encoder::calculate_encoded_input_size(2*3 + 1, QRCodeMethod::Alphabetic), 11*3 + 6); + + EXPECT_EQ(Encoder::calculate_encoded_input_size(5, QRCodeMethod::Byte), 5 * 8); + EXPECT_EQ(Encoder::calculate_encoded_input_size(10, QRCodeMethod::Byte), 10*8); +} + +TEST(EncoderTests, EncodesNumeric) { + BitArray tmp(string("00110110001110000100101001").size()); + Encoder::encode_numeric("8675309", tmp, 2); + + EXPECT_EQ(std::string(tmp), "00110110001110000100101001"); +} + +TEST(EncoderTests, EncodesAlphabetic) { + BitArray tmp(string("0000110000101101111000110100010111001011011100010011010100001101").size()); + Encoder::encode_alphabetic("HELLO WORLD", tmp, 3); + + EXPECT_EQ(std::string(tmp), "0000110000101101111000110100010111001011011100010011010100001101"); +} + +TEST(EncoderTests, EncodesBytes) { + BitArray tmp(string("0000110100001001010011010000101111001101000010111000110100011000001011010001100000001101000010111000110100001011100100100000110100001010100011010000101110001101000110001000110100001011101011010000101111101101000010110010").size()); + Encoder::encode_byte(u8"Дмитрий Шишков", tmp, 4); + + EXPECT_EQ(std::string(tmp), "0000110100001001010011010000101111001101000010111000110100011000001011010001100000001101000010111000110100001011100100100000110100001010100011010000101110001101000110001000110100001011101011010000101111101101000010110010"); +} \ No newline at end of file diff --git a/tests/tests.vcxproj b/tests/tests.vcxproj index 4680b04..70adb11 100644 --- a/tests/tests.vcxproj +++ b/tests/tests.vcxproj @@ -37,7 +37,7 @@ - +