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:
parent
f9e5717913
commit
82b2908231
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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} }} }
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
@ -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");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user