reprodyne/reprodyne.cpp

238 lines
7.4 KiB
C++

//This file is part of the reprodyne project and is licensed under the terms of the LGPL-3.0-only
#include <iostream>
#include <openssl/sha.h>
#include "user-include/reprodyne.h"
#include "programhandlers.h"
std::unique_ptr<reprodyne::ProgramRecorder> recorder;
std::unique_ptr<reprodyne::ProgramPlayer> player;
reprodyne_playback_failure_handler playbackErrorHandler = nullptr;
std::string jumpSafeString;
//(re: using std::strings in exceptions)
//Playing it on the loose and easy with these exceptions because they are playback,
// not memory errors. And it's prettier this way~
//We need to catch all exceptions and deal with them here because you don't want
// the library to leak through a C boundary. And as such, we want to be longjmp safe.
static void handlePlaybackErrorException(const reprodyne::PlaybackError& e)
{
if(!playbackErrorHandler)
{
std::cerr << "Reprodyne PLAYBACK FAILURE (No handler provided): " << e.what() << std::endl << std::flush;
std::terminate();
}
jumpSafeString = e.what(); //what() references an std::string, so it's not safe beyond this point.
playbackErrorHandler(e.getCode(), jumpSafeString.c_str());
std::terminate(); //If you don't do it, I will.
}
static void handleRuntimeErrrr(const char* what)
{
std::cerr << "Reprodyne runtime error: " << what << std::endl;
std::terminate();
}
static void unknownErrorDie()
{
std::terminate();
}
//Uniform exception handler so that we can translate to C-safe errors consistently.
template<typename T>
static T safeBlock(std::function<T()> thingamajigulator)
{
try
{ return thingamajigulator(); }
catch(const reprodyne::PlaybackError& e)
{ handlePlaybackErrorException(e); }
catch(const std::runtime_error& e)
{ handleRuntimeErrrr(e.what()); } //"eh, what??"
catch(...)
{
//fucc
unknownErrorDie();
}
std::terminate(); //Why won't you fuCKING DIE ALREADY?!?
}
template<>
void safeBlock(std::function<void()> thingamajigulator)
{
try
{ thingamajigulator(); }
catch(const reprodyne::PlaybackError& e)
{ handlePlaybackErrorException(e); }
catch(const std::runtime_error& e)
{ handleRuntimeErrrr(e.what()); }
catch(...)
{
//fucc
unknownErrorDie();
}
}
//Generic reset so I can have n-number of player types and swap between them arbitrarily
static void reset()
{
recorder.reset();
player.reset();
}
extern "C"
{
void reprodyne_do_not_call_this_function_directly_set_playback_failure_handler(reprodyne_playback_failure_handler handler)
{ playbackErrorHandler = handler; /*Not my problem anymore...*/ }
void reprodyne_do_not_call_this_function_directly_record()
{
safeBlock<void>([&]
{
reset();
recorder = std::make_unique<reprodyne::ProgramRecorder>();
});
}
void reprodyne_do_not_call_this_function_directly_save(const char* path)
{
safeBlock<void>([&]
{
if(!recorder) throw std::logic_error("bad doG! No!");
recorder->save(path);
});
}
void reprodyne_do_not_call_this_function_directly_play(const char* path)
{
safeBlock<void>([&]
{
reset();
player = std::make_unique<reprodyne::ProgramPlayer>(path);
});
}
void reprodyne_do_not_call_this_function_directly_assert_complete_read()
{
safeBlock<void>([&]
{
if(!player) throw std::logic_error("Cannot assert complete read in record mode");
player->assertCompleteRead();
});
}
void reprodyne_do_not_call_this_function_directly_mark_frame()
{
safeBlock<void>([&]
{
if(recorder) recorder->markFrame();
else if(player) player->markFrame();
});
}
void reprodyne_do_not_call_this_function_directly_open_scope(void* ptr)
{
safeBlock<void>([&]
{
if(recorder) recorder->openScope(ptr);
else if(player) player->openScope(ptr);
});
}
void reprodyne_do_not_call_this_function_directly_write_indeterminate(void* scopePtr, const char* key, double indeterminate)
{
safeBlock<void>([&]
{
if(!recorder) throw std::logic_error("Bad mode. Expected record");
recorder->intercept(scopePtr, key, indeterminate);
});
}
double reprodyne_do_not_call_this_function_directly_read_indeterminate(void* scopePtr, const char* subscopeKey)
{
return safeBlock<double>([&]
{
if(!player) throw std::logic_error("Bad mode. Expected player");
return player->intercept(scopePtr, subscopeKey, 0); //Value is ignored for read.
});
}
double reprodyne_do_not_call_this_function_directly_intercept_double(void* scopePtr, const char* scopeKey, const double indeterminate)
{
return safeBlock<double>([&]()
{
if(recorder) return recorder->intercept(scopePtr, scopeKey, indeterminate);
else if(player) return player->intercept(scopePtr, scopeKey, indeterminate);
throw std::logic_error("die");
});
}
void reprodyne_do_not_call_this_function_directly_validate_string(void* scopePtr, const char* subScopeKey, const char* call)
{
return safeBlock<void>([&]
{
//God I wish I had lisp macros...
if(recorder) recorder->serialize(scopePtr, subScopeKey, call);
else if(player) player->serialize(scopePtr, subScopeKey, call);
else throw std::logic_error("die");
});
}
void reprodyne_do_not_call_this_function_directly_validate_bitmap_hash(void* scope,
const char* key,
const unsigned int width,
const unsigned int height,
const unsigned int stride,
void* bytes)
{
safeBlock<void>([&]
{
//I like my typos sometimes
auto errorHahnDler = [&](const char* msg)
{
//This is only safe because jumpSafeString is only otherwise used by
// the playback error handler. Even still, I don't like this...
jumpSafeString = msg;
jumpSafeString += " in validate bitmap hash function!";
jumpSafeString += " Subscope key: ";
jumpSafeString += key;
throw std::runtime_error(jumpSafeString.c_str());
};
if(height < 1) errorHahnDler("Useless height");
if(width < 1) errorHahnDler("Useless width");
if(stride < width) errorHahnDler("Stride smaller than width");
if(!bytes) errorHahnDler("NULL data pointer");
//int8_t because that's how flatbuffers likes it, I ain't judge.
std::vector<int8_t> hash(SHA256_DIGEST_LENGTH);
//unsigned char because that's how OpenSSL likes it, I ain't judge.
std::vector<unsigned char> clippedImageRegion;
clippedImageRegion.reserve(width * height);
for(int h = 0; h != height; ++h)
for(int w = 0; w != width; ++w)
clippedImageRegion.push_back(reinterpret_cast<unsigned char*>(bytes)[h * stride + w]);
SHA256(&clippedImageRegion[0], clippedImageRegion.size(), reinterpret_cast<unsigned char*>(&hash[0]));
if(recorder) recorder->serialize(scope, key, width, height, hash); //Variadic templates are badass.
else if(player) player->serialize(scope, key, width, height, hash);
});
}
} //extern "C"