Skip to content

Commit

Permalink
start defining C interface
Browse files Browse the repository at this point in the history
- change QR::ErrorCorrection class enum to C-compatible enum
- define wrapper functions for QR::encode
  • Loading branch information
maxlor committed Jan 16, 2024
1 parent 5637573 commit c60bc9b
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 41 deletions.
2 changes: 2 additions & 0 deletions libQRGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ target_sources(libQRGen
src/polynomial.h
src/qr.cpp
src/qr.h
src/qrgen.cpp
src/symbol.cpp
src/symbol.h
)
Expand All @@ -35,6 +36,7 @@ target_include_directories(libQRGen PUBLIC
target_link_libraries(libQRGen
icuuc
)
target_compile_definitions(libQRGen PRIVATE LIBQRGEN_COMPILE)


##### Unit Testing #####
Expand Down
80 changes: 76 additions & 4 deletions libQRGen/include/qrgen.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,84 @@
#ifndef QRGEN_H
#define QRGEN_H

#include <cstdint>
#ifdef __cplusplus
extern "C" {
#endif

namespace QRGen {
#include <stdbool.h>
#include <stddef.h>

enum class ErrorCorrection : uint8_t { L = 0, M = 1, Q = 2, H = 3 };

} // namespace QRGen
#if defined(_WIN32)
# define QRGEN_DECL_EXPORT __declspec(dllexport)
# define QRGEN_DECL_IMPORT __declspec(dllimport)
#else
# define QRGEN_DECL_EXPORT
# define QRGEN_DECL_IMPORT
#endif

#ifdef LIBQRGEN_COMPILE
#define QRGEN_EXPORT QRGEN_DECL_EXPORT
#else
#define QRGEN_EXPORT QRGEN_DECL_IMPORT
#endif


enum QRGen_ErrorCorrection { QRGen_EC_L = 0, QRGen_EC_M = 1, QRGen_EC_Q = 2, QRGen_EC_H = 3 };


/**
* Contains a QR Code.
*/
struct QRGen_Symbol {
int width; ///< The width of the QR code in number of pixels
int height; ///< The height of the QR code in number of pixels

/**
* The pixel data as a bool array. \a data contains the pixels as bools,
* row-by-row top to bottom, and left-to-right within a row. \c true
* corresponds to a black pixel, \c false to a white one.
*
* There are a total of \a width * \a height elements in \a data.
*/
bool *data;
};


/**
* Create a QR code from \a data using defaults. The defaults are:
* - error correction level M
* - use best mask
* - use smallest version possible.
*
* A pointer to a QRGen_Symbol struct is returned. It's width and height are
* is non-zero on success, and are both 0 if the QR code could not be created.
*
* The returned symbol must be deallocated using QRGen_free_symbol().
*
* @param data an UTF-8-encoded string
* @param len the number of bytes in \a data.
* @param the QR Code, or a QRGen_Symbol struct filled with zeroes.
*/
struct QRGen_Symbol *QRGen_encode(const char *data, size_t len) QRGEN_EXPORT;


/**
* Same as QRGen_encode(), but also lets you specify the error correction
* level which is used.
*
* @see ::QRGen_Symbol
*/
struct QRGen_Symbol *QRGen_encode_ec(const char *data, size_t len, QRGen_ErrorCorrection ec) QRGEN_EXPORT;


/** Free the memory used by \a symbol. */
void QRGen_free_symbol(QRGen_Symbol *symbol) QRGEN_EXPORT;



#ifdef __cplusplus
} // extern "C"
#endif

#endif // QRGEN_H
38 changes: 24 additions & 14 deletions libQRGen/src/qr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

using namespace std;
using namespace std::placeholders;
using namespace QRGen;


static bool containsKey(const unordered_map<char32_t, uint8_t> map, char32_t key);
Expand Down Expand Up @@ -123,7 +122,7 @@ const array<array<array<array<uint16_t, 3>, 2>, 4>, 40> QR::ecBlocks {{
}};


Symbol QR::encode(u16string_view data, ErrorCorrection ec, uint8_t version, uint8_t mask) {
Symbol QR::encode(u16string_view data, QRGen_ErrorCorrection ec, uint8_t version, uint8_t mask) {
assert(version <= 40);
assert(mask == 255 || mask < 8);
EncodeResult segmentResult = encodeSegment(data, ec);
Expand All @@ -148,7 +147,7 @@ Symbol QR::encode(u16string_view data, ErrorCorrection ec, uint8_t version, uint
}


QR::EncodeResult QR::encodeSegment(std::u16string_view data, ErrorCorrection ec) {
QR::EncodeResult QR::encodeSegment(std::u16string_view data, QRGen_ErrorCorrection ec) {
static const EncodeResult failure { false, {}, Mode::terminator, 0 };
EncodeResult contentResult = encodeContent(data);
if (!contentResult.success) { return failure; }
Expand Down Expand Up @@ -216,17 +215,13 @@ QR::EncodeResult QR::encodeContent(u16string_view data) {
return failure;
}

const bool isNumeric = all_of(data.begin(), data.end(), &QR::isNumeric);
const bool isAlphanumeric = isNumeric ||
all_of(data.begin(), data.end(), bind(containsKey, alphaNumericCharacters, _1));
const bool isEightbit = isAlphanumeric ||
all_of(data.begin(), data.end(), bind(containsKey, ISO8859_1, _1));
const bool isEightbit = all_of(data.begin(), data.end(), bind(containsKey, ISO8859_1, _1));
const bool isKanji = false;
(void)isKanji; // TODO

if (isNumeric) {
if (isNumeric(data)) {
return encodeNumeric(data);
} else if (isAlphanumeric) {
} else if (isAlphaNumeric(data)) {
return encodeAlphanumeric(data);
} else if (isEightbit) {
return encodeEightbit(data);
Expand All @@ -237,7 +232,7 @@ QR::EncodeResult QR::encodeContent(u16string_view data) {
}


vector<uint8_t> QR::finalSequence(Data &bits, uint8_t version, ErrorCorrection ec) {
vector<uint8_t> QR::finalSequence(Data &bits, uint8_t version, QRGen_ErrorCorrection ec) {
size_t offset = 0;
vector<vector<uint8_t>> dataCodewordBlocks;
vector<vector<uint8_t>> ecCodewordBlocks;
Expand Down Expand Up @@ -284,6 +279,21 @@ bool QR::isNumeric(char16_t c) {
}


bool QR::isNumeric(std::u16string_view s) {
return all_of(s.begin(), s.end(), static_cast<bool(*)(char16_t)>(&QR::isNumeric));
}


bool QR::isAlphaNumeric(char16_t c) {
return alphaNumericCharacters.find(c) != alphaNumericCharacters.end();
}


bool QR::isAlphaNumeric(std::u16string_view s) {
return all_of(s.begin(), s.end(), static_cast<bool(*)(char16_t)>(&QR::isAlphaNumeric));
}


uint32_t QR::characterCountBits(uint8_t version, Mode encodeMode) {
assert(1 <= version && version <= 40);
if (!(1 <= version && version <= 40)) { return 0; }
Expand Down Expand Up @@ -311,7 +321,7 @@ uint32_t QR::characterCountBits(uint8_t version, Mode encodeMode) {

QR::EncodeResult QR::encodeNumeric(std::u16string_view data) {
assert(data.size() > 0 && data.size() <= numeric_limits<uint16_t>::max());
assert(all_of(data.begin(), data.end(), &QR::isNumeric));
assert(isNumeric(data));

Data bits;
size_t i;
Expand Down Expand Up @@ -370,7 +380,7 @@ QR::EncodeResult QR::encodeEightbit(std::u16string_view data) {
}


uint8_t QR::minimumVersion(uint32_t numContentBits, ErrorCorrection ec) {
uint8_t QR::minimumVersion(uint32_t numContentBits, QRGen_ErrorCorrection ec) {
const uint8_t ecValue = to_underlying(ec);

for (uint8_t version = 1; version <= 40; ++version) {
Expand All @@ -380,7 +390,7 @@ uint8_t QR::minimumVersion(uint32_t numContentBits, ErrorCorrection ec) {
}


uint8_t QR::minimumVersion(EncodeResult encodeResult, ErrorCorrection ec) {
uint8_t QR::minimumVersion(EncodeResult encodeResult, QRGen_ErrorCorrection ec) {
return minimumVersion(encodeResult.bits.bitCount(), ec);
}

Expand Down
13 changes: 8 additions & 5 deletions libQRGen/src/qr.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class QR
QR() = delete;

static Symbol encode(std::u16string_view data,
QRGen::ErrorCorrection ec = QRGen::ErrorCorrection::M,
QRGen_ErrorCorrection ec = QRGen_EC_M,
uint8_t version = 0,
uint8_t mask = 255);

Expand All @@ -39,21 +39,24 @@ class QR
uint16_t characterCount;
};

static EncodeResult encodeSegment(std::u16string_view data, QRGen::ErrorCorrection ec);
static EncodeResult encodeSegment(std::u16string_view data, QRGen_ErrorCorrection ec);
static EncodeResult encodeContent(std::u16string_view data);
/** Add error correction codewords and put everything into the final sequence order. */
static std::vector<uint8_t> finalSequence(Data &bits, uint8_t version, QRGen::ErrorCorrection ec);
static std::vector<uint8_t> finalSequence(Data &bits, uint8_t version, QRGen_ErrorCorrection ec);

static bool isNumeric(char16_t c);
static bool isNumeric(std::u16string_view s);
static bool isAlphaNumeric(char16_t c);
static bool isAlphaNumeric(std::u16string_view s);

static uint32_t characterCountBits(uint8_t version, Mode encodeMode);

static EncodeResult encodeNumeric(std::u16string_view data);
static EncodeResult encodeAlphanumeric(std::u16string_view data);
static EncodeResult encodeEightbit(std::u16string_view data);

static uint8_t minimumVersion(uint32_t numContentData, QRGen::ErrorCorrection ec);
static uint8_t minimumVersion(EncodeResult encodeResult, QRGen::ErrorCorrection ec);
static uint8_t minimumVersion(uint32_t numContentData, QRGen_ErrorCorrection ec);
static uint8_t minimumVersion(EncodeResult encodeResult, QRGen_ErrorCorrection ec);

static std::string toString(Mode mode);

Expand Down
76 changes: 76 additions & 0 deletions libQRGen/src/qrgen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include <qrgen.h>
#include <cstdio>
#include <codecvt>
#include <locale>
#include <string>
#include "qr.h"

using namespace std;


static QRGen_Symbol *convertSymbol(const Symbol &symbol);
static u16string fromUtf8(const char *data, size_t len);


extern "C" {

QRGen_Symbol *QRGen_encode(const char *data, size_t len) {
Symbol symbol = QR::encode(fromUtf8(data, len));
return convertSymbol(symbol);
}


struct QRGen_Symbol *QRGen_encode_ec(const char *data, size_t len, QRGen_ErrorCorrection ec) {
Symbol symbol = QR::encode(fromUtf8(data, len), ec);
return convertSymbol(symbol);
}


void QRGen_free_symbol(QRGen_Symbol *symbol) {
if (symbol) {
if (symbol->data) {
free(symbol->data);
symbol->data = nullptr;
}
free(symbol);
}
}

} // extern "C"


static QRGen_Symbol *convertSymbol(const Symbol &symbol) {
const int size = symbol.size();

if (size == 0) {
return nullptr;
}

QRGen_Symbol *result = reinterpret_cast<QRGen_Symbol*>(malloc(sizeof(QRGen_Symbol)));
if (result == nullptr) {
perror("malloc");
return nullptr;
}

result->width = size;
result->height = size;
result->data = reinterpret_cast<bool*>(malloc(sizeof(bool) * size * size));
if (result->data == nullptr) {
perror("malloc");
free(result);
return nullptr;
}

// Cannot use memcpy because symbol.pixels() is packed, but result->data is not.
for(size_t i = 0; i < symbol.pixels().size(); ++i) {
result->data[i] = symbol.pixels()[i];
}

return result;
}


static u16string fromUtf8(const char *data, size_t len) {
using CVT = codecvt_utf8_utf16<char16_t>;
return wstring_convert<CVT, char16_t>{}.from_bytes(&data[0], &data[len]);
}
22 changes: 11 additions & 11 deletions libQRGen/src/symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ void Symbol::highlightCodeword(size_t codewordNo, uint32_t highlight) {
}


void Symbol::setData(const std::vector<uint8_t> &data, QRGen::ErrorCorrection ec, uint8_t mask) {
void Symbol::setData(const std::vector<uint8_t> &data, QRGen_ErrorCorrection ec, uint8_t mask) {
unsigned int lowestPenalty = numeric_limits<unsigned int>::max();
uint8_t bestMask = mask;
if (bestMask == 255) {
Expand Down Expand Up @@ -208,7 +208,7 @@ void Symbol::drawFinderPatterns() {
}


void Symbol::drawFormatInformation(uint8_t mask, QRGen::ErrorCorrection ec) {
void Symbol::drawFormatInformation(uint8_t mask, QRGen_ErrorCorrection ec) {
// ISO/IEC 18004:2004: see sections 8.9 and appendix C
// ISO/IEC 18004:2015: see sections 7.9 and appendix C
// EC information goes into bits 14:13
Expand All @@ -220,10 +220,10 @@ void Symbol::drawFormatInformation(uint8_t mask, QRGen::ErrorCorrection ec) {
static const Polynomial generatorPolynomial(0b101'0011'0111u);
uint32_t formatBits;
switch (ec) {
case QRGen::ErrorCorrection::L: formatBits = 0b010'0000'0000'0000u; break;
case QRGen::ErrorCorrection::M: formatBits = 0b000'0000'0000'0000u; break;
case QRGen::ErrorCorrection::Q: formatBits = 0b110'0000'0000'0000u; break;
case QRGen::ErrorCorrection::H: formatBits = 0b100'0000'0000'0000u; break;
case QRGen_EC_L: formatBits = 0b010'0000'0000'0000u; break;
case QRGen_EC_M: formatBits = 0b000'0000'0000'0000u; break;
case QRGen_EC_Q: formatBits = 0b110'0000'0000'0000u; break;
case QRGen_EC_H: formatBits = 0b100'0000'0000'0000u; break;
}

// calculate format information bits
Expand Down Expand Up @@ -533,16 +533,16 @@ Symbol::Position Symbol::startPosition() const {
}


uint_fast16_t Symbol::formatInformation(uint8_t mask, QRGen::ErrorCorrection ec) {
uint_fast16_t Symbol::formatInformation(uint8_t mask, QRGen_ErrorCorrection ec) {
static constexpr uint_fast16_t xorMask{0b10101'00000'10010};
static const Polynomial divisor{0b1'01001'10111};

uint_fast16_t result;
switch (ec) {
case QRGen::ErrorCorrection::L: result = 0b010'0000'0000'0000; break;
case QRGen::ErrorCorrection::M: result = 0b000'0000'0000'0000; break;
case QRGen::ErrorCorrection::Q: result = 0b110'0000'0000'0000; break;
case QRGen::ErrorCorrection::H: result = 0b100'0000'0000'0000; break;
case QRGen_EC_L: result = 0b010'0000'0000'0000; break;
case QRGen_EC_M: result = 0b000'0000'0000'0000; break;
case QRGen_EC_Q: result = 0b110'0000'0000'0000; break;
case QRGen_EC_H: result = 0b100'0000'0000'0000; break;
}

assert(mask < 8);
Expand Down
Loading

0 comments on commit c60bc9b

Please sign in to comment.