Completed encode method in Encoder. Added metadata composition, public accessors for version and data, not computed method error annotations and tests

This commit is contained in:
Dmitriy Shishkov 2021-12-11 23:28:59 +03:00
parent f9e5717913
commit 82b2908231
5 changed files with 132 additions and 16 deletions

View File

@ -3,20 +3,42 @@
#include "Encoder.hpp"
#include "Tables.hpp"
unsigned char Encoder::determite_version()
#include <stdexcept>
BitArray Encoder::encode()
{
unsigned encoded_bit_num = calculate_encoded_input_size(input.size(), method);
unsigned metadata_bit_num = calculate_metadata_size(method, ((version < 0) ? 0 : version));
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);
metadata_bit_num = calculate_metadata_size(method, version);
}
}
else if (metadata_bit_num + encoded_bit_num >= Tables::max_capability.at(corr_lvl).at(version))
throw std::invalid_argument("This version is too low for input of this size. Set negative version to calculate it dynamicly");
e.resize(metadata_bit_num + encoded_bit_num);
write_metadata(input.size(), metadata_bit_num - 4, method, e);
encode_input(metadata_bit_num);
return e;
}
unsigned char Encoder::determite_version(unsigned size, CorrectionLevel corr_lvl)
{
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);
return upper_index(sizes, size);
}
unsigned Encoder::calculate_encoded_input_size(unsigned input_size, QRCodeMethod method)
{
if (method == QRCodeMethod::Dynamic) throw std::runtime_error("Specify correct method");
unsigned bit_num = 0;
switch (method) {
@ -40,6 +62,28 @@ unsigned Encoder::calculate_encoded_input_size(unsigned input_size, QRCodeMethod
return bit_num;
}
unsigned Encoder::calculate_metadata_size(QRCodeMethod method, unsigned char version)
{
if (method == QRCodeMethod::Dynamic) throw std::runtime_error("Specify correct method");
unsigned char size = 0;
auto lengths = Tables::data_amount_lengths.at(method);
for (int i = 0; i < 2 && lengths[i].first <= version; i++)
size = lengths[i].second;
return size + 4;
}
void Encoder::write_metadata(unsigned input_size, unsigned input_bits_amount_size, QRCodeMethod method, BitArray& out)
{
if (method == QRCodeMethod::Dynamic) throw std::runtime_error("Specify correct method");
out.set(0, Tables::mode_indicator.at(method), 4);
out.set(4, input_size, input_bits_amount_size);
}
void Encoder::encode_numeric(const string& input, BitArray& out, unsigned offset)
{
for (unsigned i = 0; i < input.size() / 3; i++) {
@ -82,3 +126,17 @@ unsigned char Encoder::encode_char(char ch)
throw std::runtime_error("No such character in alphabet. Use bytes QR code method.");
}
unsigned char Encoder::get_version()
{
if (version < 0) throw std::runtime_error("Determite version before getting it");
return version;
}
BitArray Encoder::get_data()
{
if (e.size == 0) throw std::runtime_error("Data is not calculated yet");
return e;
}

View File

@ -11,15 +11,18 @@ 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_ } {};
Encoder(const string& input_, CorrectionLevel corr_lvl_ = CorrectionLevel::M, QRCodeMethod method_ = QRCodeMethod::Dynamic, char version_ = -1) : input{ input_ }, corr_lvl{ corr_lvl_ }, method{ method_ }, version{ version_ } {};
unsigned char determite_version();
BitArray encode();
void encode();
static unsigned char determite_version(unsigned size, CorrectionLevel corr_lvl);
static unsigned calculate_encoded_input_size(unsigned input_size, QRCodeMethod method);
static unsigned calculate_metadata_size(QRCodeMethod method, unsigned char version);
constexpr void encode_input() {
static void write_metadata(unsigned input_size, unsigned input_bits_amount_size, QRCodeMethod method, BitArray& out);
constexpr void encode_input(unsigned offset) {
switch (method) {
case QRCodeMethod::Numeric:
encode_numeric(input, e, offset);
@ -39,14 +42,16 @@ public:
static unsigned char encode_char(char ch);
unsigned char get_version();
BitArray get_data();
private:
const string& input;
const string input;
CorrectionLevel corr_lvl;
const QRCodeMethod method;
unsigned char version;
char version;
BitArray e;
unsigned offset = 0;
};
template <typename T, unsigned N>

View File

@ -15,4 +15,16 @@ const std::map<CorrectionLevel, const std::array<unsigned, 20>> Tables::max_capa
{ 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}} }
};
};
const std::map<QRCodeMethod, unsigned char> Tables::mode_indicator{
{ QRCodeMethod::Numeric, 0b0001 },
{ QRCodeMethod::Alphabetic, 0b0010 },
{ QRCodeMethod::Byte, 0b0100 }
};
const std::map<QRCodeMethod, const std::array<const std::pair<unsigned char, unsigned char>, 3>> Tables::data_amount_lengths{
{ QRCodeMethod::Numeric, {{ {0, 10}, {8, 12}, {25, 14} }} },
{ QRCodeMethod::Alphabetic, {{ {0, 9}, {8, 11}, {25, 13} }} } ,
{ QRCodeMethod::Byte, {{ {0, 8}, {8, 16}, {25, 16} }} }
};

View File

@ -9,5 +9,8 @@ class Tables {
public:
static const std::array<char, 45>alphabetic;
static const std::map<CorrectionLevel, const std::array<unsigned, 20>> max_capability;
static const std::map<CorrectionLevel, const std::array<unsigned, 20>>max_capability;
static const std::map<QRCodeMethod, unsigned char>mode_indicator;
static const std::map<QRCodeMethod, const std::array<const std::pair<unsigned char, unsigned char>, 3>>data_amount_lengths;
};

View File

@ -4,6 +4,8 @@
#include "../QRCodeLibrary/Encoder.hpp"
/* upper_index function */
TEST(UpperIndexTests, FindsExactMatch) {
array<int, 5> arr1{ 1, 2, 3, 4, 5 };
@ -30,6 +32,8 @@ TEST(UpperIndexTests, IfNothingFoundReturnsArrSize) {
EXPECT_EQ(upper_index(arr, 10), 5);
}
/* Encoder class */
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);
@ -45,6 +49,26 @@ TEST(EncoderTests, CalculatesEncodedInputSize) {
EXPECT_EQ(Encoder::calculate_encoded_input_size(10, QRCodeMethod::Byte), 10*8);
}
TEST(EncoderTests, CalculatesMetadataSize) {
EXPECT_EQ(Encoder::calculate_metadata_size(QRCodeMethod::Numeric, 0), 14);
EXPECT_EQ(Encoder::calculate_metadata_size(QRCodeMethod::Alphabetic, 5), 13);
EXPECT_EQ(Encoder::calculate_metadata_size(QRCodeMethod::Numeric, 10), 16);
}
TEST(EncoderTests, DetermitesVersion) {
EXPECT_EQ(Encoder::determite_version(1600, CorrectionLevel::M), 9);
}
TEST(EncoderTests, WritesMetadata) {
string expected("010001100100");
BitArray arr(expected.size());
Encoder::write_metadata(100, expected.size() - 4, QRCodeMethod::Byte, arr);
EXPECT_EQ(std::string(arr), expected);
}
TEST(EncoderTests, EncodesNumeric) {
BitArray tmp(string("00110110001110000100101001").size());
Encoder::encode_numeric("8675309", tmp, 2);
@ -64,4 +88,18 @@ TEST(EncoderTests, EncodesBytes) {
Encoder::encode_byte(u8"Дмитрий Шишков", tmp, 4);
EXPECT_EQ(std::string(tmp), "0000110100001001010011010000101111001101000010111000110100011000001011010001100000001101000010111000110100001011100100100000110100001010100011010000101110001101000110001000110100001011101011010000101111101101000010110010");
}
TEST(EncoderTests, EncodesInput) {
Encoder e1("8675309", CorrectionLevel::M, QRCodeMethod::Numeric);
e1.encode();
EXPECT_EQ(std::string(e1.get_data()), "00010000000111110110001110000100101001");
Encoder e2("HELLO WORLD", CorrectionLevel::M, QRCodeMethod::Alphabetic);
e2.encode();
EXPECT_EQ(std::string(e2.get_data()), "00100000010110110000101101111000110100010111001011011100010011010100001101");
Encoder e3(u8"Дмитрий Шишков", CorrectionLevel::M, QRCodeMethod::Byte);
e3.encode();
EXPECT_EQ(std::string(e3.get_data()), "010000011011110100001001010011010000101111001101000010111000110100011000001011010001100000001101000010111000110100001011100100100000110100001010100011010000101110001101000110001000110100001011101011010000101111101101000010110010");
}