<commit_before>#include <cassert>
#include <iostream>
#include <vector>

using namespace std;

void prt(vector<int>& v) {
    for(auto& x: v)
        cout << x  << " ";
    cout << endl;
}

void selection_sort(vector<int>& v) {
    // select the min and put to current position
    for(int i = 0; i < v.size(); i++) {
        int min = i;
        for(int j = i+1; j < v.size(); j++) {
            if(v[j] < v[min]) {
                min = j;
            }
        }
        swap(v[min], v[i]);
    }
}

void insertion_sort(vector<int>& v) {
    // compare to sorted, swap when less than the sorted one
    for(int i = 1; i < v.size(); i++) {
        for(int j = i; j > 0; j--) {
            if(v[j] < v[j-1]) {
                swap(v[j], v[j-1]);
            }
        }
    }
}

int quick_sort_partition(vector<int>& v, int l, int h) {
    // choose l as pivot
    int i = l, j = h, p = v[l];
    while(i < j) {
        // find Vj < p
        while(i < j && v[j] >= p) j--;
        // assign to Vi to put to left, now Vj is empty
        v[i] = v[j];
        // find Vi > p
        while(i < j && v[i] <= p) i++;
        // assign  to Vj to put to right, now Vi is empty
        v[j] = v[i];
    }
    // now i == j, everything is done
    // fix the last empty Vi with p
    v[i] = p;
    return i;
}

void quick_sort_dac(vector<int>& v, int l, int h) {
    if(l >= h) {
        return;
    }
    int m = quick_sort_partition(v, l, h);
    prt(v);
    quick_sort_dac(v, l, m-1);
    quick_sort_dac(v, m+1, h);
}

void quick_sort(vector<int>& v) {
    if(v.empty()) {
        return;
    }
    quick_sort_dac(v, 0, v.size()-1);
}

// when k is 0, means the first
// v is unsorted
// the time complexity is O(N)! better than O(NlogN) and O(NLogK) <- heap
int select_kth_smallest(vector<int>& v, int k) {
    assert(! v.empty());
    int l = 0, h = v.size() - 1;
    while(l <= h) {
        int j = quick_sort_partition(v, l, h);
        if(j == k) { // found!
            return v[k];
        } else if (k < j) {
            h = j-1;
        } else { // k > j
            l = j+1;
        }
    }
    return v[k];
}

int main() {
    vector<int> a = {1,2,4,5,7,2,54,2,712,2,4,6,1};

    vector<int> b = a, c = a, d = a, e = a;

    selection_sort(b);
    prt(b);

    insertion_sort(c);
    prt(c);

    prt(d);
    quick_sort(d);
    prt(d);

    cout << "5th: " << select_kth_smallest(e, 5) << endl;
    prt(e);

    return 0;
}
<commit_msg>add heap sort<commit_after>#include <cassert>
#include <iostream>
#include <vector>

using namespace std;

void prt(vector<int>& v) {
    for(auto& x: v)
        cout << x  << " ";
    cout << endl;
}

void selection_sort(vector<int>& v) {
    // select the min and put to current position
    for(int i = 0; i < v.size(); i++) {
        int min = i;
        for(int j = i+1; j < v.size(); j++) {
            if(v[j] < v[min]) {
                min = j;
            }
        }
        swap(v[min], v[i]);
    }
}

void insertion_sort(vector<int>& v) {
    // compare to sorted, swap when less than the sorted one
    for(int i = 1; i < v.size(); i++) {
        for(int j = i; j > 0; j--) {
            if(v[j] < v[j-1]) {
                swap(v[j], v[j-1]);
            }
        }
    }
}

int quick_sort_partition(vector<int>& v, int l, int h) {
    // choose l as pivot
    int i = l, j = h, p = v[l];
    while(i < j) {
        // find Vj < p
        while(i < j && v[j] >= p) j--;
        // assign to Vi to put to left, now Vj is empty
        v[i] = v[j];
        // find Vi > p
        while(i < j && v[i] <= p) i++;
        // assign  to Vj to put to right, now Vi is empty
        v[j] = v[i];
    }
    // now i == j, everything is done
    // fix the last empty Vi with p
    v[i] = p;
    return i;
}

void quick_sort_dac(vector<int>& v, int l, int h) {
    if(l >= h) {
        return;
    }
    int m = quick_sort_partition(v, l, h);
    prt(v);
    quick_sort_dac(v, l, m-1);
    quick_sort_dac(v, m+1, h);
}

void quick_sort(vector<int>& v) {
    if(v.empty()) {
        return;
    }
    quick_sort_dac(v, 0, v.size()-1);
}

// when k is 0, means the first
// v is unsorted
// the time complexity is O(N)! better than O(NlogN) and O(NLogK) <- heap
int select_kth_smallest(vector<int>& v, int k) {
    assert(! v.empty());
    int l = 0, h = v.size() - 1;
    while(l <= h) {
        int j = quick_sort_partition(v, l, h);
        if(j == k) { // found!
            return v[k];
        } else if (k < j) {
            h = j-1;
        } else { // k > j
            l = j+1;
        }
    }
    return v[k];
}

// max-heap
// k start from 1, k is the node to sink
void heap_sink(vector<int>& v, int k, int N) {
    assert(k <= N);
    // get the one before, we will use index start from 1 to n
    int* A = v.data() - 1;
    int c;
    while(k * 2 <= N) {
        c = k * 2;
        if(c + 1 <= N && A[c + 1] > A[c]) {
            c++;
        }
        if(A[c] > A[k]) {
            swap(A[c], A[k]);
        } else {
            break;
        }
        k = c;
    }
}

void heap_make(vector<int>& v) {
    // get the one before, we will use index start from 1 to n
    int* A = v.data() - 1;
    int N = v.size();
    int k = N / 2;
    while(k >= 1) {
        heap_sink(v, k, N);
        k--;
    }
}

void heap_sort(vector<int>& v) {
    int N = v.size();
    heap_make(v);
    while(--N) {
        // swap max to last
        swap(v[0], v[N]);
        // sink the root
        heap_sink(v, 1, N);
    }
}

int main() {
    vector<int> a = {1,2,4,5,7,2,54,2,712,2,4,6,1};

    vector<int> b = a, c = a, d = a, e = a, f = a;

    cout << "selection sort" << endl;
    selection_sort(b);
    prt(b);

    cout << "insertion sort" << endl;
    insertion_sort(c);
    prt(c);

    cout << "quick sort" << endl;
    prt(d);
    quick_sort(d);
    prt(d);

    cout << "heap sort" << endl;
    heap_sort(f);
    prt(f);

    cout << "select 5th smallest: " << select_kth_smallest(e, 5) << endl;
    prt(e);

    return 0;
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014-2015, Vlad Mesco
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "jakmuse_common.h"

#ifndef VERSION
# error "VERSION is not defined."
#endif

#define COPYRIGHT_YEAR "2014-2015"

#include <cstdio>
#include <cassert>
#include <cstdlib>
#include <cstring>

#include <vector>
#include <string>
#include <algorithm>

size_t g_maxChannelLen = 0;
channels_t g_channels;
generators_t g_generators;

#define PRINT(WHAT, VAR) printf(#WHAT "\n"); \
for(size_t i = 0; i < JAKMUSE_NUMCHANNELS; ++i) {\
    printf("channel %ld:\n", i); \
    for(size_t j = 0; j < g_channels[i].size(); ++j) { \
        printf("%5d", g_channels[i][j].VAR); \
        if(j % 16 == 15 && j != g_channels[i].size() - 1) printf("\n"); \
    } \
    printf("\n"); \
}

int main(int argc, char* argv[])
{
    if(argc > 1 && strcmp(argv[1], "-v") == 0) {
        printf("JakMuse v%s, Copyright %s, Vlad Mesco\n", VERSION, COPYRIGHT_YEAR);
        printf("  available under the terms of the Simplified BSD License\n");
        exit(255);
    }

    extern void init_generators();
    init_generators();

    extern void parse();
    parse();

    extern void compile();
    compile();
    // free up memory
    g_channel_sequences.clear();

    extern void mix();
    mix();
    // free up memory
    std::for_each(&g_channels[0], &g_channels[JAKMUSE_NUMCHANNELS],
            [](pwm_channel_t& chan) {
                chan.clear();
            });

    std::string filename("jakmuse.wav");
    if(argc > 1 && strcmp(argv[1], "-w") == 0) {
        if(argc > 2) {
            filename.assign(argv[2]);
        }

        extern bool wav_write_file(std::string const&, std::vector<short> const&, unsigned);
        extern std::vector<short> g_wav;
        auto hr = wav_write_file(filename, g_wav, JAKMUSE_SAMPLES_PER_SECOND);

        if(hr) {
            printf("Wrote %s\n", filename.c_str());
            exit(0);
        } else {
            printf("Could not write %s\n", filename.c_str());
            exit(hr);
        }
    } else {
        extern void play_music();
        play_music();
    }

    return 0;
}

<commit_msg>slightly improve args parsing<commit_after>/*
Copyright (c) 2014-2015, Vlad Mesco
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "jakmuse_common.h"

#ifndef VERSION
# error "VERSION is not defined."
#endif

#define COPYRIGHT_YEAR "2014-2015"

#include <cstdio>
#include <cassert>
#include <cstdlib>
#include <cstring>

#include <vector>
#include <string>
#include <algorithm>

size_t g_maxChannelLen = 0;
channels_t g_channels;
generators_t g_generators;

#define PRINT(WHAT, VAR) printf(#WHAT "\n"); \
for(size_t i = 0; i < JAKMUSE_NUMCHANNELS; ++i) {\
    printf("channel %ld:\n", i); \
    for(size_t j = 0; j < g_channels[i].size(); ++j) { \
        printf("%5d", g_channels[i][j].VAR); \
        if(j % 16 == 15 && j != g_channels[i].size() - 1) printf("\n"); \
    } \
    printf("\n"); \
}

static void usage(char const* argv0)
{
    printf("Usage: %s [-w file.wav|-v|-h]\n", argv0);
    exit(255);
}

int main(int argc, char* argv[])
{
    bool wav(false);

    if(argc > 1 && (argv[1][0] == '-' || argv[1][0] == '/') && argv[1][1]) {
        switch(argv[1][1]) {
        case 'v':
            printf("JakMuse v%s, Copyright %s, Vlad Mesco\n", VERSION, COPYRIGHT_YEAR);
            printf("  available under the terms of the Simplified BSD License\n");
            exit(255);
            break;
        case 'h':
        case '?':
            usage(argv[0]);
            break;
        case 'w':
            wav = true;
            break;
        default:
            usage(argv[0]);
        }
    } else if(argc > 1) {
        usage(argv[0]);
    }

    extern void init_generators();
    init_generators();

    extern void parse();
    parse();

    extern void compile();
    compile();
    // free up memory
    g_channel_sequences.clear();

    extern void mix();
    mix();
    // free up memory
    std::for_each(&g_channels[0], &g_channels[JAKMUSE_NUMCHANNELS],
            [](pwm_channel_t& chan) {
                chan.clear();
            });

    std::string filename("jakmuse.wav");
    if(wav) {
        if(argc > 2) {
            filename.assign(argv[2]);
        }

        extern bool wav_write_file(std::string const&, std::vector<short> const&, unsigned);
        extern std::vector<short> g_wav;
        auto hr = wav_write_file(filename, g_wav, JAKMUSE_SAMPLES_PER_SECOND);

        if(hr) {
            printf("Wrote %s\n", filename.c_str());
            exit(0);
        } else {
            printf("Could not write %s\n", filename.c_str());
            exit(hr);
        }
    } else {
        extern void play_music();
        play_music();
    }

    return 0;
}

<|endoftext|>


<commit_before>/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "Request.h"
#include "Response.h"

#include "SkCommandLineFlags.h"
#include "SkGraphics.h"

#include "microhttpd.h"

#include "urlhandlers/UrlHandler.h"

#include <errno.h>

#if !defined _WIN32
#include <sys/socket.h>
#include <arpa/inet.h>
#endif

using namespace Response;

DEFINE_int32(port, 8888, "The port to listen on.");
DEFINE_string(address, "127.0.0.1", "The address to bind to.");

class UrlManager {
public:
    UrlManager() {
        // Register handlers
        fHandlers.push_back(new RootHandler);
        fHandlers.push_back(new PostHandler);
        fHandlers.push_back(new ImgHandler);
        fHandlers.push_back(new ClipAlphaHandler);
        fHandlers.push_back(new EnableGPUHandler);
        fHandlers.push_back(new CmdHandler);
        fHandlers.push_back(new InfoHandler);
        fHandlers.push_back(new DownloadHandler);
        fHandlers.push_back(new DataHandler);
        fHandlers.push_back(new BreakHandler);
        fHandlers.push_back(new BatchesHandler);
        fHandlers.push_back(new BatchBoundsHandler);
        fHandlers.push_back(new ColorModeHandler);
        fHandlers.push_back(new SRGBModeHandler);
        fHandlers.push_back(new QuitHandler);
    }

    ~UrlManager() {
        for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
    }

    // This is clearly not efficient for a large number of urls and handlers
    int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
               const char* upload_data, size_t* upload_data_size) const {
        for (int i = 0; i < fHandlers.count(); i++) {
            if (fHandlers[i]->canHandle(method, url)) {
                return fHandlers[i]->handle(request, connection, url, method, upload_data,
                                            upload_data_size);
            }
        }
        return MHD_NO;
    }

private:
    SkTArray<UrlHandler*> fHandlers;
};

const UrlManager kUrlManager;

int answer_to_connection(void* cls, struct MHD_Connection* connection,
                         const char* url, const char* method, const char* version,
                         const char* upload_data, size_t* upload_data_size,
                         void** con_cls) {
    SkDebugf("New %s request for %s using version %s\n", method, url, version);

    Request* request = reinterpret_cast<Request*>(cls);
    int result = kUrlManager.invoke(request, connection, url, method, upload_data,
                                    upload_data_size);
    if (MHD_NO == result) {
        fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
    }
    return result;
}

int skiaserve_main() {
    SkGraphics::Init();
    Request request(SkString("/data")); // This simple server has one request

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_port = htons(FLAGS_port);
    int result = inet_pton(AF_INET, FLAGS_address[0], &address.sin_addr);
    if (result != 1) {
        printf("inet_pton for %s:%d failed with return %d %s\n",
                FLAGS_address[0], FLAGS_port, result, strerror(errno));
        return 1;
    }

    printf("Visit http://%s:%d in your browser.\n", FLAGS_address[0], FLAGS_port);

    struct MHD_Daemon* daemon;
    daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY
#ifdef SK_DEBUG
                              | MHD_USE_DEBUG
#endif
                              , FLAGS_port, nullptr, nullptr,
                              &answer_to_connection, &request,
                              MHD_OPTION_SOCK_ADDR, &address,
                              MHD_OPTION_END);
    if (NULL == daemon) {
        SkDebugf("Could not initialize daemon\n");
        return 1;
    }

    getchar();
    MHD_stop_daemon(daemon);
    return 0;
}

#if !defined SK_BUILD_FOR_IOS
int main(int argc, char** argv) {
    SkCommandLineFlags::Parse(argc, argv);
    return skiaserve_main();
}
#endif
<commit_msg>skiaserve: Add a flag to control hosted mode, i.e. when running on debugger.skia.org.<commit_after>/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "Request.h"
#include "Response.h"

#include "SkCommandLineFlags.h"
#include "SkGraphics.h"

#include "microhttpd.h"

#include "urlhandlers/UrlHandler.h"

#include <errno.h>

#if !defined _WIN32
#include <sys/socket.h>
#include <arpa/inet.h>
#endif

using namespace Response;

DEFINE_int32(port, 8888, "The port to listen on.");
DEFINE_string(address, "127.0.0.1", "The address to bind to.");
DEFINE_bool(hosted, false, "Running in hosted mode on debugger.skia.org.");

class UrlManager {
public:
    UrlManager() {
        // Register handlers
        fHandlers.push_back(new RootHandler);
        fHandlers.push_back(new PostHandler);
        fHandlers.push_back(new ImgHandler);
        fHandlers.push_back(new ClipAlphaHandler);
        fHandlers.push_back(new EnableGPUHandler);
        fHandlers.push_back(new CmdHandler);
        fHandlers.push_back(new InfoHandler);
        fHandlers.push_back(new DownloadHandler);
        fHandlers.push_back(new DataHandler);
        fHandlers.push_back(new BreakHandler);
        fHandlers.push_back(new BatchesHandler);
        fHandlers.push_back(new BatchBoundsHandler);
        fHandlers.push_back(new ColorModeHandler);
        fHandlers.push_back(new SRGBModeHandler);
        fHandlers.push_back(new QuitHandler);
    }

    ~UrlManager() {
        for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
    }

    // This is clearly not efficient for a large number of urls and handlers
    int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
               const char* upload_data, size_t* upload_data_size) const {
        for (int i = 0; i < fHandlers.count(); i++) {
            if (fHandlers[i]->canHandle(method, url)) {
                return fHandlers[i]->handle(request, connection, url, method, upload_data,
                                            upload_data_size);
            }
        }
        return MHD_NO;
    }

private:
    SkTArray<UrlHandler*> fHandlers;
};

const UrlManager kUrlManager;

int answer_to_connection(void* cls, struct MHD_Connection* connection,
                         const char* url, const char* method, const char* version,
                         const char* upload_data, size_t* upload_data_size,
                         void** con_cls) {
    SkDebugf("New %s request for %s using version %s\n", method, url, version);

    Request* request = reinterpret_cast<Request*>(cls);
    int result = kUrlManager.invoke(request, connection, url, method, upload_data,
                                    upload_data_size);
    if (MHD_NO == result) {
        fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
    }
    return result;
}

int skiaserve_main() {
    SkGraphics::Init();
    Request request(SkString("/data")); // This simple server has one request

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_port = htons(FLAGS_port);
    int result = inet_pton(AF_INET, FLAGS_address[0], &address.sin_addr);
    if (result != 1) {
        printf("inet_pton for %s:%d failed with return %d %s\n",
                FLAGS_address[0], FLAGS_port, result, strerror(errno));
        return 1;
    }

    printf("Visit http://%s:%d in your browser.\n", FLAGS_address[0], FLAGS_port);

    struct MHD_Daemon* daemon;
    daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY
#ifdef SK_DEBUG
                              | MHD_USE_DEBUG
#endif
                              , FLAGS_port, nullptr, nullptr,
                              &answer_to_connection, &request,
                              MHD_OPTION_SOCK_ADDR, &address,
                              MHD_OPTION_END);
    if (NULL == daemon) {
        SkDebugf("Could not initialize daemon\n");
        return 1;
    }

    if (FLAGS_hosted) {
        while (1) {
            SkDebugf("loop\n");
            #if defined(SK_BUILD_FOR_WIN)
                Sleep(60 * 1000);
            #else
                sleep(60);
            #endif
        }
    } else {
        getchar();
    }
    MHD_stop_daemon(daemon);
    return 0;
}

#if !defined SK_BUILD_FOR_IOS
int main(int argc, char** argv) {
    SkCommandLineFlags::Parse(argc, argv);
    return skiaserve_main();
}
#endif
<|endoftext|>


<commit_before>/*************************************************************************
 *   Copyright (C) 2011-2017 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Plateform.                                     *
 *                                                                       *
 *   Plateform is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   Plateform is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with Plateform.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#ifndef PLA_CRYPTO_H
#define PLA_CRYPTO_H

#include "pla/include.hpp"
#include "pla/binarystring.hpp"
#include "pla/string.hpp"
#include "pla/stream.hpp"

#include <nettle/sha1.h>
#include <nettle/sha2.h>
#include <nettle/sha3.h>
#include <nettle/aes.h>
#include <nettle/ctr.h>
#include <nettle/gcm.h>
#include <nettle/rsa.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

namespace pla
{

// Hash function interface
class Hash
{
public:
	Hash(void) {}
	virtual ~Hash(void) {}

	// Implementation
	virtual size_t length(void) const = 0;
	virtual void init(void) = 0;
	virtual void process(const char *data, size_t size) = 0;
	virtual void process(const BinaryString &str) = 0;
	virtual void finalize(char *digest) = 0;	// digest size must be length()
	virtual void finalize(BinaryString &digest) = 0;

	// Direct hash function call
	void    compute(const char *data, size_t size, char *digest);
	void    compute(const char *data, size_t size, BinaryString &digest);
	int64_t compute(Stream &stream, char *digest);
	int64_t compute(Stream &stream, BinaryString &digest);
	int64_t compute(Stream &stream, int64_t max, char *digest);
	int64_t compute(Stream &stream, int64_t max, BinaryString &digest);
	BinaryString compute(const BinaryString &str);

	// MAC
	virtual void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest) = 0;
	virtual void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest) = 0;
};

// SHA1 hash function implementation
class Sha1 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// HMAC-SHA1
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

	// PBKDF2-HMAC-SHA1
	void pbkdf2(const char *secret, size_t len, const char *salt, size_t salt_len, char *key, size_t key_len, unsigned iterations);
	void pbkdf2(const BinaryString &secret, const BinaryString &salt, BinaryString &key, size_t key_len, unsigned iterations);

private:
	struct sha1_ctx mCtx;
};

// SHA256 hash function implementation
class Sha256 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// HMAC-SHA256
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

	// PBKDF2-HMAC-SHA256
	void pbkdf2(const char *secret, size_t len, const char *salt, size_t salt_len, char *key, size_t key_len, unsigned iterations);
	void pbkdf2(const BinaryString &secret, const BinaryString &salt, BinaryString &key, size_t key_len, unsigned iterations);

private:
	struct sha256_ctx mCtx;
};

// SHA512 hash function implementation
class Sha512 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// HMAC-SHA512
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

	// PBKDF2-HMAC-SHA512
	void pbkdf2(const char *secret, size_t len, const char *salt, size_t salt_len, char *key, size_t key_len, unsigned iterations);
	void pbkdf2(const BinaryString &secret, const BinaryString &salt, BinaryString &key, size_t key_len, unsigned iterations);

private:
	struct sha512_ctx mCtx;
};

// SHA3-256 hash function implementation
class Sha3_256 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// "Unlike SHA-1 and SHA-2, Keccak does not have the length-extension weakness,
	// hence does not need the HMAC nested construction. Instead, MAC computation
	// can be performed by simply prepending the message with the key."
	// This is exactly what this function does. It's not standard!
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

	// TODO: SHAKE and KMAC implementation

private:
	struct sha3_256_ctx mCtx;
};

// SHA3-512 hash function implementation
class Sha3_512 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// See Sha3_256::mac()
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

private:
	struct sha3_512_ctx mCtx;
};

typedef Sha3_256 Sha3;	// SHA3 defaults to SHA3-256

class Cipher : public Stream
{
public:
	Cipher(Stream *stream, bool mustDelete = false);
	virtual ~Cipher(void);

	virtual void setEncryptionKey(const BinaryString &key) = 0;
	virtual void setDecryptionKey(const BinaryString &key) = 0;
	virtual void setInitializationVector(const BinaryString &iv) = 0;
	virtual bool getAuthenticationTag(BinaryString &tag) { return false; }

	// Stream
	size_t readData(char *buffer, size_t size);
	void writeData(const char *data, size_t size);
	void seekRead(int64_t position);	// Unavailable
	void seekWrite(int64_t position);	// Unavailable
	int64_t tellRead(void) const;
	int64_t tellWrite(void) const;
	void close(void);

protected:
	virtual size_t blockSize(void) const = 0;
	virtual void encryptBlock(char *block, size_t size) = 0;
	virtual void decryptBlock(char *block, size_t size) = 0;

private:
	Stream *mStream;
	bool mMustDelete;

	char *mReadBlock, *mWriteBlock;
	size_t mReadBlockSize, mWriteBlockSize;
	uint64_t mReadPosition, mWritePosition;
};

// AES-CTR implementation
class AesCtr : public Cipher
{
public:
	AesCtr(Stream *stream, bool mustDelete = false);
	~AesCtr(void);

	void setEncryptionKey(const BinaryString &key);
	void setDecryptionKey(const BinaryString &key);
	void setInitializationVector(const BinaryString &iv);

protected:
	size_t blockSize(void) const;
	void encryptBlock(char *block, size_t size);
	void decryptBlock(char *block, size_t size);

private:
	struct CTR_CTX(struct aes_ctx, AES_BLOCK_SIZE) mCtx;
};

// AES-GCM implementation
class AesGcm : public Cipher
{
public:
	AesGcm(Stream *stream, bool mustDelete = false);
	~AesGcm(void);

	void setEncryptionKey(const BinaryString &key);
	void setDecryptionKey(const BinaryString &key);
	void setInitializationVector(const BinaryString &iv);
	bool getAuthenticationTag(BinaryString &tag);

protected:
	size_t blockSize(void) const;
	void encryptBlock(char *block, size_t size);
	void decryptBlock(char *block, size_t size);

private:
	struct gcm_aes_ctx mCtx;
};

class Rsa
{
public:
	class PublicKey : public Serializable
	{
	public:
		PublicKey(void);
		PublicKey(const PublicKey &key);
		PublicKey(gnutls_x509_crt_t crt);
		~PublicKey(void);
		PublicKey &operator=(const PublicKey &key);

		bool operator==(const PublicKey &key) const;

		bool isNull(void) const;
		void clear(void);
		const BinaryString &digest(Hash &hash) const;
		const BinaryString &digest(void) const;
		const BinaryString &fingerprint(void) const { return digest(); }
		bool verify(const BinaryString &digest, const BinaryString &signature) const;

		// Serializable
		void serialize(Serializer &s) const;
		bool deserialize(Serializer &s);
		void serialize(Stream &s) const;
		bool deserialize(Stream &s);
		bool isInlineSerializable(void) const;

	private:
		void derEncode(Stream &out) const;	// used only to compute digest/fingerprint

		struct rsa_public_key mKey;
		mutable BinaryString mDigest;
		friend class Rsa;
	};

	class PrivateKey : public Serializable
	{
	public:
		PrivateKey(void);
		PrivateKey(const PrivateKey &key);
		~PrivateKey(void);
		PrivateKey &operator=(const PrivateKey &key);

		bool isNull(void) const;
		void clear(void);
		void sign(const BinaryString &digest, BinaryString &signature) const;

		// Serializable
		void serialize(Serializer &s) const;
		bool deserialize(Serializer &s);
		void serialize(Stream &s) const;
		bool deserialize(Stream &s);
		bool isInlineSerializable(void) const;

	private:
		struct rsa_private_key mKey;
		friend class Rsa;
	};

	Rsa(unsigned bits = 4096);
	~Rsa(void);

	void generate(PublicKey &pub, PrivateKey &priv);

	static void CreateCertificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key, const PublicKey &pub, const PrivateKey &priv, const String &name);
	static void SignCertificate(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, gnutls_x509_privkey_t issuerKey);

private:
	unsigned mBits;
};

// Argon2 key derivation function for password hashing
class Argon2
{
public:
	static unsigned DefaultTimeCost;
	static unsigned DefaultMemoryCost;
	static unsigned DefaultParallelism;

	Argon2(void);
	Argon2(unsigned tcost, unsigned mcost, unsigned parallelism);
	~Argon2(void);

	void compute(const char *secret, size_t len, const char *salt, size_t salt_len, char *key, size_t key_len);
	void compute(const BinaryString &secret, const BinaryString &salt, BinaryString &key, size_t key_len);

private:
	unsigned mTimeCost;
	unsigned mMemoryCost;
	unsigned mParallelism;
};

// Add-on functions for custom mpz import/export
size_t mpz_size_binary(const mpz_t n);
void mpz_import_binary(mpz_t n, const BinaryString &bs);
void mpz_export_binary(const mpz_t n, BinaryString &bs);
void mpz_import_string(mpz_t n, const String &str);
void mpz_export_string(const mpz_t n, String &str);

}

#endif
<commit_msg>RSA public key digest defaults to SHA256<commit_after>/*************************************************************************
 *   Copyright (C) 2011-2017 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Plateform.                                     *
 *                                                                       *
 *   Plateform is free software: you can redistribute it and/or modify   *
 *   it under the terms of the GNU Affero General Public License as      *
 *   published by the Free Software Foundation, either version 3 of      *
 *   the License, or (at your option) any later version.                 *
 *                                                                       *
 *   Plateform is distributed in the hope that it will be useful, but    *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
 *   GNU Affero General Public License for more details.                 *
 *                                                                       *
 *   You should have received a copy of the GNU Affero General Public    *
 *   License along with Plateform.                                       *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#ifndef PLA_CRYPTO_H
#define PLA_CRYPTO_H

#include "pla/include.hpp"
#include "pla/binarystring.hpp"
#include "pla/string.hpp"
#include "pla/stream.hpp"

#include <nettle/sha1.h>
#include <nettle/sha2.h>
#include <nettle/sha3.h>
#include <nettle/aes.h>
#include <nettle/ctr.h>
#include <nettle/gcm.h>
#include <nettle/rsa.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

namespace pla
{

// Hash function interface
class Hash
{
public:
	Hash(void) {}
	virtual ~Hash(void) {}

	// Implementation
	virtual size_t length(void) const = 0;
	virtual void init(void) = 0;
	virtual void process(const char *data, size_t size) = 0;
	virtual void process(const BinaryString &str) = 0;
	virtual void finalize(char *digest) = 0;	// digest size must be length()
	virtual void finalize(BinaryString &digest) = 0;

	// Direct hash function call
	void    compute(const char *data, size_t size, char *digest);
	void    compute(const char *data, size_t size, BinaryString &digest);
	int64_t compute(Stream &stream, char *digest);
	int64_t compute(Stream &stream, BinaryString &digest);
	int64_t compute(Stream &stream, int64_t max, char *digest);
	int64_t compute(Stream &stream, int64_t max, BinaryString &digest);
	BinaryString compute(const BinaryString &str);

	// MAC
	virtual void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest) = 0;
	virtual void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest) = 0;
};

// SHA1 hash function implementation
class Sha1 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// HMAC-SHA1
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

	// PBKDF2-HMAC-SHA1
	void pbkdf2(const char *secret, size_t len, const char *salt, size_t salt_len, char *key, size_t key_len, unsigned iterations);
	void pbkdf2(const BinaryString &secret, const BinaryString &salt, BinaryString &key, size_t key_len, unsigned iterations);

private:
	struct sha1_ctx mCtx;
};

// SHA256 hash function implementation
class Sha256 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// HMAC-SHA256
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

	// PBKDF2-HMAC-SHA256
	void pbkdf2(const char *secret, size_t len, const char *salt, size_t salt_len, char *key, size_t key_len, unsigned iterations);
	void pbkdf2(const BinaryString &secret, const BinaryString &salt, BinaryString &key, size_t key_len, unsigned iterations);

private:
	struct sha256_ctx mCtx;
};

// SHA512 hash function implementation
class Sha512 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// HMAC-SHA512
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

	// PBKDF2-HMAC-SHA512
	void pbkdf2(const char *secret, size_t len, const char *salt, size_t salt_len, char *key, size_t key_len, unsigned iterations);
	void pbkdf2(const BinaryString &secret, const BinaryString &salt, BinaryString &key, size_t key_len, unsigned iterations);

private:
	struct sha512_ctx mCtx;
};

// SHA3-256 hash function implementation
class Sha3_256 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// "Unlike SHA-1 and SHA-2, Keccak does not have the length-extension weakness,
	// hence does not need the HMAC nested construction. Instead, MAC computation
	// can be performed by simply prepending the message with the key."
	// This is exactly what this function does. It's not standard!
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

	// TODO: SHAKE and KMAC implementation

private:
	struct sha3_256_ctx mCtx;
};

// SHA3-512 hash function implementation
class Sha3_512 : public Hash
{
public:
	size_t length(void) const;
	void init(void);
	void process(const char *data, size_t size);
	void process(const BinaryString &str);
	void finalize(char *digest);
	void finalize(BinaryString &digest);

	// See Sha3_256::mac()
	void mac(const char *message, size_t len, const char *key, size_t key_len, char *digest);
	void mac(const BinaryString &message, const BinaryString &key, BinaryString &digest);

private:
	struct sha3_512_ctx mCtx;
};

typedef Sha3_256 Sha3;	// SHA3 defaults to SHA3-256

class Cipher : public Stream
{
public:
	Cipher(Stream *stream, bool mustDelete = false);
	virtual ~Cipher(void);

	virtual void setEncryptionKey(const BinaryString &key) = 0;
	virtual void setDecryptionKey(const BinaryString &key) = 0;
	virtual void setInitializationVector(const BinaryString &iv) = 0;
	virtual bool getAuthenticationTag(BinaryString &tag) { return false; }

	// Stream
	size_t readData(char *buffer, size_t size);
	void writeData(const char *data, size_t size);
	void seekRead(int64_t position);	// Unavailable
	void seekWrite(int64_t position);	// Unavailable
	int64_t tellRead(void) const;
	int64_t tellWrite(void) const;
	void close(void);

protected:
	virtual size_t blockSize(void) const = 0;
	virtual void encryptBlock(char *block, size_t size) = 0;
	virtual void decryptBlock(char *block, size_t size) = 0;

private:
	Stream *mStream;
	bool mMustDelete;

	char *mReadBlock, *mWriteBlock;
	size_t mReadBlockSize, mWriteBlockSize;
	uint64_t mReadPosition, mWritePosition;
};

// AES-CTR implementation
class AesCtr : public Cipher
{
public:
	AesCtr(Stream *stream, bool mustDelete = false);
	~AesCtr(void);

	void setEncryptionKey(const BinaryString &key);
	void setDecryptionKey(const BinaryString &key);
	void setInitializationVector(const BinaryString &iv);

protected:
	size_t blockSize(void) const;
	void encryptBlock(char *block, size_t size);
	void decryptBlock(char *block, size_t size);

private:
	struct CTR_CTX(struct aes_ctx, AES_BLOCK_SIZE) mCtx;
};

// AES-GCM implementation
class AesGcm : public Cipher
{
public:
	AesGcm(Stream *stream, bool mustDelete = false);
	~AesGcm(void);

	void setEncryptionKey(const BinaryString &key);
	void setDecryptionKey(const BinaryString &key);
	void setInitializationVector(const BinaryString &iv);
	bool getAuthenticationTag(BinaryString &tag);

protected:
	size_t blockSize(void) const;
	void encryptBlock(char *block, size_t size);
	void decryptBlock(char *block, size_t size);

private:
	struct gcm_aes_ctx mCtx;
};

class Rsa
{
public:
	class PublicKey : public Serializable
	{
	public:
		PublicKey(void);
		PublicKey(const PublicKey &key);
		PublicKey(gnutls_x509_crt_t crt);
		~PublicKey(void);
		PublicKey &operator=(const PublicKey &key);

		bool operator==(const PublicKey &key) const;

		bool isNull(void) const;
		void clear(void);
		const BinaryString &digest(Hash &hash) const;
		const BinaryString &digest(void) const;			// Defaults to SHA256
		const BinaryString &fingerprint(void) const { return digest(); }
		bool verify(const BinaryString &digest, const BinaryString &signature) const;

		// Serializable
		void serialize(Serializer &s) const;
		bool deserialize(Serializer &s);
		void serialize(Stream &s) const;
		bool deserialize(Stream &s);
		bool isInlineSerializable(void) const;

	private:
		void derEncode(Stream &out) const;	// used only to compute digest/fingerprint

		struct rsa_public_key mKey;
		mutable BinaryString mDigest;
		friend class Rsa;
	};

	class PrivateKey : public Serializable
	{
	public:
		PrivateKey(void);
		PrivateKey(const PrivateKey &key);
		~PrivateKey(void);
		PrivateKey &operator=(const PrivateKey &key);

		bool isNull(void) const;
		void clear(void);
		void sign(const BinaryString &digest, BinaryString &signature) const;

		// Serializable
		void serialize(Serializer &s) const;
		bool deserialize(Serializer &s);
		void serialize(Stream &s) const;
		bool deserialize(Stream &s);
		bool isInlineSerializable(void) const;

	private:
		struct rsa_private_key mKey;
		friend class Rsa;
	};

	Rsa(unsigned bits = 4096);
	~Rsa(void);

	void generate(PublicKey &pub, PrivateKey &priv);

	static void CreateCertificate(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key, const PublicKey &pub, const PrivateKey &priv, const String &name);
	static void SignCertificate(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, gnutls_x509_privkey_t issuerKey);

private:
	unsigned mBits;
};

// Argon2 key derivation function for password hashing
class Argon2
{
public:
	static unsigned DefaultTimeCost;
	static unsigned DefaultMemoryCost;
	static unsigned DefaultParallelism;

	Argon2(void);
	Argon2(unsigned tcost, unsigned mcost, unsigned parallelism);
	~Argon2(void);

	void compute(const char *secret, size_t len, const char *salt, size_t salt_len, char *key, size_t key_len);
	void compute(const BinaryString &secret, const BinaryString &salt, BinaryString &key, size_t key_len);

private:
	unsigned mTimeCost;
	unsigned mMemoryCost;
	unsigned mParallelism;
};

// Add-on functions for custom mpz import/export
size_t mpz_size_binary(const mpz_t n);
void mpz_import_binary(mpz_t n, const BinaryString &bs);
void mpz_export_binary(const mpz_t n, BinaryString &bs);
void mpz_import_string(mpz_t n, const String &str);
void mpz_export_string(const mpz_t n, String &str);

}

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2012-2014 Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdarg>
#include <string>

#include "configreader.h"
#include "jit.h"
#include "logprintf.h"
#include "plugin.h"

#if JIT_ASMJIT
  #include "amxjit/compiler-asmjit.h"
#endif
#if JIT_LLVM
  #include "amxjit/compiler-llvm.h"
#endif
#include "amxjit/disasm.h"
#include "amxjit/logger.h"

#define logprintf Use_Printf_isntead_of_logprintf

namespace {

void Printf(const char *format, ...) {
  std::va_list va;
  va_start(va, format);

  std::string new_format;
  new_format.append("[jit] ");
  new_format.append(format);

  vlogprintf(new_format.c_str(), va);
  va_end(va);
}

class ErrorHandler: public amxjit::CompileErrorHandler {
 public:
  virtual void Execute(const amxjit::Instruction &instr) {
    Printf("Invalid or unsupported instruction at address %08x:",
           instr.address());
    Printf("  => %s", instr.ToString().c_str());
  }
};

cell OnJITCompile(AMX *amx) {
  int index;
  if (amx_FindPublic(amx, "OnJITCompile", &index) == AMX_ERR_NONE) {
    cell retval;
    amx_Exec(amx, &retval, index);
    return retval;
  }
  return 1;
}

amxjit::CompileOutput *Compile(AMX *amx) {
  if (!OnJITCompile(amx)) {
    Printf("Compilation was disabled");
    return 0;
  }

  amxjit::Logger *logger = 0;
  amxjit::Compiler *compiler = 0;
  amxjit::CompileOutput *output = 0;

  ConfigReader server_cfg("server.cfg");

  bool jit_log = false;
  server_cfg.GetOption("jit_log", jit_log);

  if (jit_log) {
    logger = new amxjit::FileLogger("plugins/jit.log");
  }

  std::string backend = "asmjit";
  server_cfg.GetOption("jit_backend", backend);

  #if JIT_ASMJIT
    if (backend == "asmjit") {
      compiler = new amxjit::CompilerAsmjit;
    }
  #endif
  #if JIT_LLVM
    if (backend == "llvm") {
      compiler = new amxjit::CompilerLLVM;
    }
  #endif

  if (compiler != 0) {
    ErrorHandler error_handler;
    compiler->SetLogger(logger);
    compiler->SetErrorHandler(&error_handler);
    output = compiler->Compile(amx);
  } else {
    Printf("Unrecognized backend '%s'", backend.c_str());
  }

  delete compiler;
  delete logger;

  return output;
}

} // anonymous namespace

JIT::JIT(AMX *amx):
  AMXService<JIT>(amx),
  state_(INIT)
{
}

JIT::~JIT() {
  if (state_ == COMPILE_SUCCEDED) {
    assert(code_ != 0);
    code_->Delete();
  }
}

int JIT::Exec(cell *retval, int index) {
  switch (state_) {
    case INIT:
      state_ = COMPILE;
      if ((code_ = Compile(amx())) != 0) {
        state_ = COMPILE_SUCCEDED;
      } else {
        state_ = COMPILE_FAILED;
        return AMX_ERR_INIT_JIT;
      }
    case COMPILE_SUCCEDED: {
      amxjit::EntryPoint entry_point = code_->GetEntryPoint();
      return entry_point(index, retval);
    }
    case COMPILE:
    case COMPILE_FAILED:
      return AMX_ERR_INIT_JIT;
    default:
      assert(0 && "Invalid JIT state");
      return AMX_ERR_NONE;
  }
}
<commit_msg>Fix assert() failure in amx_Callback()<commit_after>// Copyright (c) 2012-2014 Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdarg>
#include <string>

#include "configreader.h"
#include "jit.h"
#include "logprintf.h"
#include "plugin.h"

#if JIT_ASMJIT
  #include "amxjit/compiler-asmjit.h"
#endif
#if JIT_LLVM
  #include "amxjit/compiler-llvm.h"
#endif
#include "amxjit/disasm.h"
#include "amxjit/logger.h"

#define logprintf Use_Printf_isntead_of_logprintf

namespace {

void Printf(const char *format, ...) {
  std::va_list va;
  va_start(va, format);

  std::string new_format;
  new_format.append("[jit] ");
  new_format.append(format);

  vlogprintf(new_format.c_str(), va);
  va_end(va);
}

class ErrorHandler: public amxjit::CompileErrorHandler {
 public:
  virtual void Execute(const amxjit::Instruction &instr) {
    Printf("Invalid or unsupported instruction at address %08x:",
           instr.address());
    Printf("  => %s", instr.ToString().c_str());
  }
};

cell OnJITCompile(AMX *amx) {
  int index;
  if (amx_FindPublic(amx, "OnJITCompile", &index) == AMX_ERR_NONE) {
    cell retval;
    amx_Exec(amx, &retval, index);
    return retval;
  }
  return 1;
}

amxjit::CompileOutput *Compile(AMX *amx) {
  if (!OnJITCompile(amx)) {
    Printf("Compilation was disabled");
    return 0;
  }

  amxjit::Logger *logger = 0;
  amxjit::Compiler *compiler = 0;
  amxjit::CompileOutput *output = 0;

  ConfigReader server_cfg("server.cfg");

  bool jit_log = false;
  server_cfg.GetOption("jit_log", jit_log);

  if (jit_log) {
    logger = new amxjit::FileLogger("plugins/jit.log");
  }

  std::string backend = "asmjit";
  server_cfg.GetOption("jit_backend", backend);

  #if JIT_ASMJIT
    if (backend == "asmjit") {
      compiler = new amxjit::CompilerAsmjit;
    }
  #endif
  #if JIT_LLVM
    if (backend == "llvm") {
      compiler = new amxjit::CompilerLLVM;
    }
  #endif

  if (compiler != 0) {
    ErrorHandler error_handler;
    compiler->SetLogger(logger);
    compiler->SetErrorHandler(&error_handler);
    output = compiler->Compile(amx);
  } else {
    Printf("Unrecognized backend '%s'", backend.c_str());
  }

  delete compiler;
  delete logger;

  return output;
}

} // anonymous namespace

JIT::JIT(AMX *amx):
  AMXService<JIT>(amx),
  state_(INIT)
{
  amx->sysreq_d = 0;
}

JIT::~JIT() {
  if (state_ == COMPILE_SUCCEDED) {
    assert(code_ != 0);
    code_->Delete();
  }
}

int JIT::Exec(cell *retval, int index) {
  switch (state_) {
    case INIT:
      state_ = COMPILE;
      if ((code_ = Compile(amx())) != 0) {
        state_ = COMPILE_SUCCEDED;
      } else {
        state_ = COMPILE_FAILED;
        return AMX_ERR_INIT_JIT;
      }
    case COMPILE_SUCCEDED: {
      amxjit::EntryPoint entry_point = code_->GetEntryPoint();
      return entry_point(index, retval);
    }
    case COMPILE:
    case COMPILE_FAILED:
      return AMX_ERR_INIT_JIT;
    default:
      assert(0 && "Invalid JIT state");
      return AMX_ERR_NONE;
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Oleg Morozenkov
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "tgbot/Api.h"

#include "tgbot/TgTypeParser.h"
#include "tgbot/TgException.h"
#include "tgbot/net/HttpClient.h"

using namespace std;
using namespace boost::property_tree;

namespace TgBot {

Api::Api(const string& token) : _token(token) {
}

User::Ptr Api::getMe() const {
	return TgTypeParser::getInstance().parseJsonAndGetUser(sendRequest("getMe"));
}

Message::Ptr Api::sendMessage(int32_t chatId, const string& text, bool disableWebPagePreview, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("text", text));
	if (disableWebPagePreview) {
		args.push_back(HttpReqArg("disable_web_page_preview", disableWebPagePreview));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendMessage", args));
}

Message::Ptr Api::forwardMessage(int32_t chatId, int32_t fromChatId, int32_t messageId) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("from_chat_id", fromChatId));
	args.push_back(HttpReqArg("message_id", messageId));
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("forwardMessage", args));
}

Message::Ptr Api::sendPhoto(int32_t chatId, const InputFile::Ptr& photo, const string& caption, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("photo", photo->data, true, photo->mimeType, photo->fileName));
	if (!caption.empty()) {
		args.push_back(HttpReqArg("caption", caption));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendPhoto", args));
}

Message::Ptr Api::sendPhoto(int32_t chatId, const string& photoId, const string& caption, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("photo", photoId));
	if (!caption.empty()) {
		args.push_back(HttpReqArg("caption", caption));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendPhoto", args));
}

Message::Ptr Api::sendAudio(int32_t chatId, const InputFile::Ptr& audio, int32_t duration, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("audio", audio->data, true, audio->mimeType, audio->fileName));
	if (duration) {
		args.push_back(HttpReqArg("duration", duration));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendAudio", args));
}

Message::Ptr Api::sendAudio(int32_t chatId, const string& audioId, int32_t duration, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("audio", audioId));
	if (duration) {
		args.push_back(HttpReqArg("duration", duration));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendAudio", args));
}

Message::Ptr Api::sendDocument(int32_t chatId, const InputFile::Ptr& document, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("document", document->data, true, document->mimeType, document->fileName));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendDocument", args));
}

Message::Ptr Api::sendDocument(int32_t chatId, const string& document, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("document", document));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendDocument", args));
}

Message::Ptr Api::sendSticker(int32_t chatId, const InputFile::Ptr& sticker, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("sticker", sticker->data, true, sticker->mimeType, sticker->fileName));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendSticker", args));
}

Message::Ptr Api::sendSticker(int32_t chatId, const string& stickerId, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("sticker", stickerId));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendSticker", args));
}

Message::Ptr Api::sendVideo(int32_t chatId, const InputFile::Ptr& video, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("video", video->data, true, video->mimeType, video->fileName));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendVideo", args));
}

Message::Ptr Api::sendVideo(int32_t chatId, const string& videoId, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("video", videoId));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendVideo", args));
}

Message::Ptr Api::sendLocation(int32_t chatId, float latitude, float longitude, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("latitude", latitude));
	args.push_back(HttpReqArg("longitude", longitude));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendLocation", args));
}

void Api::sendChatAction(int32_t chatId, const string& action) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("action", action));
	sendRequest("sendChatAction", args);
}

UserProfilePhotos::Ptr Api::getUserProfilePhotos(int32_t userId, int32_t offset, int32_t limit) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("user_id", userId));
	if (offset) {
		args.push_back(HttpReqArg("offset", offset));
	}
	limit = max(1, min(100, limit));
	args.push_back(HttpReqArg("limit", limit));
	return TgTypeParser::getInstance().parseJsonAndGetUserProfilePhotos(sendRequest("getUserProfilePhotos", args));
}

vector<Update::Ptr> Api::getUpdates(int32_t offset, int32_t limit, int32_t timeout) const {
	vector<HttpReqArg> args;
	if (offset) {
		args.push_back(HttpReqArg("offset", offset));
	}
	limit = max(1, min(100, limit));
	args.push_back(HttpReqArg("limit", limit));
	if (timeout) {
		args.push_back(HttpReqArg("timeout", timeout));
	}
	return TgTypeParser::getInstance().parseJsonAndGetArray<Update>(&TgTypeParser::parseJsonAndGetUpdate, sendRequest("getUpdates", args), "result");
}

void Api::setWebhook(const string& url) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("url", url));
	sendRequest("setWebhook", args);
}

ptree Api::sendRequest(const string& method, const vector<HttpReqArg>& args) const {
	string url = "https://api.telegram.org/bot";
	url += _token;
	url += "/";
	url += method;
	string serverResponse = HttpClient::getInstance().makeRequest(url, args);
	if (serverResponse.find("<html>") != serverResponse.npos) {
		throw TgException("tgbot-cpp library have got html page instead of json response. Maybe you entered wrong bot token.");
	}
	ptree result = TgTypeParser::getInstance().parseJson(serverResponse);
	try {
		if (result.get<bool>("ok", false)) {
			return result.get_child("result");
		} else {
			throw TgException(result.get("description", ""));
		}
	} catch (boost::property_tree::ptree_error& e) {
		throw TgException("tgbot-cpp library can't parse json response. " + e.what());
	}
}

}
<commit_msg>Fix compile error<commit_after>/*
 * Copyright (c) 2015 Oleg Morozenkov
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "tgbot/Api.h"

#include "tgbot/TgTypeParser.h"
#include "tgbot/TgException.h"
#include "tgbot/net/HttpClient.h"

using namespace std;
using namespace boost::property_tree;

namespace TgBot {

Api::Api(const string& token) : _token(token) {
}

User::Ptr Api::getMe() const {
	return TgTypeParser::getInstance().parseJsonAndGetUser(sendRequest("getMe"));
}

Message::Ptr Api::sendMessage(int32_t chatId, const string& text, bool disableWebPagePreview, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("text", text));
	if (disableWebPagePreview) {
		args.push_back(HttpReqArg("disable_web_page_preview", disableWebPagePreview));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendMessage", args));
}

Message::Ptr Api::forwardMessage(int32_t chatId, int32_t fromChatId, int32_t messageId) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("from_chat_id", fromChatId));
	args.push_back(HttpReqArg("message_id", messageId));
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("forwardMessage", args));
}

Message::Ptr Api::sendPhoto(int32_t chatId, const InputFile::Ptr& photo, const string& caption, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("photo", photo->data, true, photo->mimeType, photo->fileName));
	if (!caption.empty()) {
		args.push_back(HttpReqArg("caption", caption));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendPhoto", args));
}

Message::Ptr Api::sendPhoto(int32_t chatId, const string& photoId, const string& caption, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("photo", photoId));
	if (!caption.empty()) {
		args.push_back(HttpReqArg("caption", caption));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendPhoto", args));
}

Message::Ptr Api::sendAudio(int32_t chatId, const InputFile::Ptr& audio, int32_t duration, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("audio", audio->data, true, audio->mimeType, audio->fileName));
	if (duration) {
		args.push_back(HttpReqArg("duration", duration));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendAudio", args));
}

Message::Ptr Api::sendAudio(int32_t chatId, const string& audioId, int32_t duration, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("audio", audioId));
	if (duration) {
		args.push_back(HttpReqArg("duration", duration));
	}
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendAudio", args));
}

Message::Ptr Api::sendDocument(int32_t chatId, const InputFile::Ptr& document, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("document", document->data, true, document->mimeType, document->fileName));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendDocument", args));
}

Message::Ptr Api::sendDocument(int32_t chatId, const string& document, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("document", document));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendDocument", args));
}

Message::Ptr Api::sendSticker(int32_t chatId, const InputFile::Ptr& sticker, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("sticker", sticker->data, true, sticker->mimeType, sticker->fileName));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendSticker", args));
}

Message::Ptr Api::sendSticker(int32_t chatId, const string& stickerId, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("sticker", stickerId));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendSticker", args));
}

Message::Ptr Api::sendVideo(int32_t chatId, const InputFile::Ptr& video, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("video", video->data, true, video->mimeType, video->fileName));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendVideo", args));
}

Message::Ptr Api::sendVideo(int32_t chatId, const string& videoId, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("video", videoId));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendVideo", args));
}

Message::Ptr Api::sendLocation(int32_t chatId, float latitude, float longitude, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("latitude", latitude));
	args.push_back(HttpReqArg("longitude", longitude));
	if (replyToMessageId) {
		args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId));
	}
	if (replyMarkup) {
		args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup)));
	}
	return TgTypeParser::getInstance().parseJsonAndGetMessage(sendRequest("sendLocation", args));
}

void Api::sendChatAction(int32_t chatId, const string& action) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("chat_id", chatId));
	args.push_back(HttpReqArg("action", action));
	sendRequest("sendChatAction", args);
}

UserProfilePhotos::Ptr Api::getUserProfilePhotos(int32_t userId, int32_t offset, int32_t limit) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("user_id", userId));
	if (offset) {
		args.push_back(HttpReqArg("offset", offset));
	}
	limit = max(1, min(100, limit));
	args.push_back(HttpReqArg("limit", limit));
	return TgTypeParser::getInstance().parseJsonAndGetUserProfilePhotos(sendRequest("getUserProfilePhotos", args));
}

vector<Update::Ptr> Api::getUpdates(int32_t offset, int32_t limit, int32_t timeout) const {
	vector<HttpReqArg> args;
	if (offset) {
		args.push_back(HttpReqArg("offset", offset));
	}
	limit = max(1, min(100, limit));
	args.push_back(HttpReqArg("limit", limit));
	if (timeout) {
		args.push_back(HttpReqArg("timeout", timeout));
	}
	return TgTypeParser::getInstance().parseJsonAndGetArray<Update>(&TgTypeParser::parseJsonAndGetUpdate, sendRequest("getUpdates", args), "result");
}

void Api::setWebhook(const string& url) const {
	vector<HttpReqArg> args;
	args.push_back(HttpReqArg("url", url));
	sendRequest("setWebhook", args);
}

ptree Api::sendRequest(const string& method, const vector<HttpReqArg>& args) const {
	string url = "https://api.telegram.org/bot";
	url += _token;
	url += "/";
	url += method;
	string serverResponse = HttpClient::getInstance().makeRequest(url, args);
	if (serverResponse.find("<html>") != serverResponse.npos) {
		throw TgException("tgbot-cpp library have got html page instead of json response. Maybe you entered wrong bot token.");
	}
	ptree result = TgTypeParser::getInstance().parseJson(serverResponse);
	try {
		if (result.get<bool>("ok", false)) {
			return result.get_child("result");
		} else {
			throw TgException(result.get("description", ""));
		}
	} catch (boost::property_tree::ptree_error& e) {
		throw TgException("tgbot-cpp library can't parse json response. " + string(e.what()));
	}
}

}
<|endoftext|>


<commit_before>#include "App.h"
#include "ProgramOptions.h"
#include <string>
#include <iomanip>
#include "FontInfo.h"
#include "utils/extractFileName.h"
#include "external/lodepng/lodepng.h"
#include "utils/getNumberLen.h"

std::vector<rbp::RectSize> App::getGlyphRectangles(const Glyphs &glyphs, const std::uint32_t additionalWidth, const std::uint32_t additionalHeight)
{
    std::vector<rbp::RectSize> result;
    for (const auto& kv : glyphs)
    {
        const auto& glyphInfo = kv.second;
        if (!glyphInfo.isEmpty())
            result.emplace_back(glyphInfo.width + additionalWidth, glyphInfo.height + additionalHeight, kv.first);
    }
    return result;
}

App::Glyphs App::collectGlyphInfo(const std::vector<ft::Font>& fonts, const std::set<std::uint32_t>& codes)
{
    Glyphs result;

    for (const auto& id : codes)
    {
        GlyphInfo glyphInfo;

        for (size_t i = 0; i < fonts.size(); ++i)
        {
            if (fonts[i].isGlyphProvided(id))
            {
                ft::Font::GlyphMetrics glyphMetrics = fonts[i].renderGlyph(nullptr, 0, 0, 0, 0, id, 0);
                glyphInfo.fontIndex = i;
                glyphInfo.width = glyphMetrics.width;
                glyphInfo.height = glyphMetrics.height;
                glyphInfo.xAdvance = glyphMetrics.horiAdvance;
                glyphInfo.xOffset = glyphMetrics.horiBearingX;
                glyphInfo.yOffset = fonts[i].ascent - glyphMetrics.horiBearingY;
                break;
            }
        }

        if (glyphInfo.fontIndex == -1)
            std::cout << "warning: glyph " << id << " not found." << std::endl;

        //TODO: add more checks for glyph.
        result[id] = glyphInfo;
    }

    return result;
}

std::uint32_t App::arrangeGlyphs(Glyphs& glyphs, const Config& config)
{
    const auto additionalWidth = config.spacing.hor + config.padding.left + config.padding.right;
    const auto additionalHeight = config.spacing.ver + config.padding.up + config.padding.down;
    //TODO: check workAreaW,H
    const auto workAreaW = config.textureSize.w - config.spacing.hor;
    const auto workAreaH = config.textureSize.h - config.spacing.ver;

    auto glyphRectangles = getGlyphRectangles(glyphs, additionalWidth, additionalHeight);

    rbp::MaxRectsBinPack mrbp;
    std::uint32_t pageCount = 0;
    for (;;)
    {
        mrbp.Init(workAreaW, workAreaH);

        std::vector<rbp::Rect> arrangedRectangles;
        mrbp.Insert( glyphRectangles, arrangedRectangles, rbp::MaxRectsBinPack::RectBestAreaFit );
        if ( arrangedRectangles.empty() )
        {
            if ( !glyphRectangles.empty() )
                throw std::runtime_error("can not fit glyphs into texture");
            break;
        }

        for ( const auto& r: arrangedRectangles )
        {
            glyphs[r.tag].x = r.x + config.spacing.hor;
            glyphs[r.tag].y = r.y + config.spacing.ver;
            glyphs[r.tag].page = pageCount;
        }

        ++pageCount;
    }

    return pageCount;
}

void App::savePng(const std::string& fileName, const std::uint32_t* buffer, const std::uint32_t w, const std::uint32_t h, const bool withAlpha)
{
    std::vector<std::uint8_t> png;
    lodepng::State state;

    state.encoder.add_id = 0; // Don't add LodePNG version chunk to save more bytes
    state.encoder.auto_convert = 0;
    state.info_png.color.colortype = withAlpha ? LCT_RGBA : LCT_RGB;

    ///state.encoder.text_compression = 1; //Not needed because we don't add text chunks, but this demonstrates another optimization setting
    //state.encoder.zlibsettings.nicematch = 258; //Set this to the max possible, otherwise it can hurt compression
    //state.encoder.zlibsettings.lazymatching = 1; //Definitely use lazy matching for better compression
    //state.encoder.zlibsettings.windowsize = 32768; //Use maximum possible window size for best compression

    auto error = lodepng::encode(png, reinterpret_cast<const unsigned char*>(buffer), w, h, state);
    if (error)
        throw std::runtime_error("png encoder error " + std::to_string(error) + ": " + lodepng_error_text(error));

    error = lodepng::save_file(png, fileName);
    if (error)
        throw std::runtime_error("png save to file error " + std::to_string(error) + ": " + lodepng_error_text(error));
}

std::vector<std::string> App::renderTextures(const Glyphs& glyphs, const Config& config, const std::vector<ft::Font>& fonts, const std::uint32_t pageCount){
    std::vector<std::string> fileNames;

    //TODO: should we decrement pageCount before calculate?
    const auto pageNameDigits = getNumberLen(pageCount);

    for (std::uint32_t page = 0; page < pageCount; ++page)
    {
        std::vector<std::uint32_t> surface(config.textureSize.w * config.textureSize.h);
        memset(&surface[0], 0, surface.size() * sizeof(std::uint32_t));

        // Render every glyph
        //TODO: do not repeat same glyphs (with same index)
        for (const auto& kv: glyphs)
        {
            const auto& glyph = kv.second;
            if (glyph.page != page)
                continue;

            if (!glyph.isEmpty())
            {
                const auto x = glyph.x + config.padding.left;
                const auto y = glyph.y + config.padding.up;

                fonts[glyph.fontIndex].renderGlyph(&surface[0], config.textureSize.w, config.textureSize.h, x, y, kv.first, config.color.getBGR());
            }
        }

        if (!config.backgroundTransparent)
        {
            auto cur = surface.data();
            const auto end = &surface.back();

            const auto fgColor = config.color.getBGR();
            const auto bgColor = config.backgroundColor.getBGR();

            while (cur <= end)
            {
                const std::uint32_t a0 = (*cur) >> 24;
                const std::uint32_t a1 = 256 - a0;
                const std::uint32_t rb1 = (a1 * (bgColor & 0xFF00FF)) >> 8;
                const std::uint32_t rb2 = (a0 * (fgColor & 0xFF00FF)) >> 8;
                const std::uint32_t g1  = (a1 * (bgColor & 0x00FF00)) >> 8;
                const std::uint32_t g2  = (a0 * (fgColor & 0x00FF00)) >> 8;
                *cur =  ((rb1 | rb2) & 0xFF00FF) + ((g1 | g2) & 0x00FF00);
                ++cur;
            }
        }

        std::stringstream ss;
        ss << config.output << "_" << std::setfill ('0') << std::setw(pageNameDigits) << page << ".png";
        const auto fileName = ss.str();
        fileNames.push_back(extractFileName(fileName));

        savePng(fileName, &surface[0], config.textureSize.w, config.textureSize.h, config.backgroundTransparent);
    }

    return fileNames;
}

void App::writeFontInfoFile(const Glyphs& glyphs, const Config& config, const std::vector<ft::Font>& fonts, const std::vector<std::string>& fileNames)
{
    FontInfo f;

    f.info.face = fonts[0].getFamilyNameOr("unknown");
    f.info.size = config.fontSize;
    f.info.unicode = true;
    f.info.aa = 1;
    f.info.padding.up = static_cast<std::uint8_t>(config.padding.up);
    f.info.padding.right = static_cast<std::uint8_t>(config.padding.right);
    f.info.padding.down = static_cast<std::uint8_t>(config.padding.down);
    f.info.padding.left = static_cast<std::uint8_t>(config.padding.left);
    f.info.spacing.horizontal = static_cast<std::uint8_t>(config.spacing.hor);
    f.info.spacing.vertical = static_cast<std::uint8_t>(config.spacing.ver);

    f.common.lineHeight = static_cast<std::uint16_t>(fonts[0].lineskip);
    f.common.base = static_cast<std::uint16_t>(fonts[0].ascent);
    f.common.scaleW = static_cast<std::uint16_t>(config.textureSize.w);
    f.common.scaleH = static_cast<std::uint16_t>(config.textureSize.h);

    f.pages = fileNames;

    for (const auto kv: glyphs)
    {
        //TODO: page = 0 for empty glyphs.
        const auto &glyph = kv.second;
        FontInfo::Char c;
        c.id = kv.first;
        if (!glyph.isEmpty())
        {
            c.x = static_cast<std::uint16_t>(glyph.x);
            c.y = static_cast<std::uint16_t>(glyph.y);
            c.width = static_cast<std::uint16_t>(glyph.width + config.padding.left + config.padding.right);
            c.height = static_cast<std::uint16_t>(glyph.height + config.padding.up + config.padding.down);
            c.page = static_cast<std::uint8_t>(glyph.page);
            c.xoffset = static_cast<std::int16_t>(glyph.xOffset - config.padding.left);
            c.yoffset = static_cast<std::int16_t>(glyph.yOffset - config.padding.up);
        }
        c.xadvance = static_cast<std::int16_t>(glyph.xAdvance);
        c.chnl = 15;

        f.chars.push_back(c);
    }

    if (config.includeKerningPairs)
    {
        //TODO: test if getKerning(font, ch0, ch1) != getKerning(font, ch1, ch0) (and change logic if so)
        auto glyphCodes2(config.chars);
        //TODO: remove bruteforcing
        for (const auto& ch0 : config.chars)
        {
            for (const auto& ch1 : glyphCodes2)
            {
                const auto k = static_cast<std::int16_t>(fonts[0].getKerning(ch0, ch1));
                if (k)
                {
                    FontInfo::Kerning kerning;
                    kerning.first = ch0;
                    kerning.second = ch1;
                    kerning.amount = k;
                    f.kernings.push_back(kerning);
                }
            }
            glyphCodes2.erase(ch0);
        }
    }

    const auto dataFileName = config.output + ".fnt";
    switch (config.dataFormat) {
        case Config::DataFormat::Xml:
            f.writeToXmlFile(dataFileName);
            break;
        case Config::DataFormat::Text:
            f.writeToTextFile(dataFileName);
            break;
        case Config::DataFormat::Bin:
            f.writeToBinFile(dataFileName);
            break;
        case Config::DataFormat::Json:
            f.writeToJsonFile(dataFileName);
            break;
    }
}

void App::execute(const int argc, char* argv[])
{
    const auto config = ProgramOptions::parseCommandLine(argc, argv);

    ft::Library library;

    std::vector<ft::Font> fonts;
    for (auto& f: config.fontFile)
        fonts.emplace_back(library, f, config.fontSize);

    auto glyphs = collectGlyphInfo(fonts, config.chars);
    const auto pageCount = arrangeGlyphs(glyphs, config);

    const auto fileNames = renderTextures(glyphs, config, fonts, pageCount);
    writeFontInfoFile(glyphs, config, fonts, fileNames);
}
<commit_msg>Fix missing kerning<commit_after>#include "App.h"
#include "ProgramOptions.h"
#include <string>
#include <iomanip>
#include "FontInfo.h"
#include "utils/extractFileName.h"
#include "external/lodepng/lodepng.h"
#include "utils/getNumberLen.h"

std::vector<rbp::RectSize> App::getGlyphRectangles(const Glyphs &glyphs, const std::uint32_t additionalWidth, const std::uint32_t additionalHeight)
{
    std::vector<rbp::RectSize> result;
    for (const auto& kv : glyphs)
    {
        const auto& glyphInfo = kv.second;
        if (!glyphInfo.isEmpty())
            result.emplace_back(glyphInfo.width + additionalWidth, glyphInfo.height + additionalHeight, kv.first);
    }
    return result;
}

App::Glyphs App::collectGlyphInfo(const std::vector<ft::Font>& fonts, const std::set<std::uint32_t>& codes)
{
    Glyphs result;

    for (const auto& id : codes)
    {
        GlyphInfo glyphInfo;

        for (size_t i = 0; i < fonts.size(); ++i)
        {
            if (fonts[i].isGlyphProvided(id))
            {
                ft::Font::GlyphMetrics glyphMetrics = fonts[i].renderGlyph(nullptr, 0, 0, 0, 0, id, 0);
                glyphInfo.fontIndex = i;
                glyphInfo.width = glyphMetrics.width;
                glyphInfo.height = glyphMetrics.height;
                glyphInfo.xAdvance = glyphMetrics.horiAdvance;
                glyphInfo.xOffset = glyphMetrics.horiBearingX;
                glyphInfo.yOffset = fonts[i].ascent - glyphMetrics.horiBearingY;
                break;
            }
        }

        if (glyphInfo.fontIndex == -1)
            std::cout << "warning: glyph " << id << " not found." << std::endl;

        //TODO: add more checks for glyph.
        result[id] = glyphInfo;
    }

    return result;
}

std::uint32_t App::arrangeGlyphs(Glyphs& glyphs, const Config& config)
{
    const auto additionalWidth = config.spacing.hor + config.padding.left + config.padding.right;
    const auto additionalHeight = config.spacing.ver + config.padding.up + config.padding.down;
    //TODO: check workAreaW,H
    const auto workAreaW = config.textureSize.w - config.spacing.hor;
    const auto workAreaH = config.textureSize.h - config.spacing.ver;

    auto glyphRectangles = getGlyphRectangles(glyphs, additionalWidth, additionalHeight);

    rbp::MaxRectsBinPack mrbp;
    std::uint32_t pageCount = 0;
    for (;;)
    {
        mrbp.Init(workAreaW, workAreaH);

        std::vector<rbp::Rect> arrangedRectangles;
        mrbp.Insert( glyphRectangles, arrangedRectangles, rbp::MaxRectsBinPack::RectBestAreaFit );
        if ( arrangedRectangles.empty() )
        {
            if ( !glyphRectangles.empty() )
                throw std::runtime_error("can not fit glyphs into texture");
            break;
        }

        for ( const auto& r: arrangedRectangles )
        {
            glyphs[r.tag].x = r.x + config.spacing.hor;
            glyphs[r.tag].y = r.y + config.spacing.ver;
            glyphs[r.tag].page = pageCount;
        }

        ++pageCount;
    }

    return pageCount;
}

void App::savePng(const std::string& fileName, const std::uint32_t* buffer, const std::uint32_t w, const std::uint32_t h, const bool withAlpha)
{
    std::vector<std::uint8_t> png;
    lodepng::State state;

    state.encoder.add_id = 0; // Don't add LodePNG version chunk to save more bytes
    state.encoder.auto_convert = 0;
    state.info_png.color.colortype = withAlpha ? LCT_RGBA : LCT_RGB;

    ///state.encoder.text_compression = 1; //Not needed because we don't add text chunks, but this demonstrates another optimization setting
    //state.encoder.zlibsettings.nicematch = 258; //Set this to the max possible, otherwise it can hurt compression
    //state.encoder.zlibsettings.lazymatching = 1; //Definitely use lazy matching for better compression
    //state.encoder.zlibsettings.windowsize = 32768; //Use maximum possible window size for best compression

    auto error = lodepng::encode(png, reinterpret_cast<const unsigned char*>(buffer), w, h, state);
    if (error)
        throw std::runtime_error("png encoder error " + std::to_string(error) + ": " + lodepng_error_text(error));

    error = lodepng::save_file(png, fileName);
    if (error)
        throw std::runtime_error("png save to file error " + std::to_string(error) + ": " + lodepng_error_text(error));
}

std::vector<std::string> App::renderTextures(const Glyphs& glyphs, const Config& config, const std::vector<ft::Font>& fonts, const std::uint32_t pageCount){
    std::vector<std::string> fileNames;

    //TODO: should we decrement pageCount before calculate?
    const auto pageNameDigits = getNumberLen(pageCount);

    for (std::uint32_t page = 0; page < pageCount; ++page)
    {
        std::vector<std::uint32_t> surface(config.textureSize.w * config.textureSize.h);
        memset(&surface[0], 0, surface.size() * sizeof(std::uint32_t));

        // Render every glyph
        //TODO: do not repeat same glyphs (with same index)
        for (const auto& kv: glyphs)
        {
            const auto& glyph = kv.second;
            if (glyph.page != page)
                continue;

            if (!glyph.isEmpty())
            {
                const auto x = glyph.x + config.padding.left;
                const auto y = glyph.y + config.padding.up;

                fonts[glyph.fontIndex].renderGlyph(&surface[0], config.textureSize.w, config.textureSize.h, x, y, kv.first, config.color.getBGR());
            }
        }

        if (!config.backgroundTransparent)
        {
            auto cur = surface.data();
            const auto end = &surface.back();

            const auto fgColor = config.color.getBGR();
            const auto bgColor = config.backgroundColor.getBGR();

            while (cur <= end)
            {
                const std::uint32_t a0 = (*cur) >> 24;
                const std::uint32_t a1 = 256 - a0;
                const std::uint32_t rb1 = (a1 * (bgColor & 0xFF00FF)) >> 8;
                const std::uint32_t rb2 = (a0 * (fgColor & 0xFF00FF)) >> 8;
                const std::uint32_t g1  = (a1 * (bgColor & 0x00FF00)) >> 8;
                const std::uint32_t g2  = (a0 * (fgColor & 0x00FF00)) >> 8;
                *cur =  ((rb1 | rb2) & 0xFF00FF) + ((g1 | g2) & 0x00FF00);
                ++cur;
            }
        }

        std::stringstream ss;
        ss << config.output << "_" << std::setfill ('0') << std::setw(pageNameDigits) << page << ".png";
        const auto fileName = ss.str();
        fileNames.push_back(extractFileName(fileName));

        savePng(fileName, &surface[0], config.textureSize.w, config.textureSize.h, config.backgroundTransparent);
    }

    return fileNames;
}

void App::writeFontInfoFile(const Glyphs& glyphs, const Config& config, const std::vector<ft::Font>& fonts, const std::vector<std::string>& fileNames)
{
    FontInfo f;

    f.info.face = fonts[0].getFamilyNameOr("unknown");
    f.info.size = config.fontSize;
    f.info.unicode = true;
    f.info.aa = 1;
    f.info.padding.up = static_cast<std::uint8_t>(config.padding.up);
    f.info.padding.right = static_cast<std::uint8_t>(config.padding.right);
    f.info.padding.down = static_cast<std::uint8_t>(config.padding.down);
    f.info.padding.left = static_cast<std::uint8_t>(config.padding.left);
    f.info.spacing.horizontal = static_cast<std::uint8_t>(config.spacing.hor);
    f.info.spacing.vertical = static_cast<std::uint8_t>(config.spacing.ver);

    f.common.lineHeight = static_cast<std::uint16_t>(fonts[0].lineskip);
    f.common.base = static_cast<std::uint16_t>(fonts[0].ascent);
    f.common.scaleW = static_cast<std::uint16_t>(config.textureSize.w);
    f.common.scaleH = static_cast<std::uint16_t>(config.textureSize.h);

    f.pages = fileNames;

    for (const auto kv: glyphs)
    {
        //TODO: page = 0 for empty glyphs.
        const auto &glyph = kv.second;
        FontInfo::Char c;
        c.id = kv.first;
        if (!glyph.isEmpty())
        {
            c.x = static_cast<std::uint16_t>(glyph.x);
            c.y = static_cast<std::uint16_t>(glyph.y);
            c.width = static_cast<std::uint16_t>(glyph.width + config.padding.left + config.padding.right);
            c.height = static_cast<std::uint16_t>(glyph.height + config.padding.up + config.padding.down);
            c.page = static_cast<std::uint8_t>(glyph.page);
            c.xoffset = static_cast<std::int16_t>(glyph.xOffset - config.padding.left);
            c.yoffset = static_cast<std::int16_t>(glyph.yOffset - config.padding.up);
        }
        c.xadvance = static_cast<std::int16_t>(glyph.xAdvance);
        c.chnl = 15;

        f.chars.push_back(c);
    }

    if (config.includeKerningPairs)
    {
        auto chars(config.chars);

        for (const auto& ch0 : config.chars)
        {
            for (const auto& ch1 : chars)
            {
                const auto k = static_cast<std::int16_t>(fonts[0].getKerning(ch0, ch1));
                if (k)
                {
                    FontInfo::Kerning kerning;
                    kerning.first = ch0;
                    kerning.second = ch1;
                    kerning.amount = k;
                    f.kernings.push_back(kerning);
                }
            }
        }
    }

    const auto dataFileName = config.output + ".fnt";
    switch (config.dataFormat) {
        case Config::DataFormat::Xml:
            f.writeToXmlFile(dataFileName);
            break;
        case Config::DataFormat::Text:
            f.writeToTextFile(dataFileName);
            break;
        case Config::DataFormat::Bin:
            f.writeToBinFile(dataFileName);
            break;
        case Config::DataFormat::Json:
            f.writeToJsonFile(dataFileName);
            break;
    }
}

void App::execute(const int argc, char* argv[])
{
    const auto config = ProgramOptions::parseCommandLine(argc, argv);

    ft::Library library;

    std::vector<ft::Font> fonts;
    for (auto& f: config.fontFile)
        fonts.emplace_back(library, f, config.fontSize);

    auto glyphs = collectGlyphInfo(fonts, config.chars);
    const auto pageCount = arrangeGlyphs(glyphs, config);

    const auto fileNames = renderTextures(glyphs, config, fonts, pageCount);
    writeFontInfoFile(glyphs, config, fonts, fileNames);
}
<|endoftext|>