<commit_before>#include <jni.h>
// To get free disk space
#include <sys/vfs.h>

#include "../../../../../defines.hpp"

#include "../../../../../platform/http_request.hpp"

#include "../../../../../base/logging.hpp"
#include "../../../../../base/string_utils.hpp"

#include "../../../../../coding/zip_reader.hpp"
#include "../../../../../coding/url_encode.hpp"
#include "../../../../../coding/reader_streambuf.hpp"
#include "../../../../../coding/internal/file_data.hpp"

#include "../../../../../platform/platform.hpp"
#include "../../../../../platform/http_request.hpp"

#include "../../../../../std/vector.hpp"
#include "../../../../../std/string.hpp"
#include "../../../../../std/bind.hpp"
#include "../../../../../std/shared_ptr.hpp"

#include "../core/jni_helper.hpp"

#include "Framework.hpp"

// Special error codes to notify GUI about free space
//@{
#define ERR_DOWNLOAD_SUCCESS   0
#define ERR_NOT_ENOUGH_MEMORY     -1
#define ERR_NOT_ENOUGH_FREE_SPACE -2
#define ERR_STORAGE_DISCONNECTED  -3
#define ERR_DOWNLOAD_ERROR -4
#define ERR_NO_MORE_FILES -5
#define ERR_FILE_IN_PROGRESS -6
//@}

struct FileToDownload
{
  vector<string> m_urls;
  string m_fileName;
  string m_pathOnSdcard;
  uint64_t m_fileSize;
};

static string g_apkPath;
static string g_sdcardPath;
static vector<FileToDownload> g_filesToDownload;
static int g_totalDownloadedBytes;
static int g_totalBytesToDownload;
static shared_ptr<downloader::HttpRequest> g_currentRequest;

extern "C"
{
  int HasSpaceForFiles(size_t fileSize)
  {
    struct statfs st;

    if (statfs(g_sdcardPath.c_str(), &st) != 0)
      return ERR_STORAGE_DISCONNECTED;

    if (st.f_bsize * st.f_bavail <= fileSize)
      return ERR_NOT_ENOUGH_FREE_SPACE;

    return fileSize;
  }

  JNIEXPORT jint JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_getBytesToDownload(JNIEnv * env, jobject thiz,
      jstring apkPath, jstring sdcardPath)
  {
    g_apkPath = jni::ToString(apkPath);
    g_sdcardPath = jni::ToString(sdcardPath);

    jint totalBytesToDownload = 0;

    ReaderStreamBuf buffer(GetPlatform().GetReader("external_resources.txt"));

    istream in(&buffer);

    string name;
    int size;

    while (true)
    {
      in >> name;

      if (!in.good())
        break;

      in >> size;

      if (!in.good())
        break;

      FileToDownload f;

      f.m_pathOnSdcard = g_sdcardPath + name;
      f.m_fileName = name;

      uint64_t sizeOnSdcard = 0;
      Platform::GetFileSizeByFullPath(f.m_pathOnSdcard, sizeOnSdcard);

      if (size != sizeOnSdcard)
      {
        LOG(LINFO, ("should check : ", name, "sized", size, "bytes"));
        f.m_fileSize = size;
        g_filesToDownload.push_back(f);
        totalBytesToDownload += size;
      }
    }

    g_totalDownloadedBytes = 0;

    int res = HasSpaceForFiles(totalBytesToDownload);

    switch (res)
    {
    case ERR_STORAGE_DISCONNECTED:
      LOG(LWARNING, ("External file system is not available"));
      break;
    case ERR_NOT_ENOUGH_FREE_SPACE:
      LOG(LWARNING, ("Not enough space to extract files"));
      break;
    };

    g_totalBytesToDownload = totalBytesToDownload;
    g_currentRequest.reset();

    return res;
  }

  void DownloadFileFinished(shared_ptr<jobject> obj, downloader::HttpRequest & req)
  {
    int errorCode = 0;

    ASSERT(req.Status() != downloader::HttpRequest::EInProgress, ("should be either Completed or Failed"));

    switch (req.Status())
    {
    case downloader::HttpRequest::ECompleted:
      errorCode = ERR_DOWNLOAD_SUCCESS;
      break;
    case downloader::HttpRequest::EFailed:
      errorCode = ERR_DOWNLOAD_ERROR;
      break;
    };

    g_currentRequest.reset();

    if (errorCode == ERR_DOWNLOAD_SUCCESS)
    {
      FileToDownload & curFile = g_filesToDownload.back();

      /// cleaning up by ourselves
      FileWriter::DeleteFileX(curFile.m_pathOnSdcard + DOWNLOADING_FILE_EXTENSION);
      FileWriter::DeleteFileX(curFile.m_pathOnSdcard + RESUME_FILE_EXTENSION);

      LOG(LDEBUG, ("finished downloading", curFile.m_fileName, "sized", curFile.m_fileSize, "bytes"));

      /// slight hack, check manually for Maps extension and AddMap accordingly
      if (curFile.m_fileName.find(".mwm") != string::npos)
      {
        LOG(LINFO, ("adding it as a map"));
        g_framework->AddMap(curFile.m_fileName);
      }

      g_totalDownloadedBytes += curFile.m_fileSize;

      LOG(LDEBUG, ("totalDownloadedBytes:", g_totalDownloadedBytes));

      g_filesToDownload.pop_back();
    }

    JNIEnv * env = jni::GetEnv();

    jmethodID methodID = jni::GetJavaMethodID(env, *obj.get(), "onDownloadFinished", "(I)V");
    env->CallVoidMethod(*obj.get(), methodID, errorCode);
  }

  void DownloadFileProgress(shared_ptr<jobject> obj, downloader::HttpRequest & req)
  {
    LOG(LDEBUG, (req.Progress().first, "bytes for", g_filesToDownload.back().m_fileName, "was downloaded"));

    FileToDownload & curFile = g_filesToDownload.back();

    jint curTotal = req.Progress().second;
    jint curProgress = req.Progress().first;
    jint glbTotal = g_totalBytesToDownload;
    jint glbProgress = g_totalDownloadedBytes + req.Progress().first;

    JNIEnv * env = jni::GetEnv();

    jmethodID methodID = jni::GetJavaMethodID(env, *obj.get(), "onDownloadProgress", "(IIII)V");
    env->CallVoidMethod(*obj.get(), methodID,
                         curTotal, curProgress,
                         glbTotal, glbProgress);
  }

  void DownloadURLListFinished(downloader::HttpRequest & req,
      downloader::HttpRequest::CallbackT onFinish,
      downloader::HttpRequest::CallbackT onProgress)
  {
    if (req.Status() == downloader::HttpRequest::EFailed)
      onFinish(req);
    else
    {
      FileToDownload & curFile = g_filesToDownload.back();

      LOG(LDEBUG, ("finished URL list download for", curFile.m_fileName));

      downloader::ParseServerList(req.Data(), curFile.m_urls);

      for (size_t i = 0; i < curFile.m_urls.size(); ++i)
      {
        curFile.m_urls[i] = curFile.m_urls[i] + OMIM_OS_NAME "/"
                          + strings::to_string(g_framework->Storage().GetCurrentVersion()) + "/"
                          + UrlEncode(curFile.m_fileName);
        LOG(LDEBUG, (curFile.m_urls[i]));
      }

      g_currentRequest.reset(downloader::HttpRequest::GetFile(curFile.m_urls,
                                                          curFile.m_pathOnSdcard,
                                                          curFile.m_fileSize,
                                                          onFinish,
                                                          onProgress,
                                                          64 * 1024,
                                                          false));
    }
  }

  JNIEXPORT void JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_cancelCurrentFile(JNIEnv * env, jobject thiz)
  {
    LOG(LINFO, ("pauseDownload, currentRequest=", g_currentRequest));
    g_currentRequest.reset();
  }

  JNIEXPORT int JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_startNextFileDownload(JNIEnv * env,
      jobject thiz, jobject observer)
  {
    if (g_filesToDownload.empty())
      return ERR_NO_MORE_FILES;

    FileToDownload & curFile = g_filesToDownload.back();

    LOG(LINFO, ("downloading", curFile.m_fileName, "sized", curFile.m_fileSize, "bytes"));

    downloader::HttpRequest::CallbackT onFinish(bind(&DownloadFileFinished, jni::make_global_ref(observer), _1));
    downloader::HttpRequest::CallbackT onProgress(bind(&DownloadFileProgress, jni::make_global_ref(observer), _1));

    g_currentRequest.reset(downloader::HttpRequest::PostJson(GetPlatform().MetaServerUrl(),
                                                         curFile.m_fileName,
                                                         bind(&DownloadURLListFinished, _1,
                                                             onFinish,
                                                             onProgress)));

    return ERR_FILE_IN_PROGRESS;
  }

  // Move downloaded maps from /sdcard/Android/data/com.mapswithme.maps/files/
  // to /sdcard/MapsWithMe
  JNIEXPORT void JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_moveMaps(JNIEnv * env, jobject thiz,
      jstring fromPath, jstring toPath)
  {
    string from = jni::ToString(fromPath);
    string to = jni::ToString(toPath);

    Platform & pl = GetPlatform();
    Platform::FilesList files;
    // Move *.mwm files
    pl.GetFilesInDir(from, "*" DATA_FILE_EXTENSION, files);
    for (size_t i = 0; i < files.size(); ++i)
    {
      LOG(LDEBUG, ("moving map from:", from + files[i], ", to:", to + files[i]));
      my::RenameFileX((from + files[i]).c_str(), (to + files[i]).c_str());
    }

    // Delete not finished *.downloading files
    files.clear();
    pl.GetFilesInDir(from, "*" DOWNLOADING_FILE_EXTENSION, files);
    pl.GetFilesInDir(from, "*" RESUME_FILE_EXTENSION, files);
    for (size_t i = 0; i < files.size(); ++i)
      my::DeleteFileX((from + files[i]).c_str());
  }

  JNIEXPORT jstring JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_findCountryByPos(JNIEnv * env, jobject thiz,
      jdouble lat, jdouble lon)
  {
    double x = MercatorBounds::LonToX(lon);
    double y = MercatorBounds::LatToY(lat);

    string name = g_framework->GetCountryName(x, y);

    return env->NewStringUTF(name.c_str());
  }
}
<commit_msg>moved extra logging into LDEBUG level.<commit_after>#include <jni.h>
// To get free disk space
#include <sys/vfs.h>

#include "../../../../../defines.hpp"

#include "../../../../../platform/http_request.hpp"

#include "../../../../../base/logging.hpp"
#include "../../../../../base/string_utils.hpp"

#include "../../../../../coding/zip_reader.hpp"
#include "../../../../../coding/url_encode.hpp"
#include "../../../../../coding/reader_streambuf.hpp"
#include "../../../../../coding/internal/file_data.hpp"

#include "../../../../../platform/platform.hpp"
#include "../../../../../platform/http_request.hpp"

#include "../../../../../std/vector.hpp"
#include "../../../../../std/string.hpp"
#include "../../../../../std/bind.hpp"
#include "../../../../../std/shared_ptr.hpp"

#include "../core/jni_helper.hpp"

#include "Framework.hpp"

// Special error codes to notify GUI about free space
//@{
#define ERR_DOWNLOAD_SUCCESS   0
#define ERR_NOT_ENOUGH_MEMORY     -1
#define ERR_NOT_ENOUGH_FREE_SPACE -2
#define ERR_STORAGE_DISCONNECTED  -3
#define ERR_DOWNLOAD_ERROR -4
#define ERR_NO_MORE_FILES -5
#define ERR_FILE_IN_PROGRESS -6
//@}

struct FileToDownload
{
  vector<string> m_urls;
  string m_fileName;
  string m_pathOnSdcard;
  uint64_t m_fileSize;
};

static string g_apkPath;
static string g_sdcardPath;
static vector<FileToDownload> g_filesToDownload;
static int g_totalDownloadedBytes;
static int g_totalBytesToDownload;
static shared_ptr<downloader::HttpRequest> g_currentRequest;

extern "C"
{
  int HasSpaceForFiles(size_t fileSize)
  {
    struct statfs st;

    if (statfs(g_sdcardPath.c_str(), &st) != 0)
      return ERR_STORAGE_DISCONNECTED;

    if (st.f_bsize * st.f_bavail <= fileSize)
      return ERR_NOT_ENOUGH_FREE_SPACE;

    return fileSize;
  }

  JNIEXPORT jint JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_getBytesToDownload(JNIEnv * env, jobject thiz,
      jstring apkPath, jstring sdcardPath)
  {
    g_apkPath = jni::ToString(apkPath);
    g_sdcardPath = jni::ToString(sdcardPath);

    jint totalBytesToDownload = 0;

    ReaderStreamBuf buffer(GetPlatform().GetReader("external_resources.txt"));

    istream in(&buffer);

    string name;
    int size;

    while (true)
    {
      in >> name;

      if (!in.good())
        break;

      in >> size;

      if (!in.good())
        break;

      FileToDownload f;

      f.m_pathOnSdcard = g_sdcardPath + name;
      f.m_fileName = name;

      uint64_t sizeOnSdcard = 0;
      Platform::GetFileSizeByFullPath(f.m_pathOnSdcard, sizeOnSdcard);

      if (size != sizeOnSdcard)
      {
        LOG(LDEBUG, ("should download : ", name, "sized", size, "bytes"));
        f.m_fileSize = size;
        g_filesToDownload.push_back(f);
        totalBytesToDownload += size;
      }
    }

    g_totalDownloadedBytes = 0;

    int res = HasSpaceForFiles(totalBytesToDownload);

    switch (res)
    {
    case ERR_STORAGE_DISCONNECTED:
      LOG(LWARNING, ("External file system is not available"));
      break;
    case ERR_NOT_ENOUGH_FREE_SPACE:
      LOG(LWARNING, ("Not enough space to extract files"));
      break;
    };

    g_totalBytesToDownload = totalBytesToDownload;
    g_currentRequest.reset();

    return res;
  }

  void DownloadFileFinished(shared_ptr<jobject> obj, downloader::HttpRequest & req)
  {
    int errorCode = 0;

    ASSERT(req.Status() != downloader::HttpRequest::EInProgress, ("should be either Completed or Failed"));

    switch (req.Status())
    {
    case downloader::HttpRequest::ECompleted:
      errorCode = ERR_DOWNLOAD_SUCCESS;
      break;
    case downloader::HttpRequest::EFailed:
      errorCode = ERR_DOWNLOAD_ERROR;
      break;
    };

    g_currentRequest.reset();

    if (errorCode == ERR_DOWNLOAD_SUCCESS)
    {
      FileToDownload & curFile = g_filesToDownload.back();

      /// cleaning up by ourselves
      FileWriter::DeleteFileX(curFile.m_pathOnSdcard + DOWNLOADING_FILE_EXTENSION);
      FileWriter::DeleteFileX(curFile.m_pathOnSdcard + RESUME_FILE_EXTENSION);

      LOG(LDEBUG, ("finished downloading", curFile.m_fileName, "sized", curFile.m_fileSize, "bytes"));

      /// slight hack, check manually for Maps extension and AddMap accordingly
      if (curFile.m_fileName.find(".mwm") != string::npos)
      {
        LOG(LINFO, ("adding it as a map"));
        g_framework->AddMap(curFile.m_fileName);
      }

      g_totalDownloadedBytes += curFile.m_fileSize;

      LOG(LDEBUG, ("totalDownloadedBytes:", g_totalDownloadedBytes));

      g_filesToDownload.pop_back();
    }

    JNIEnv * env = jni::GetEnv();

    jmethodID methodID = jni::GetJavaMethodID(env, *obj.get(), "onDownloadFinished", "(I)V");
    env->CallVoidMethod(*obj.get(), methodID, errorCode);
  }

  void DownloadFileProgress(shared_ptr<jobject> obj, downloader::HttpRequest & req)
  {
    LOG(LDEBUG, (req.Progress().first, "bytes for", g_filesToDownload.back().m_fileName, "was downloaded"));

    FileToDownload & curFile = g_filesToDownload.back();

    jint curTotal = req.Progress().second;
    jint curProgress = req.Progress().first;
    jint glbTotal = g_totalBytesToDownload;
    jint glbProgress = g_totalDownloadedBytes + req.Progress().first;

    JNIEnv * env = jni::GetEnv();

    jmethodID methodID = jni::GetJavaMethodID(env, *obj.get(), "onDownloadProgress", "(IIII)V");
    env->CallVoidMethod(*obj.get(), methodID,
                         curTotal, curProgress,
                         glbTotal, glbProgress);
  }

  void DownloadURLListFinished(downloader::HttpRequest & req,
      downloader::HttpRequest::CallbackT onFinish,
      downloader::HttpRequest::CallbackT onProgress)
  {
    if (req.Status() == downloader::HttpRequest::EFailed)
      onFinish(req);
    else
    {
      FileToDownload & curFile = g_filesToDownload.back();

      LOG(LDEBUG, ("finished URL list download for", curFile.m_fileName));

      downloader::ParseServerList(req.Data(), curFile.m_urls);

      for (size_t i = 0; i < curFile.m_urls.size(); ++i)
      {
        curFile.m_urls[i] = curFile.m_urls[i] + OMIM_OS_NAME "/"
                          + strings::to_string(g_framework->Storage().GetCurrentVersion()) + "/"
                          + UrlEncode(curFile.m_fileName);
        LOG(LDEBUG, (curFile.m_urls[i]));
      }

      g_currentRequest.reset(downloader::HttpRequest::GetFile(curFile.m_urls,
                                                          curFile.m_pathOnSdcard,
                                                          curFile.m_fileSize,
                                                          onFinish,
                                                          onProgress,
                                                          64 * 1024,
                                                          false));
    }
  }

  JNIEXPORT void JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_cancelCurrentFile(JNIEnv * env, jobject thiz)
  {
    LOG(LDEBUG, ("cancelCurrentFile, currentRequest=", g_currentRequest.get()));
    g_currentRequest.reset();
  }

  JNIEXPORT int JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_startNextFileDownload(JNIEnv * env,
      jobject thiz, jobject observer)
  {
    if (g_filesToDownload.empty())
      return ERR_NO_MORE_FILES;

    FileToDownload & curFile = g_filesToDownload.back();

    LOG(LDEBUG, ("downloading", curFile.m_fileName, "sized", curFile.m_fileSize, "bytes"));

    downloader::HttpRequest::CallbackT onFinish(bind(&DownloadFileFinished, jni::make_global_ref(observer), _1));
    downloader::HttpRequest::CallbackT onProgress(bind(&DownloadFileProgress, jni::make_global_ref(observer), _1));

    g_currentRequest.reset(downloader::HttpRequest::PostJson(GetPlatform().MetaServerUrl(),
                                                         curFile.m_fileName,
                                                         bind(&DownloadURLListFinished, _1,
                                                             onFinish,
                                                             onProgress)));

    return ERR_FILE_IN_PROGRESS;
  }

  // Move downloaded maps from /sdcard/Android/data/com.mapswithme.maps/files/
  // to /sdcard/MapsWithMe
  JNIEXPORT void JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_moveMaps(JNIEnv * env, jobject thiz,
      jstring fromPath, jstring toPath)
  {
    string from = jni::ToString(fromPath);
    string to = jni::ToString(toPath);

    Platform & pl = GetPlatform();
    Platform::FilesList files;
    // Move *.mwm files
    pl.GetFilesInDir(from, "*" DATA_FILE_EXTENSION, files);
    for (size_t i = 0; i < files.size(); ++i)
    {
      LOG(LDEBUG, ("moving map from:", from + files[i], ", to:", to + files[i]));
      my::RenameFileX((from + files[i]).c_str(), (to + files[i]).c_str());
    }

    // Delete not finished *.downloading files
    files.clear();
    pl.GetFilesInDir(from, "*" DOWNLOADING_FILE_EXTENSION, files);
    pl.GetFilesInDir(from, "*" RESUME_FILE_EXTENSION, files);
    for (size_t i = 0; i < files.size(); ++i)
      my::DeleteFileX((from + files[i]).c_str());
  }

  JNIEXPORT jstring JNICALL
  Java_com_mapswithme_maps_DownloadResourcesActivity_findCountryByPos(JNIEnv * env, jobject thiz,
      jdouble lat, jdouble lon)
  {
    double x = MercatorBounds::LonToX(lon);
    double y = MercatorBounds::LatToY(lat);

    string name = g_framework->GetCountryName(x, y);

    return env->NewStringUTF(name.c_str());
  }
}
<|endoftext|>


<commit_before>/*
 * THE UNICODE TEST SUITE FOR CINDER: https://github.com/arielm/Unicode
 * COPYRIGHT (C) 2013, ARIEL MALKA ALL RIGHTS RESERVED.
 *
 * THE FOLLOWING SOURCE-CODE IS DISTRIBUTED UNDER THE MODIFIED BSD LICENSE:
 * https://github.com/arielm/Unicode/blob/master/LICENSE.md
 */

/*
 * DEMONSTRATING SHAPING FALLBACK, AS TAKING PLACE IN:
 * https://github.com/mapnik/mapnik/blob/64d5153aeaeb1c9e736bfead297dfea39b066d2c/include/mapnik/text/harfbuzz_shaper.hpp
 *
 *
 * DroidSansHebrew-Regular.ttf IS ONLY CONTAINING HEBREW CHARACTERS AND DIACRITICS
 * THEREFORE, WE RELY ON DroidSans.ttf FOR ANYTHING ELSE
 *
 *
 * KEEPING THE CODE DELIBERATELY NOT OPTIMIZED TO FACILITATE UNDERSTANDING
 */

#include "cinder/app/AppNative.h"

#include "YFont.h"

using namespace std;
using namespace ci;
using namespace app;

const float FONT_SIZE = 56;

class Application : public AppNative
{
    shared_ptr<FreetypeHelper> ftHelper; // THE UNDERLYING FT_Library WILL BE DESTROYED AFTER ALL THE YFont INSTANCES

    shared_ptr<YFont> font1;
    shared_ptr<YFont> font2;

public:
    void prepareSettings(Settings *settings);
    void setup();

    void draw();
    void drawSpan(YFont &font1, YFont &font2, const TextSpan &span, float y);
    void drawSpan(YFont &font1, YFont &font2, const TextSpan &span, float x, float y);
    void drawHLine(float y);
};

void Application::prepareSettings(Settings *settings)
{
    settings->setWindowSize(1024, 512);
}

void Application::setup()
{
    ftHelper = make_shared<FreetypeHelper>();

    font1 = make_shared<YFont>(ftHelper, FontDescriptor(loadAsset("fonts/DroidSansHebrew-Regular.ttf")), FONT_SIZE, ColorA(1, 1, 1, 1));
    font2 = make_shared<YFont>(ftHelper, FontDescriptor(loadAsset("fonts/DroidSans.ttf")), FONT_SIZE, ColorA(1, 1, 0.5f, 1));

    // ---

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);
}

void Application::draw()
{
    gl::clear(Color::gray(0.5f), false);
    gl::setMatricesWindow(toPixels(getWindowSize()), true);

    drawSpan(*font1, *font2, TextSpan("אֱב;גד 123וּ", HB_SCRIPT_HEBREW, HB_DIRECTION_RTL, "he"), 256);
}

void Application::drawSpan(YFont &font1, YFont &font2, const TextSpan &span, float y)
{
    drawSpan(font1, font2, span, 24, y);

    glColor4f(1, 0.75f, 0, 0.5f);
    drawHLine(y);
}

void Application::drawSpan(YFont &font1, YFont &font2, const TextSpan &span, float x, float y)
{
    const char *shapers[]  = { "ot", "fallback", NULL };
    hb_buffer_t *buffer = hb_buffer_create();

    vector<Shape> shapes;

    /*
     * FIRST PASS
     */

    span.apply(buffer);
    hb_shape_full(font1.hbFont, buffer, NULL, 0, shapers);

    unsigned int glyphCount;
    auto glyph_info = hb_buffer_get_glyph_infos(buffer, &glyphCount);
    auto glyph_pos = hb_buffer_get_glyph_positions(buffer, nullptr);

    for (int i = 0; i < glyphCount; i++)
    {
        auto codepoint = glyph_info[i].codepoint;
        Vec2f offset(glyph_pos[i].x_offset, -glyph_pos[i].y_offset);
        float advance = glyph_pos[i].x_advance;

        if (codepoint)
        {
            shapes.emplace_back(&font1, codepoint, offset, advance);
        }
        else
        {
            shapes.emplace_back(); // MAKING ROOM FOR LATER
        }
    }

    hb_buffer_clear_contents(buffer);

    /*
     * SECOND PASS
     */

    span.apply(buffer);
    hb_shape_full(font2.hbFont, buffer, NULL, 0, shapers);

    for (int i = 0; i < glyphCount; i++)
    {
        auto codepoint = glyph_info[i].codepoint;
        Vec2f offset(glyph_pos[i].x_offset, -glyph_pos[i].y_offset);
        float advance = glyph_pos[i].x_advance;

        if (codepoint)
        {
            shapes[i].update(&font2, codepoint, offset, advance);
        }
    }

    hb_buffer_destroy(buffer);

    // ---

    glPushMatrix();
    glTranslatef(x, y, 0);

    for (int i = 0; i < glyphCount; i++)
    {
        float advance = shapes[i].draw();
        glTranslatef(advance, 0, 0);
    }

    glPopMatrix();
}

void Application::drawHLine(float y)
{
    gl::drawLine(Vec2f(-9999, y), Vec2f(+9999, y));
}

CINDER_APP_NATIVE(Application, RendererGl(RendererGl::AA_NONE))
<commit_msg>ShapingFallback UPDATE: IT IS NOT WORKING AS INTENDED WHEN INTRODUCING DIACRITICS!<commit_after>/*
 * THE UNICODE TEST SUITE FOR CINDER: https://github.com/arielm/Unicode
 * COPYRIGHT (C) 2013, ARIEL MALKA ALL RIGHTS RESERVED.
 *
 * THE FOLLOWING SOURCE-CODE IS DISTRIBUTED UNDER THE MODIFIED BSD LICENSE:
 * https://github.com/arielm/Unicode/blob/master/LICENSE.md
 */

/*
 * TESTING SHAPING FALLBACK, AS TAKING PLACE IN:
 * https://github.com/mapnik/mapnik/blob/64d5153aeaeb1c9e736bfead297dfea39b066d2c/include/mapnik/text/harfbuzz_shaper.hpp
 *
 *
 * DroidSansHebrew-Regular.ttf IS ONLY CONTAINING HEBREW CHARACTERS AND DIACRITICS
 * THEREFORE, WE RELY ON DroidSans.ttf FOR ANYTHING ELSE
 *
 *
 * UPDATE:
 * - IT IS NOT WORKING AS INTENDED WHEN INTRODUCING DIACRITICS!
 * - LET'S TRY TO MAKE USE OF THE CLUSTER INFO, AS IN MAPNIK...
 */

#include "cinder/app/AppNative.h"

#include "YFont.h"

using namespace std;
using namespace ci;
using namespace app;

const float FONT_SIZE = 56;

class Application : public AppNative
{
    shared_ptr<FreetypeHelper> ftHelper; // THE UNDERLYING FT_Library WILL BE DESTROYED AFTER ALL THE YFont INSTANCES

    shared_ptr<YFont> font1;
    shared_ptr<YFont> font2;

public:
    void prepareSettings(Settings *settings);
    void setup();

    void draw();
    void drawSpan(YFont &font1, YFont &font2, const TextSpan &span, float y);
    void drawSpan(YFont &font1, YFont &font2, const TextSpan &span, float x, float y);
    void drawHLine(float y);
};

void Application::prepareSettings(Settings *settings)
{
    settings->setWindowSize(1024, 512);
}

void Application::setup()
{
    ftHelper = make_shared<FreetypeHelper>();

    font1 = make_shared<YFont>(ftHelper, FontDescriptor(loadAsset("fonts/DroidSansHebrew-Regular.ttf")), FONT_SIZE, ColorA(1, 1, 1, 1));
    font2 = make_shared<YFont>(ftHelper, FontDescriptor(loadAsset("fonts/DroidSans.ttf")), FONT_SIZE, ColorA(1, 1, 0.5f, 1));

    // ---

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);
}

void Application::draw()
{
    gl::clear(Color::gray(0.5f), false);
    gl::setMatricesWindow(toPixels(getWindowSize()), true);

    drawSpan(*font1, *font2, TextSpan("אב;גד 123ו", HB_SCRIPT_HEBREW, HB_DIRECTION_RTL, "he"), 128);
    drawSpan(*font1, *font2, TextSpan("אֱב;גד 123וּ", HB_SCRIPT_HEBREW, HB_DIRECTION_RTL, "he"), 256); // NOT WORKING AS INTENDED
}

void Application::drawSpan(YFont &font1, YFont &font2, const TextSpan &span, float y)
{
    drawSpan(font1, font2, span, 24, y);

    glColor4f(1, 0.75f, 0, 0.5f);
    drawHLine(y);
}

void Application::drawSpan(YFont &font1, YFont &font2, const TextSpan &span, float x, float y)
{
    const char *shapers[]  = { "ot", "fallback", NULL };
    hb_buffer_t *buffer = hb_buffer_create();

    vector<Shape> shapes;

    /*
     * FIRST PASS
     */

    span.apply(buffer);
    hb_shape_full(font1.hbFont, buffer, NULL, 0, shapers);

    unsigned int glyphCount;
    auto glyph_info = hb_buffer_get_glyph_infos(buffer, &glyphCount);
    auto glyph_pos = hb_buffer_get_glyph_positions(buffer, nullptr);

    for (int i = 0; i < glyphCount; i++)
    {
        auto codepoint = glyph_info[i].codepoint;
        Vec2f offset(glyph_pos[i].x_offset, -glyph_pos[i].y_offset);
        float advance = glyph_pos[i].x_advance;

        if (codepoint)
        {
            shapes.emplace_back(&font1, codepoint, offset, advance);
        }
        else
        {
            shapes.emplace_back(); // MAKING ROOM FOR LATER
        }
    }

    hb_buffer_clear_contents(buffer);

    /*
     * SECOND PASS
     */

    span.apply(buffer);
    hb_shape_full(font2.hbFont, buffer, NULL, 0, shapers);

    for (int i = 0; i < glyphCount; i++)
    {
        auto codepoint = glyph_info[i].codepoint;
        Vec2f offset(glyph_pos[i].x_offset, -glyph_pos[i].y_offset);
        float advance = glyph_pos[i].x_advance;

        if (codepoint)
        {
            shapes[i].update(&font2, codepoint, offset, advance);
        }
    }

    hb_buffer_destroy(buffer);

    // ---

    glPushMatrix();
    glTranslatef(x, y, 0);

    for (int i = 0; i < glyphCount; i++)
    {
        float advance = shapes[i].draw();
        glTranslatef(advance, 0, 0);
    }

    glPopMatrix();
}

void Application::drawHLine(float y)
{
    gl::drawLine(Vec2f(-9999, y), Vec2f(+9999, y));
}

CINDER_APP_NATIVE(Application, RendererGl(RendererGl::AA_NONE))
<|endoftext|>


<commit_before><commit_msg>loplugin:salbool<commit_after><|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: swregion.cxx,v $
 *
 *  $Revision: 1.1.1.1 $
 *
 *  last change: $Author: hr $ $Date: 2000-09-19 00:08:16 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifdef PRECOMPILED
#include "core_pch.hxx"
#endif

#pragma hdrstop

#ifndef _DEBUG_HXX //autogen
#include <tools/debug.hxx>
#endif
#include "swtypes.hxx"
#include "swrect.hxx"
#include "swregion.hxx"

SV_IMPL_VARARR( SwRects, SwRect );

/*************************************************************************
|*
|*  SwRegionRects::SwRegionRects()
|*
|*  Ersterstellung      MA 28. Oct. 92
|*  Letzte Aenderung    MA 01. Feb. 93
|*
|*************************************************************************/

SwRegionRects::SwRegionRects( const SwRect &rStartRect, USHORT nInit,
                                                        USHORT nGrow ) :
    SwRects( (BYTE)nInit, (BYTE)nGrow ),
    aOrigin( rStartRect )
{
    Insert( aOrigin, 0 );
}

/*************************************************************************
 *                      inline InsertRect()
 *
 * InsertRect() wird nur von operator-=() gerufen.
 * Wenn bDel == TRUE ist, dann wird das Rect an der Position nPos mit
 * rRect ueberschrieben, ansonsten wird rRect hinten angehaengt.
 *************************************************************************/

inline void SwRegionRects::InsertRect( const SwRect &rRect, const USHORT nPos,
                                       FASTBOOL &rDel )
{
    if( rDel )
    {
        *(pData+nPos) = rRect;
        rDel = FALSE;
    }
    else
        Insert( rRect, Count() );
}

/*************************************************************************
|*
|*  SwRegionRects::operator-=()
|*
|*  Beschreibung        Alle Ueberschneidungen der Rechtecke, die sich
|*      gerade im Array befinden, mit dem uebergebenen Rechteck werden
|*      entfernt.
|*      Dazu muessen die vorhandenen Rechtecke entweder aufgeteilt oder
|*      geloescht werden.
|*  Ersterstellung      MA 28. Oct. 92
|*  Letzte Aenderung    MA 09. Sep. 93
|*
|*************************************************************************/

void SwRegionRects::operator-=( const SwRect &rRect )
{
    USHORT nMax = Count();
    for ( USHORT i = 0; i < nMax; ++i )
    {
        if ( rRect.IsOver( *(pData+i) ) )
        {
            SwRect aTmp( *(pData+i) );
            SwRect aInter( aTmp );
            aInter._Intersection( rRect );

            // Das erste Rect, das wir inserten wollen, nimmt die
            // Stelle von i ein. So ersparen wir uns das Delete().
            FASTBOOL bDel = TRUE;

            //Jetzt aufteilen das Teil: Es sollen diejenigen Rechtecke
            //zurueckbleiben, die im alten aber nicht im neuen liegen.
            //Sprich alle Rechtecke die im alten aber nicht in der Intersection
            //liegen.
            long nTmp;
            if ( 0 < (nTmp = aInter.Top() - aTmp.Top()) )
            {
                const long nOldVal = aTmp.Height();
                aTmp.SSize().Height() = nTmp;
                InsertRect( aTmp, i, bDel );
                aTmp.Height( nOldVal );
            }

            aTmp.Top( aInter.Top() + aInter.Height() );
            if ( aTmp.Height() > 0 )
                InsertRect( aTmp, i, bDel );

            aTmp.Top( aInter.Top() );
            aTmp.Bottom( aInter.Bottom() );
            if ( 0 < (nTmp = aInter.Left() - aTmp.Left()) )
            {
                const long nOldVal = aTmp.Width();
                aTmp.Width( nTmp );
                InsertRect( aTmp, i, bDel );
                aTmp.Width( nOldVal );
            }

            aTmp.Left( aInter.Left() + aInter.Width() ); //+1?
            if ( aTmp.Width() > 0 )
                InsertRect( aTmp, i, bDel );

            if( bDel )
            {
                Remove( i );
                --i;              //Damit wir keinen uebergehen.
                --nMax;           //Damit wir keinen zuviel verarbeiten.
            }
        }
    }

}

/*************************************************************************
 *                      SwRegionRects::Invert()
 *
 * Bezugspunkt ist aOrigin, das Original-SRectangle.
 * Aus Loechern werden Flaechen, aus Flaechen werden Loecher.
 * Ein Hinweis: Wenn keine Rects abgezogen wurden, so ist das enthaltene
 * Rechteck identisch mit aOrigin. Nach Invert() besteht die Region aus
 * einem Null-SRectangle.
 *************************************************************************/

void SwRegionRects::Invert()
{
    // Nicht besonders elegant und schnell, aber wirkungsvoll:
    // Wir legen eine weitere Region an und ziehen alle Flaechen ab,
    // die in uns noch uebrig geblieben sind. Danach werden alle
    // Werte uebertragen.

    // Um unuetze Speicheranforderungen zu vermeiden versuchen wir die
    // iniale Groesse moeglichst brauchbar anzulegen:
    // Anzahl der Rechtecke in der Region * 2 + 2
    // plus zwei um den Sonderfall eines einzelnen Loches (macht vier
    // Rechtecke im inversen Fall) abzudecken.

    SwRegionRects aInvRegion( aOrigin, Count()*2+2 );
    const SwRect *pDat = GetData();
    for( USHORT i = 0; i < Count(); ++pDat, ++i )
        aInvRegion -= *pDat;

    USHORT nCpy = Count(), nDel = 0;
    if( aInvRegion.Count() < Count() )
    {
        nDel = Count() - aInvRegion.Count();
        nCpy = aInvRegion.Count();
    }
    // alle vorhandenen ueberschreiben
    memcpy( pData, aInvRegion.GetData(), nCpy * sizeof( SwRect ));

    if( nCpy < aInvRegion.Count() )
        Insert( &aInvRegion, nCpy, nCpy );
    else if( nDel )
        Remove( nCpy, nDel );
}
/*************************************************************************
|*
|*  SwRegionRects::Compress()
|*
|*  Beschreibung        Zusammenfassen von benachbarten Rechtecken.
|*  Ersterstellung      MA 16. Apr. 93
|*  Letzte Aenderung    MA 21. Apr. 93
|*
|*************************************************************************/
inline SwTwips CalcArea( const SwRect &rRect )
{
    return rRect.Width() * rRect.Height();
}

#pragma optimize("e",off)
void SwRegionRects::Compress( BOOL bFuzzy )
{
    for ( int i = 0; i < Count(); ++i )
    {
        for ( int j = i+1; j < Count(); ++j )
        {
            //Wenn zwei Rechtecke ineinanderliegen, so ist eins davon
            //uberfluessig.
            if ( (*(pData + i)).IsInside( *(pData + j) ) )
            {
                Remove( j, 1 );
                --j;
            }
            else if ( (*(pData + j)).IsInside( *(pData + i) ) )
            {
                *(pData + i) = *(pData + j);
                Remove( j, 1 );
                i = -1;
                break;
            }
            else
            {
                //Wenn zwei Rechtecke dieselbe Flaeche haben wie deren
                //Union abzueglich deren Intersection, so ist eines
                //davon ueberfluessig.
                //Um moeglichst viel zusammenzufassen und in der Folge
                //moeglichst wenig einzelne Paints zu haben darf die Flaeche
                //der Union ruhig ein bischen groesser sein
                //( 9622 * 141.5 = 1361513 ~= ein virtel Zentimeter ueber die
                //                            Breite einer DINA4 Seite)
                const long nFuzzy = bFuzzy ? 1361513 : 0;
                SwRect aUnion( *(pData + i) );aUnion.Union( *(pData + j) );
                SwRect aInter( *(pData + i) );aInter.Intersection( *(pData + j));
                if ( (::CalcArea( *(pData + i) ) +
                      ::CalcArea( *(pData + j) ) + nFuzzy) >=
                     (::CalcArea( aUnion ) - CalcArea( aInter )) )
                {
                    *(pData + i) = aUnion;
                    Remove( j, 1 );
                    i = -1;
                    break;
                }
            }
        }
    }
}
#pragma optimize("",on)

<commit_msg>INTEGRATION: CWS os8 (1.1.1.1.182); FILE MERGED 2003/04/03 07:09:36 os 1.1.1.1.182.1: #108583# precompiled headers removed<commit_after>/*************************************************************************
 *
 *  $RCSfile: swregion.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: vg $ $Date: 2003-04-17 13:43:05 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#pragma hdrstop

#ifndef _DEBUG_HXX //autogen
#include <tools/debug.hxx>
#endif
#include "swtypes.hxx"
#include "swrect.hxx"
#include "swregion.hxx"

SV_IMPL_VARARR( SwRects, SwRect );

/*************************************************************************
|*
|*  SwRegionRects::SwRegionRects()
|*
|*  Ersterstellung      MA 28. Oct. 92
|*  Letzte Aenderung    MA 01. Feb. 93
|*
|*************************************************************************/

SwRegionRects::SwRegionRects( const SwRect &rStartRect, USHORT nInit,
                                                        USHORT nGrow ) :
    SwRects( (BYTE)nInit, (BYTE)nGrow ),
    aOrigin( rStartRect )
{
    Insert( aOrigin, 0 );
}

/*************************************************************************
 *                      inline InsertRect()
 *
 * InsertRect() wird nur von operator-=() gerufen.
 * Wenn bDel == TRUE ist, dann wird das Rect an der Position nPos mit
 * rRect ueberschrieben, ansonsten wird rRect hinten angehaengt.
 *************************************************************************/

inline void SwRegionRects::InsertRect( const SwRect &rRect, const USHORT nPos,
                                       FASTBOOL &rDel )
{
    if( rDel )
    {
        *(pData+nPos) = rRect;
        rDel = FALSE;
    }
    else
        Insert( rRect, Count() );
}

/*************************************************************************
|*
|*  SwRegionRects::operator-=()
|*
|*  Beschreibung        Alle Ueberschneidungen der Rechtecke, die sich
|*      gerade im Array befinden, mit dem uebergebenen Rechteck werden
|*      entfernt.
|*      Dazu muessen die vorhandenen Rechtecke entweder aufgeteilt oder
|*      geloescht werden.
|*  Ersterstellung      MA 28. Oct. 92
|*  Letzte Aenderung    MA 09. Sep. 93
|*
|*************************************************************************/

void SwRegionRects::operator-=( const SwRect &rRect )
{
    USHORT nMax = Count();
    for ( USHORT i = 0; i < nMax; ++i )
    {
        if ( rRect.IsOver( *(pData+i) ) )
        {
            SwRect aTmp( *(pData+i) );
            SwRect aInter( aTmp );
            aInter._Intersection( rRect );

            // Das erste Rect, das wir inserten wollen, nimmt die
            // Stelle von i ein. So ersparen wir uns das Delete().
            FASTBOOL bDel = TRUE;

            //Jetzt aufteilen das Teil: Es sollen diejenigen Rechtecke
            //zurueckbleiben, die im alten aber nicht im neuen liegen.
            //Sprich alle Rechtecke die im alten aber nicht in der Intersection
            //liegen.
            long nTmp;
            if ( 0 < (nTmp = aInter.Top() - aTmp.Top()) )
            {
                const long nOldVal = aTmp.Height();
                aTmp.SSize().Height() = nTmp;
                InsertRect( aTmp, i, bDel );
                aTmp.Height( nOldVal );
            }

            aTmp.Top( aInter.Top() + aInter.Height() );
            if ( aTmp.Height() > 0 )
                InsertRect( aTmp, i, bDel );

            aTmp.Top( aInter.Top() );
            aTmp.Bottom( aInter.Bottom() );
            if ( 0 < (nTmp = aInter.Left() - aTmp.Left()) )
            {
                const long nOldVal = aTmp.Width();
                aTmp.Width( nTmp );
                InsertRect( aTmp, i, bDel );
                aTmp.Width( nOldVal );
            }

            aTmp.Left( aInter.Left() + aInter.Width() ); //+1?
            if ( aTmp.Width() > 0 )
                InsertRect( aTmp, i, bDel );

            if( bDel )
            {
                Remove( i );
                --i;              //Damit wir keinen uebergehen.
                --nMax;           //Damit wir keinen zuviel verarbeiten.
            }
        }
    }

}

/*************************************************************************
 *                      SwRegionRects::Invert()
 *
 * Bezugspunkt ist aOrigin, das Original-SRectangle.
 * Aus Loechern werden Flaechen, aus Flaechen werden Loecher.
 * Ein Hinweis: Wenn keine Rects abgezogen wurden, so ist das enthaltene
 * Rechteck identisch mit aOrigin. Nach Invert() besteht die Region aus
 * einem Null-SRectangle.
 *************************************************************************/

void SwRegionRects::Invert()
{
    // Nicht besonders elegant und schnell, aber wirkungsvoll:
    // Wir legen eine weitere Region an und ziehen alle Flaechen ab,
    // die in uns noch uebrig geblieben sind. Danach werden alle
    // Werte uebertragen.

    // Um unuetze Speicheranforderungen zu vermeiden versuchen wir die
    // iniale Groesse moeglichst brauchbar anzulegen:
    // Anzahl der Rechtecke in der Region * 2 + 2
    // plus zwei um den Sonderfall eines einzelnen Loches (macht vier
    // Rechtecke im inversen Fall) abzudecken.

    SwRegionRects aInvRegion( aOrigin, Count()*2+2 );
    const SwRect *pDat = GetData();
    for( USHORT i = 0; i < Count(); ++pDat, ++i )
        aInvRegion -= *pDat;

    USHORT nCpy = Count(), nDel = 0;
    if( aInvRegion.Count() < Count() )
    {
        nDel = Count() - aInvRegion.Count();
        nCpy = aInvRegion.Count();
    }
    // alle vorhandenen ueberschreiben
    memcpy( pData, aInvRegion.GetData(), nCpy * sizeof( SwRect ));

    if( nCpy < aInvRegion.Count() )
        Insert( &aInvRegion, nCpy, nCpy );
    else if( nDel )
        Remove( nCpy, nDel );
}
/*************************************************************************
|*
|*  SwRegionRects::Compress()
|*
|*  Beschreibung        Zusammenfassen von benachbarten Rechtecken.
|*  Ersterstellung      MA 16. Apr. 93
|*  Letzte Aenderung    MA 21. Apr. 93
|*
|*************************************************************************/
inline SwTwips CalcArea( const SwRect &rRect )
{
    return rRect.Width() * rRect.Height();
}

#pragma optimize("e",off)
void SwRegionRects::Compress( BOOL bFuzzy )
{
    for ( int i = 0; i < Count(); ++i )
    {
        for ( int j = i+1; j < Count(); ++j )
        {
            //Wenn zwei Rechtecke ineinanderliegen, so ist eins davon
            //uberfluessig.
            if ( (*(pData + i)).IsInside( *(pData + j) ) )
            {
                Remove( j, 1 );
                --j;
            }
            else if ( (*(pData + j)).IsInside( *(pData + i) ) )
            {
                *(pData + i) = *(pData + j);
                Remove( j, 1 );
                i = -1;
                break;
            }
            else
            {
                //Wenn zwei Rechtecke dieselbe Flaeche haben wie deren
                //Union abzueglich deren Intersection, so ist eines
                //davon ueberfluessig.
                //Um moeglichst viel zusammenzufassen und in der Folge
                //moeglichst wenig einzelne Paints zu haben darf die Flaeche
                //der Union ruhig ein bischen groesser sein
                //( 9622 * 141.5 = 1361513 ~= ein virtel Zentimeter ueber die
                //                            Breite einer DINA4 Seite)
                const long nFuzzy = bFuzzy ? 1361513 : 0;
                SwRect aUnion( *(pData + i) );aUnion.Union( *(pData + j) );
                SwRect aInter( *(pData + i) );aInter.Intersection( *(pData + j));
                if ( (::CalcArea( *(pData + i) ) +
                      ::CalcArea( *(pData + j) ) + nFuzzy) >=
                     (::CalcArea( aUnion ) - CalcArea( aInter )) )
                {
                    *(pData + i) = aUnion;
                    Remove( j, 1 );
                    i = -1;
                    break;
                }
            }
        }
    }
}
#pragma optimize("",on)

<|endoftext|>


<commit_before>#include "taichi/cache/gfx/cache_manager.h"
#include "taichi/analysis/offline_cache_util.h"
#include "taichi/codegen/spirv/snode_struct_compiler.h"
#include "taichi/common/cleanup.h"
#include "taichi/common/version.h"
#include "taichi/program/kernel.h"
#include "taichi/runtime/gfx/aot_module_loader_impl.h"
#include "taichi/runtime/gfx/snode_tree_manager.h"
#include "taichi/util/lock.h"
#include "taichi/util/offline_cache.h"

namespace taichi::lang {

namespace {

constexpr char kMetadataFileLockName[] = "metadata.lock";
constexpr char kAotMetadataFilename[] = "metadata.tcb";
constexpr char kDebuggingAotMetadataFilename[] = "metadata.json";
constexpr char kGraphMetadataFilename[] = "graphs.tcb";
constexpr char kOfflineCacheMetadataFilename[] = "offline_cache_metadata.tcb";
using CompiledKernelData = gfx::GfxRuntime::RegisterParams;

inline gfx::CacheManager::Metadata::KernelMetadata make_kernel_metadata(
    const std::string &key,
    const gfx::GfxRuntime::RegisterParams &compiled) {
  std::size_t codes_size = 0;
  for (const auto &e : compiled.task_spirv_source_codes) {
    codes_size += e.size() * sizeof(*e.data());
  }

  gfx::CacheManager::Metadata::KernelMetadata res;
  res.kernel_key = key;
  res.size = codes_size;
  res.created_at = std::time(nullptr);
  res.last_used_at = std::time(nullptr);
  res.num_files = compiled.task_spirv_source_codes.size();
  return res;
}

}  // namespace

namespace offline_cache {

template <>
struct CacheCleanerUtils<gfx::CacheManager::Metadata> {
  using MetadataType = gfx::CacheManager::Metadata;
  using KernelMetaData = MetadataType::KernelMetadata;

  // To save metadata as file
  static bool save_metadata(const CacheCleanerConfig &config,
                            const MetadataType &data) {
    // Update AOT metadata
    gfx::TaichiAotData old_aot_data, new_aot_data;
    auto aot_metadata_path =
        taichi::join_path(config.path, kAotMetadataFilename);
    if (read_from_binary_file(old_aot_data, aot_metadata_path)) {
      const auto &kernels = data.kernels;
      for (auto &k : old_aot_data.kernels) {
        if (kernels.count(k.name)) {
          new_aot_data.kernels.push_back(std::move(k));
        }
      }
      write_to_binary_file(new_aot_data, aot_metadata_path);
    }
    write_to_binary_file(
        data, taichi::join_path(config.path, config.metadata_filename));
    return true;
  }

  static bool save_debugging_metadata(const CacheCleanerConfig &config,
                                      const MetadataType &data) {
    // Do nothing
    return true;
  }

  // To get cache files name
  static std::vector<std::string> get_cache_files(
      const CacheCleanerConfig &config,
      const KernelMetaData &kernel_meta) {
    std::vector<std::string> result;
    for (std::size_t i = 0; i < kernel_meta.num_files; ++i) {
      result.push_back(kernel_meta.kernel_key + std::to_string(i) + ".spv");
    }
    return result;
  }

  // To remove other files except cache files and offline cache metadta files
  static void remove_other_files(const CacheCleanerConfig &config) {
    taichi::remove(taichi::join_path(config.path, kAotMetadataFilename));
    taichi::remove(
        taichi::join_path(config.path, kDebuggingAotMetadataFilename));
    taichi::remove(taichi::join_path(config.path, kGraphMetadataFilename));
  }

  // To check if a file is cache file
  static bool is_valid_cache_file(const CacheCleanerConfig &config,
                                  const std::string &name) {
    return filename_extension(name) == "spv";
  }
};

}  // namespace offline_cache

namespace gfx {

CacheManager::CacheManager(Params &&init_params)
    : mode_(init_params.mode),
      runtime_(init_params.runtime),
      compiled_structs_(*init_params.compiled_structs) {
  TI_ASSERT(init_params.runtime);
  TI_ASSERT(init_params.target_device);

  path_ = offline_cache::get_cache_path_by_arch(init_params.cache_path,
                                                init_params.arch);

  if (taichi::path_exists(taichi::join_path(path_, kAotMetadataFilename)) &&
      taichi::path_exists(taichi::join_path(path_, kGraphMetadataFilename))) {
    auto lock_path = taichi::join_path(path_, kMetadataFileLockName);
    if (lock_with_file(lock_path)) {
      auto _ = make_cleanup([&lock_path]() {
        if (!unlock_with_file(lock_path)) {
          TI_WARN("Unlock {} failed", lock_path);
        }
      });
      gfx::AotModuleParams params;
      params.module_path = path_;
      params.runtime = runtime_;
      params.enable_lazy_loading = true;
      cached_module_ = gfx::make_aot_module(params, init_params.arch);
    }
  }

  caching_module_builder_ = std::make_unique<gfx::AotModuleBuilderImpl>(
      compiled_structs_, init_params.arch,
      std::move(init_params.target_device));

  offline_cache_metadata_.version[0] = TI_VERSION_MAJOR;
  offline_cache_metadata_.version[1] = TI_VERSION_MINOR;
  offline_cache_metadata_.version[2] = TI_VERSION_PATCH;
}

CompiledKernelData CacheManager::load_or_compile(CompileConfig *config,
                                                 Kernel *kernel) {
  if (kernel->is_evaluator) {
    spirv::lower(kernel);
    return gfx::run_codegen(kernel, runtime_->get_ti_device(),
                            compiled_structs_);
  }
  std::string kernel_key = make_kernel_key(config, kernel);
  if (mode_ > NotCache) {
    if (auto opt = this->try_load_cached_kernel(kernel, kernel_key)) {
      return *opt;
    }
  }
  return this->compile_and_cache_kernel(kernel_key, kernel);
}

void CacheManager::dump_with_merging() const {
  if (mode_ == MemAndDiskCache && !offline_cache_metadata_.kernels.empty()) {
    taichi::create_directories(path_);
    auto *cache_builder =
        static_cast<gfx::AotModuleBuilderImpl *>(caching_module_builder_.get());
    cache_builder->mangle_aot_data();

    auto lock_path = taichi::join_path(path_, kMetadataFileLockName);
    if (lock_with_file(lock_path)) {
      auto _ = make_cleanup([&lock_path]() {
        if (!unlock_with_file(lock_path)) {
          TI_WARN("Unlock {} failed", lock_path);
        }
      });

      // Update metadata.{tcb,json}
      cache_builder->merge_with_old_meta_data(path_);
      cache_builder->dump(path_, "");

      // Update offline_cache_metadata.tcb
      using offline_cache::load_metadata_with_checking;
      using Error = offline_cache::LoadMetadataError;
      Metadata old_data;
      const auto filename =
          taichi::join_path(path_, kOfflineCacheMetadataFilename);
      if (load_metadata_with_checking(old_data, filename) == Error::kNoError) {
        for (auto &[k, v] : offline_cache_metadata_.kernels) {
          auto iter = old_data.kernels.find(k);
          if (iter != old_data.kernels.end()) {  // Update
            iter->second.last_used_at = v.last_used_at;
          } else {  // Add new
            old_data.size += v.size;
            old_data.kernels[k] = std::move(v);
          }
        }
        write_to_binary_file(old_data, filename);
      } else {
        write_to_binary_file(offline_cache_metadata_, filename);
      }
    }
  }
}

void CacheManager::clean_offline_cache(offline_cache::CleanCachePolicy policy,
                                       int max_bytes,
                                       double cleaning_factor) const {
  if (mode_ == MemAndDiskCache) {
    using CacheCleaner = offline_cache::CacheCleaner<Metadata>;
    offline_cache::CacheCleanerConfig params;
    params.path = path_;
    params.policy = policy;
    params.cleaning_factor = cleaning_factor;
    params.max_size = max_bytes;
    params.metadata_filename = kOfflineCacheMetadataFilename;
    params.debugging_metadata_filename = "";  // No debugging file
    params.metadata_lock_name = kMetadataFileLockName;
    CacheCleaner::run(params);
  }
}

std::optional<CompiledKernelData> CacheManager::try_load_cached_kernel(
    Kernel *kernel,
    const std::string &key) {
  if (mode_ == NotCache) {
    return std::nullopt;
  }
  // Find in memory-cache
  auto *cache_builder =
      static_cast<gfx::AotModuleBuilderImpl *>(caching_module_builder_.get());
  auto params_opt = cache_builder->try_get_kernel_register_params(key);
  if (params_opt.has_value()) {
    TI_DEBUG("Create kernel '{}' from in-memory cache (key='{}')",
             kernel->get_name(), key);
    kernel->mark_as_from_cache();
    // TODO: Support multiple SNodeTrees in AOT.
    params_opt->num_snode_trees = compiled_structs_.size();
    return params_opt;
  }
  // Find in disk-cache
  if (mode_ == MemAndDiskCache && cached_module_) {
    if (auto *aot_kernel = cached_module_->get_kernel(key)) {
      TI_DEBUG("Create kernel '{}' from cache (key='{}')", kernel->get_name(),
               key);
      kernel->mark_as_from_cache();
      auto *aot_kernel_impl = static_cast<gfx::KernelImpl *>(aot_kernel);
      auto compiled = aot_kernel_impl->params();
      // TODO: Support multiple SNodeTrees in AOT.
      compiled.num_snode_trees = compiled_structs_.size();
      auto kmetadata = make_kernel_metadata(key, compiled);
      offline_cache_metadata_.size += kmetadata.size;
      offline_cache_metadata_.kernels[key] = std::move(kmetadata);
      return compiled;
    }
  }
  return std::nullopt;
}

CompiledKernelData CacheManager::compile_and_cache_kernel(
    const std::string &key,
    Kernel *kernel) {
  TI_DEBUG_IF(mode_ == MemAndDiskCache, "Cache kernel '{}' (key='{}')",
              kernel->get_name(), key);
  auto *cache_builder =
      static_cast<gfx::AotModuleBuilderImpl *>(caching_module_builder_.get());
  TI_ASSERT(cache_builder != nullptr);
  cache_builder->add(key, kernel);
  auto params_opt = cache_builder->try_get_kernel_register_params(key);
  TI_ASSERT(params_opt.has_value());
  // TODO: Support multiple SNodeTrees in AOT.
  params_opt->num_snode_trees = compiled_structs_.size();
  auto kmetadata = make_kernel_metadata(key, *params_opt);
  offline_cache_metadata_.size += kmetadata.size;
  offline_cache_metadata_.kernels[key] = std::move(kmetadata);
  return *params_opt;
}

std::string CacheManager::make_kernel_key(CompileConfig *config,
                                          Kernel *kernel) const {
  if (mode_ < MemAndDiskCache) {
    return kernel->get_name();
  }
  auto key = kernel->get_cached_kernel_key();
  if (key.empty()) {
    key = get_hashed_offline_cache_key(config, kernel);
    kernel->set_kernel_key_for_cache(key);
  }
  return key;
}

}  // namespace gfx
}  // namespace taichi::lang
<commit_msg>[bug] Fix crashing when loading old offline cache files (for gfx backends) (#6157)<commit_after>#include "taichi/cache/gfx/cache_manager.h"
#include "taichi/analysis/offline_cache_util.h"
#include "taichi/codegen/spirv/snode_struct_compiler.h"
#include "taichi/common/cleanup.h"
#include "taichi/common/version.h"
#include "taichi/program/kernel.h"
#include "taichi/runtime/gfx/aot_module_loader_impl.h"
#include "taichi/runtime/gfx/snode_tree_manager.h"
#include "taichi/util/lock.h"
#include "taichi/util/offline_cache.h"

namespace taichi::lang {

namespace {

constexpr char kMetadataFileLockName[] = "metadata.lock";
constexpr char kAotMetadataFilename[] = "metadata.tcb";
constexpr char kDebuggingAotMetadataFilename[] = "metadata.json";
constexpr char kGraphMetadataFilename[] = "graphs.tcb";
constexpr char kOfflineCacheMetadataFilename[] = "offline_cache_metadata.tcb";
using CompiledKernelData = gfx::GfxRuntime::RegisterParams;

inline gfx::CacheManager::Metadata::KernelMetadata make_kernel_metadata(
    const std::string &key,
    const gfx::GfxRuntime::RegisterParams &compiled) {
  std::size_t codes_size = 0;
  for (const auto &e : compiled.task_spirv_source_codes) {
    codes_size += e.size() * sizeof(*e.data());
  }

  gfx::CacheManager::Metadata::KernelMetadata res;
  res.kernel_key = key;
  res.size = codes_size;
  res.created_at = std::time(nullptr);
  res.last_used_at = std::time(nullptr);
  res.num_files = compiled.task_spirv_source_codes.size();
  return res;
}

}  // namespace

namespace offline_cache {

template <>
struct CacheCleanerUtils<gfx::CacheManager::Metadata> {
  using MetadataType = gfx::CacheManager::Metadata;
  using KernelMetaData = MetadataType::KernelMetadata;

  // To save metadata as file
  static bool save_metadata(const CacheCleanerConfig &config,
                            const MetadataType &data) {
    // Update AOT metadata
    gfx::TaichiAotData old_aot_data, new_aot_data;
    auto aot_metadata_path =
        taichi::join_path(config.path, kAotMetadataFilename);
    if (read_from_binary_file(old_aot_data, aot_metadata_path)) {
      const auto &kernels = data.kernels;
      for (auto &k : old_aot_data.kernels) {
        if (kernels.count(k.name)) {
          new_aot_data.kernels.push_back(std::move(k));
        }
      }
      write_to_binary_file(new_aot_data, aot_metadata_path);
    }
    write_to_binary_file(
        data, taichi::join_path(config.path, config.metadata_filename));
    return true;
  }

  static bool save_debugging_metadata(const CacheCleanerConfig &config,
                                      const MetadataType &data) {
    // Do nothing
    return true;
  }

  // To get cache files name
  static std::vector<std::string> get_cache_files(
      const CacheCleanerConfig &config,
      const KernelMetaData &kernel_meta) {
    std::vector<std::string> result;
    for (std::size_t i = 0; i < kernel_meta.num_files; ++i) {
      result.push_back(kernel_meta.kernel_key + std::to_string(i) + ".spv");
    }
    return result;
  }

  // To remove other files except cache files and offline cache metadta files
  static void remove_other_files(const CacheCleanerConfig &config) {
    taichi::remove(taichi::join_path(config.path, kAotMetadataFilename));
    taichi::remove(
        taichi::join_path(config.path, kDebuggingAotMetadataFilename));
    taichi::remove(taichi::join_path(config.path, kGraphMetadataFilename));
  }

  // To check if a file is cache file
  static bool is_valid_cache_file(const CacheCleanerConfig &config,
                                  const std::string &name) {
    return filename_extension(name) == "spv";
  }
};

}  // namespace offline_cache

namespace gfx {

CacheManager::CacheManager(Params &&init_params)
    : mode_(init_params.mode),
      runtime_(init_params.runtime),
      compiled_structs_(*init_params.compiled_structs) {
  TI_ASSERT(init_params.runtime);
  TI_ASSERT(init_params.target_device);

  path_ = offline_cache::get_cache_path_by_arch(init_params.cache_path,
                                                init_params.arch);
  {  // Load cached module with checking
    using Error = offline_cache::LoadMetadataError;
    using offline_cache::load_metadata_with_checking;
    Metadata tmp;
    auto filepath = taichi::join_path(path_, kOfflineCacheMetadataFilename);
    if (load_metadata_with_checking(tmp, filepath) == Error::kNoError) {
      auto lock_path = taichi::join_path(path_, kMetadataFileLockName);
      auto exists =
          taichi::path_exists(taichi::join_path(path_, kAotMetadataFilename)) &&
          taichi::path_exists(taichi::join_path(path_, kGraphMetadataFilename));
      if (exists && lock_with_file(lock_path)) {
        auto _ = make_cleanup([&lock_path]() {
          if (!unlock_with_file(lock_path)) {
            TI_WARN("Unlock {} failed", lock_path);
          }
        });
        gfx::AotModuleParams params;
        params.module_path = path_;
        params.runtime = runtime_;
        params.enable_lazy_loading = true;
        cached_module_ = gfx::make_aot_module(params, init_params.arch);
      }
    }
  }

  caching_module_builder_ = std::make_unique<gfx::AotModuleBuilderImpl>(
      compiled_structs_, init_params.arch,
      std::move(init_params.target_device));

  offline_cache_metadata_.version[0] = TI_VERSION_MAJOR;
  offline_cache_metadata_.version[1] = TI_VERSION_MINOR;
  offline_cache_metadata_.version[2] = TI_VERSION_PATCH;
}

CompiledKernelData CacheManager::load_or_compile(CompileConfig *config,
                                                 Kernel *kernel) {
  if (kernel->is_evaluator) {
    spirv::lower(kernel);
    return gfx::run_codegen(kernel, runtime_->get_ti_device(),
                            compiled_structs_);
  }
  std::string kernel_key = make_kernel_key(config, kernel);
  if (mode_ > NotCache) {
    if (auto opt = this->try_load_cached_kernel(kernel, kernel_key)) {
      return *opt;
    }
  }
  return this->compile_and_cache_kernel(kernel_key, kernel);
}

void CacheManager::dump_with_merging() const {
  if (mode_ == MemAndDiskCache && !offline_cache_metadata_.kernels.empty()) {
    taichi::create_directories(path_);
    auto *cache_builder =
        static_cast<gfx::AotModuleBuilderImpl *>(caching_module_builder_.get());
    cache_builder->mangle_aot_data();

    auto lock_path = taichi::join_path(path_, kMetadataFileLockName);
    if (lock_with_file(lock_path)) {
      auto _ = make_cleanup([&lock_path]() {
        if (!unlock_with_file(lock_path)) {
          TI_WARN("Unlock {} failed", lock_path);
        }
      });

      // Update metadata.{tcb,json}
      cache_builder->merge_with_old_meta_data(path_);
      cache_builder->dump(path_, "");

      // Update offline_cache_metadata.tcb
      using offline_cache::load_metadata_with_checking;
      using Error = offline_cache::LoadMetadataError;
      Metadata old_data;
      const auto filename =
          taichi::join_path(path_, kOfflineCacheMetadataFilename);
      if (load_metadata_with_checking(old_data, filename) == Error::kNoError) {
        for (auto &[k, v] : offline_cache_metadata_.kernels) {
          auto iter = old_data.kernels.find(k);
          if (iter != old_data.kernels.end()) {  // Update
            iter->second.last_used_at = v.last_used_at;
          } else {  // Add new
            old_data.size += v.size;
            old_data.kernels[k] = std::move(v);
          }
        }
        write_to_binary_file(old_data, filename);
      } else {
        write_to_binary_file(offline_cache_metadata_, filename);
      }
    }
  }
}

void CacheManager::clean_offline_cache(offline_cache::CleanCachePolicy policy,
                                       int max_bytes,
                                       double cleaning_factor) const {
  if (mode_ == MemAndDiskCache) {
    using CacheCleaner = offline_cache::CacheCleaner<Metadata>;
    offline_cache::CacheCleanerConfig params;
    params.path = path_;
    params.policy = policy;
    params.cleaning_factor = cleaning_factor;
    params.max_size = max_bytes;
    params.metadata_filename = kOfflineCacheMetadataFilename;
    params.debugging_metadata_filename = "";  // No debugging file
    params.metadata_lock_name = kMetadataFileLockName;
    CacheCleaner::run(params);
  }
}

std::optional<CompiledKernelData> CacheManager::try_load_cached_kernel(
    Kernel *kernel,
    const std::string &key) {
  if (mode_ == NotCache) {
    return std::nullopt;
  }
  // Find in memory-cache
  auto *cache_builder =
      static_cast<gfx::AotModuleBuilderImpl *>(caching_module_builder_.get());
  auto params_opt = cache_builder->try_get_kernel_register_params(key);
  if (params_opt.has_value()) {
    TI_DEBUG("Create kernel '{}' from in-memory cache (key='{}')",
             kernel->get_name(), key);
    kernel->mark_as_from_cache();
    // TODO: Support multiple SNodeTrees in AOT.
    params_opt->num_snode_trees = compiled_structs_.size();
    return params_opt;
  }
  // Find in disk-cache
  if (mode_ == MemAndDiskCache && cached_module_) {
    if (auto *aot_kernel = cached_module_->get_kernel(key)) {
      TI_DEBUG("Create kernel '{}' from cache (key='{}')", kernel->get_name(),
               key);
      kernel->mark_as_from_cache();
      auto *aot_kernel_impl = static_cast<gfx::KernelImpl *>(aot_kernel);
      auto compiled = aot_kernel_impl->params();
      // TODO: Support multiple SNodeTrees in AOT.
      compiled.num_snode_trees = compiled_structs_.size();
      auto kmetadata = make_kernel_metadata(key, compiled);
      offline_cache_metadata_.size += kmetadata.size;
      offline_cache_metadata_.kernels[key] = std::move(kmetadata);
      return compiled;
    }
  }
  return std::nullopt;
}

CompiledKernelData CacheManager::compile_and_cache_kernel(
    const std::string &key,
    Kernel *kernel) {
  TI_DEBUG_IF(mode_ == MemAndDiskCache, "Cache kernel '{}' (key='{}')",
              kernel->get_name(), key);
  auto *cache_builder =
      static_cast<gfx::AotModuleBuilderImpl *>(caching_module_builder_.get());
  TI_ASSERT(cache_builder != nullptr);
  cache_builder->add(key, kernel);
  auto params_opt = cache_builder->try_get_kernel_register_params(key);
  TI_ASSERT(params_opt.has_value());
  // TODO: Support multiple SNodeTrees in AOT.
  params_opt->num_snode_trees = compiled_structs_.size();
  auto kmetadata = make_kernel_metadata(key, *params_opt);
  offline_cache_metadata_.size += kmetadata.size;
  offline_cache_metadata_.kernels[key] = std::move(kmetadata);
  return *params_opt;
}

std::string CacheManager::make_kernel_key(CompileConfig *config,
                                          Kernel *kernel) const {
  if (mode_ < MemAndDiskCache) {
    return kernel->get_name();
  }
  auto key = kernel->get_cached_kernel_key();
  if (key.empty()) {
    key = get_hashed_offline_cache_key(config, kernel);
    kernel->set_kernel_key_for_cache(key);
  }
  return key;
}

}  // namespace gfx
}  // namespace taichi::lang
<|endoftext|>


<commit_before>/*
 * libjingle
 * Copyright 2014 Google Inc.
 *
 * 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.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

#if defined(HAVE_SRTP) && defined(ENABLE_EXTERNAL_AUTH)

#include "talk/session/media/externalhmac.h"

#include <stdlib.h>  // For malloc/free.

#ifdef SRTP_RELATIVE_PATH
#include "srtp.h"  // NOLINT
#else
#include "third_party/libsrtp/include/srtp.h"
#endif  // SRTP_RELATIVE_PATH

#include "talk/base/logging.h"

// The debug module for authentiation
debug_module_t mod_external_hmac = {
  0,                            // Debugging is off by default
  (char*)"external-hmac-sha-1"  // Printable name for module
};

extern auth_type_t external_hmac;

// Begin test case 0 */
uint8_t
external_hmac_test_case_0_key[20] = {
  0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
  0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
  0x0b, 0x0b, 0x0b, 0x0b
};

uint8_t
external_hmac_test_case_0_data[8] = {
  0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65   // "Hi There"
};

uint8_t
external_hmac_fake_tag[10] = {
  0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd
};

auth_test_case_t
external_hmac_test_case_0 = {
  20,                                // Octets in key
  external_hmac_test_case_0_key,     // Key
  8,                                 // Octets in data
  external_hmac_test_case_0_data,    // Data
  10,                                // Octets in tag
  external_hmac_fake_tag,            // Tag
  NULL                               // Pointer to next testcase
};

err_status_t
external_hmac_alloc(auth_t** a, int key_len, int out_len) {
  uint8_t* pointer;

  // Check key length - note that we don't support keys larger
  // than 20 bytes yet
  if (key_len > 20)
    return err_status_bad_param;

  // Check output length - should be less than 20 bytes/
  if (out_len > 20)
    return err_status_bad_param;

  // Allocate memory for auth and hmac_ctx_t structures.
  pointer = reinterpret_cast<uint8_t*>(
      malloc(sizeof(external_hmac_ctx_t) + sizeof(auth_t)));
  if (pointer == NULL)
    return err_status_alloc_fail;

  // Set pointers
  *a = (auth_t *)pointer;
  (*a)->type = &external_hmac;
  (*a)->state = pointer + sizeof(auth_t);
  (*a)->out_len = out_len;
  (*a)->key_len = key_len;
  (*a)->prefix_len = 0;

  // Increment global count of all hmac uses.
  external_hmac.ref_count++;

  return err_status_ok;
}

err_status_t
external_hmac_dealloc(auth_t* a) {
  // Zeroize entire state
  memset((uint8_t *)a, sizeof(external_hmac_ctx_t) + sizeof(auth_t));

  // Free memory
  free(a);

  // Decrement global count of all hmac uses.
  external_hmac.ref_count--;

  return err_status_ok;
}

err_status_t
external_hmac_init(external_hmac_ctx_t* state,
                   const uint8_t* key, int key_len) {
  if (key_len > HMAC_KEY_LENGTH)
    return err_status_bad_param;

  memset(state->key, 0, key_len);
  memcpy(state->key, key, key_len);
  state->key_length = key_len;
  return err_status_ok;
}

err_status_t
external_hmac_start(external_hmac_ctx_t* state) {
  return err_status_ok;
}

err_status_t
external_hmac_update(external_hmac_ctx_t* state, const uint8_t* message,
                     int msg_octets) {
  return err_status_ok;
}

err_status_t
external_hmac_compute(external_hmac_ctx_t* state, const void* message,
                      int msg_octets, int tag_len, uint8_t* result) {
  memcpy(result, external_hmac_fake_tag, tag_len);
  return err_status_ok;
}

char external_hmac_description[] = "external hmac sha-1 authentication";

 // auth_type_t external_hmac is the hmac metaobject

auth_type_t
external_hmac  = {
  (auth_alloc_func)      external_hmac_alloc,
  (auth_dealloc_func)    external_hmac_dealloc,
  (auth_init_func)       external_hmac_init,
  (auth_compute_func)    external_hmac_compute,
  (auth_update_func)     external_hmac_update,
  (auth_start_func)      external_hmac_start,
  (char *)               external_hmac_description,
  (int)                  0,  /* instance count */
  (auth_test_case_t *)   &external_hmac_test_case_0,
  (debug_module_t *)     &mod_external_hmac,
  (auth_type_id_t)       EXTERNAL_HMAC_SHA1
};

err_status_t
external_crypto_init() {
  err_status_t status = crypto_kernel_replace_auth_type(
      &external_hmac, EXTERNAL_HMAC_SHA1);
  if (status) {
    LOG(LS_ERROR) << "Error in replacing default auth module, error: "
                  << status;
    return err_status_fail;
  }
  return err_status_ok;
}

#endif  // defined(HAVE_SRTP) && defined(ENABLE_EXTERNAL_AUTH)
<commit_msg>Fixing incorrect memset.<commit_after>/*
 * libjingle
 * Copyright 2014 Google Inc.
 *
 * 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.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

#if defined(HAVE_SRTP) && defined(ENABLE_EXTERNAL_AUTH)

#include "talk/session/media/externalhmac.h"

#include <stdlib.h>  // For malloc/free.

#ifdef SRTP_RELATIVE_PATH
#include "srtp.h"  // NOLINT
#else
#include "third_party/libsrtp/include/srtp.h"
#endif  // SRTP_RELATIVE_PATH

#include "talk/base/logging.h"

// The debug module for authentiation
debug_module_t mod_external_hmac = {
  0,                            // Debugging is off by default
  (char*)"external-hmac-sha-1"  // Printable name for module
};

extern auth_type_t external_hmac;

// Begin test case 0 */
uint8_t
external_hmac_test_case_0_key[20] = {
  0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
  0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
  0x0b, 0x0b, 0x0b, 0x0b
};

uint8_t
external_hmac_test_case_0_data[8] = {
  0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65   // "Hi There"
};

uint8_t
external_hmac_fake_tag[10] = {
  0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd
};

auth_test_case_t
external_hmac_test_case_0 = {
  20,                                // Octets in key
  external_hmac_test_case_0_key,     // Key
  8,                                 // Octets in data
  external_hmac_test_case_0_data,    // Data
  10,                                // Octets in tag
  external_hmac_fake_tag,            // Tag
  NULL                               // Pointer to next testcase
};

err_status_t
external_hmac_alloc(auth_t** a, int key_len, int out_len) {
  uint8_t* pointer;

  // Check key length - note that we don't support keys larger
  // than 20 bytes yet
  if (key_len > 20)
    return err_status_bad_param;

  // Check output length - should be less than 20 bytes/
  if (out_len > 20)
    return err_status_bad_param;

  // Allocate memory for auth and hmac_ctx_t structures.
  pointer = reinterpret_cast<uint8_t*>(
      malloc(sizeof(external_hmac_ctx_t) + sizeof(auth_t)));
  if (pointer == NULL)
    return err_status_alloc_fail;

  // Set pointers
  *a = (auth_t *)pointer;
  (*a)->type = &external_hmac;
  (*a)->state = pointer + sizeof(auth_t);
  (*a)->out_len = out_len;
  (*a)->key_len = key_len;
  (*a)->prefix_len = 0;

  // Increment global count of all hmac uses.
  external_hmac.ref_count++;

  return err_status_ok;
}

err_status_t
external_hmac_dealloc(auth_t* a) {
  // Zeroize entire state
  memset((uint8_t *)a, 0, sizeof(external_hmac_ctx_t) + sizeof(auth_t));

  // Free memory
  free(a);

  // Decrement global count of all hmac uses.
  external_hmac.ref_count--;

  return err_status_ok;
}

err_status_t
external_hmac_init(external_hmac_ctx_t* state,
                   const uint8_t* key, int key_len) {
  if (key_len > HMAC_KEY_LENGTH)
    return err_status_bad_param;

  memset(state->key, 0, key_len);
  memcpy(state->key, key, key_len);
  state->key_length = key_len;
  return err_status_ok;
}

err_status_t
external_hmac_start(external_hmac_ctx_t* state) {
  return err_status_ok;
}

err_status_t
external_hmac_update(external_hmac_ctx_t* state, const uint8_t* message,
                     int msg_octets) {
  return err_status_ok;
}

err_status_t
external_hmac_compute(external_hmac_ctx_t* state, const void* message,
                      int msg_octets, int tag_len, uint8_t* result) {
  memcpy(result, external_hmac_fake_tag, tag_len);
  return err_status_ok;
}

char external_hmac_description[] = "external hmac sha-1 authentication";

 // auth_type_t external_hmac is the hmac metaobject

auth_type_t
external_hmac  = {
  (auth_alloc_func)      external_hmac_alloc,
  (auth_dealloc_func)    external_hmac_dealloc,
  (auth_init_func)       external_hmac_init,
  (auth_compute_func)    external_hmac_compute,
  (auth_update_func)     external_hmac_update,
  (auth_start_func)      external_hmac_start,
  (char *)               external_hmac_description,
  (int)                  0,  /* instance count */
  (auth_test_case_t *)   &external_hmac_test_case_0,
  (debug_module_t *)     &mod_external_hmac,
  (auth_type_id_t)       EXTERNAL_HMAC_SHA1
};

err_status_t
external_crypto_init() {
  err_status_t status = crypto_kernel_replace_auth_type(
      &external_hmac, EXTERNAL_HMAC_SHA1);
  if (status) {
    LOG(LS_ERROR) << "Error in replacing default auth module, error: "
                  << status;
    return err_status_fail;
  }
  return err_status_ok;
}

#endif  // defined(HAVE_SRTP) && defined(ENABLE_EXTERNAL_AUTH)
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/common/math/quaternion.h"

#include <cmath>

#include "gtest/gtest.h"

namespace apollo {
namespace common {
namespace math {

TEST(QuaternionTest, QuaternionToHeading) {
  constexpr double v = sqrt(0.5);  // = cos(pi / 4) = sin(pi / 4)
  EXPECT_DOUBLE_EQ(0,
                   QuaternionToHeading(v, 0.0, 0.0, -v));  // Pointing to east.
  EXPECT_DOUBLE_EQ(
      M_PI_2, QuaternionToHeading(1.0, 0.0, 0.0, 0.0));  // Pointing to north.
  EXPECT_DOUBLE_EQ(-M_PI_2, QuaternionToHeading(0.0, 0.0, 0.0,
                                                1.0));  // Pointing to south.
  EXPECT_DOUBLE_EQ(-M_PI,
                   QuaternionToHeading(v, 0.0, 0.0, v));  // Pointing to west.

  Eigen::Quaternionf q(1.0, 0.0, 0.0, 0.0);
  EXPECT_DOUBLE_EQ(M_PI_2, QuaternionToHeading(q));  // Pointing to north.

  const double headings[] = {-3.3, -2.2, -1.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7};
  for (double heading : headings) {
    EXPECT_NEAR(NormalizeAngle(heading),
                QuaternionToHeading(HeadingToQuaternion<double>(heading)),
                1e-15);
  }
  {
    apollo::common::Quaternion q;
    q.set_qx(0.016590540978116377);
    q.set_qy(0.012968083311103572);
    q.set_qz(-0.99256254167039326);
    q.set_qw(-0.1199007240933047);
    Eigen::Vector3d rotated(-0.26184808017295008, -0.32827419468368224,
                            -0.17535585973456849);
    auto original = InverseQuaternionRotate(q, rotated);
    EXPECT_NEAR(original[0], 0.18112868882363914, 1e-9);
    EXPECT_NEAR(original[1], 0.38614886414425986, 1e-9);
    EXPECT_NEAR(original[2], -0.15861744649897938, 1e-9);
  }
}

}  // namespace math
}  // namespace common
}  // namespace apollo
<commit_msg>common/math: Added more tests for quaternion.<commit_after>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/common/math/quaternion.h"

#include <cmath>

#include "gtest/gtest.h"

namespace apollo {
namespace common {
namespace math {

TEST(QuaternionTest, QuaternionToHeading) {
  constexpr double v = sqrt(0.5);  // = cos(pi / 4) = sin(pi / 4)
  EXPECT_DOUBLE_EQ(0,
                   QuaternionToHeading(v, 0.0, 0.0, -v));  // Pointing to east.
  EXPECT_DOUBLE_EQ(
      M_PI_2, QuaternionToHeading(1.0, 0.0, 0.0, 0.0));  // Pointing to north.
  EXPECT_DOUBLE_EQ(-M_PI_2, QuaternionToHeading(0.0, 0.0, 0.0,
                                                1.0));  // Pointing to south.
  EXPECT_DOUBLE_EQ(-M_PI,
                   QuaternionToHeading(v, 0.0, 0.0, v));  // Pointing to west.

  Eigen::Quaternionf q(1.0, 0.0, 0.0, 0.0);
  EXPECT_DOUBLE_EQ(M_PI_2, QuaternionToHeading(q));  // Pointing to north.

  const double headings[] = {-3.3, -2.2, -1.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7};
  for (double heading : headings) {
    EXPECT_NEAR(NormalizeAngle(heading),
                QuaternionToHeading(HeadingToQuaternion<double>(heading)),
                1e-15);
  }
}

TEST(QuaternionTest, QuaternionRotate) {
  apollo::common::Quaternion q;
  q.set_qx(0.016590540978116377);
  q.set_qy(0.012968083311103572);
  q.set_qz(-0.99256254167039326);
  q.set_qw(-0.1199007240933047);

  Eigen::Vector3d original(0.18112868882363914, 0.38614886414425986,
                           -0.15861744649897938);

  auto rotated = QuaternionRotate(q, original);
  EXPECT_NEAR(rotated[0], -0.26184808017295008, 1e-9);
  EXPECT_NEAR(rotated[1], -0.32827419468368224, 1e-9);
  EXPECT_NEAR(rotated[2], -0.17535585973456849, 1e-9);
}

TEST(QuaternionTest, InverseQuaternionRotate) {
  apollo::common::Quaternion q;
  q.set_qx(0.016590540978116377);
  q.set_qy(0.012968083311103572);
  q.set_qz(-0.99256254167039326);
  q.set_qw(-0.1199007240933047);
  Eigen::Vector3d rotated(-0.26184808017295008, -0.32827419468368224,
                          -0.17535585973456849);
  auto original = InverseQuaternionRotate(q, rotated);
  EXPECT_NEAR(original[0], 0.18112868882363914, 1e-9);
  EXPECT_NEAR(original[1], 0.38614886414425986, 1e-9);
  EXPECT_NEAR(original[2], -0.15861744649897938, 1e-9);
}

}  // namespace math
}  // namespace common
}  // namespace apollo
<|endoftext|>