Last active
April 12, 2022 00:03
-
-
Save Frityet/d3f4a9f3e2742adae9ac038f34ec42f0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <errno.h> | |
extern int errno; | |
#include <pthread.h> | |
//Compilation does require CURL, so you should compile once, and just use the .dll | |
#include <curl/curl.h> | |
typedef uint8_t byte_t; | |
struct Memory { | |
//if compiled with a C++ compiler, it will error on assigning a void* to any other pointer type | |
//By using a union we can mitagate this | |
union { | |
byte_t *bytes; | |
void *ptr; | |
} data; | |
size_t size; | |
}; | |
struct ThreadedTaskMemory { | |
bool complete; | |
long total, now; | |
struct Memory result; | |
//PRIVATE: | |
pthread_t _thread; | |
}; | |
static int update_progress(struct ThreadedTaskMemory *mem, long total, long now, long _, long __) | |
{ | |
mem->total = total; | |
mem->now = now; | |
return 0; | |
} | |
static size_t write_memory(void *contents, size_t size, size_t bytecount, void *ptr) | |
{ | |
size_t total = size * bytecount; | |
struct Memory *mem = ptr; //The layout of ptr should correspond to struct Memory | |
//Resize the memory with the previous size, plus the new size with one to be the null character | |
byte_t *newmem = realloc(mem->data.ptr, mem->size + total + 1); | |
if (newmem == NULL) { | |
fprintf(stderr, "Could not allocate %zu bytes of memory!\nReason: %s\n", mem->size + total, strerror(errno)); | |
return 0; | |
} | |
mem->data.ptr = newmem; | |
memcpy(&(mem->data.bytes[mem->size]), contents, total); | |
mem->size += total; | |
//Just in case this is a string, add a null terminator | |
mem->data.bytes[mem->size] = '\0'; | |
return total; | |
} | |
struct Memory download_file(const char *url) | |
{ | |
CURL *curl = curl_easy_init(); | |
struct Memory mem = {0}; | |
//Pointer must be allocated for it to be reallocated in write_memory | |
mem.data.ptr = malloc(1); | |
curl_easy_setopt(curl, CURLOPT_URL, url); | |
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory); | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem); | |
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); | |
CURLcode err; | |
if ((err = curl_easy_perform(curl)) != CURLE_OK) { | |
fprintf(stderr, "Could not download file at URL %s!\nReason: %s\n", url, curl_easy_strerror(err)); | |
free(mem.data.ptr); | |
curl_free(curl); | |
return (struct Memory){0}; | |
} | |
curl_free(curl); | |
return mem; | |
} | |
struct DownloadFileAsync_Arguments { | |
/*_Atomic*/ | |
struct ThreadedTaskMemory *thread; | |
char *url; | |
size_t url_length; | |
}; | |
static void async_download(struct DownloadFileAsync_Arguments *args) | |
{ | |
CURL *curl = curl_easy_init(); | |
struct Memory mem = {0}; | |
//Pointer must be allocated for it to be reallocated in write_memory | |
mem.data.ptr = malloc(1); | |
curl_easy_setopt(curl, CURLOPT_URL, args->url); | |
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory); | |
// curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,update_progress); | |
// curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, args->thread); | |
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false); | |
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION,update_progress); | |
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, args->thread); | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem); | |
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); | |
CURLcode err; | |
if ((err = curl_easy_perform(curl)) != CURLE_OK) { | |
fprintf(stderr, "Could not download file at URL %s!\nReason: %s\n", args->url, curl_easy_strerror(err)); | |
free(mem.data.ptr); | |
curl_free(curl); | |
} | |
curl_free(curl); | |
args->thread->result.data.ptr = mem.data.ptr; | |
args->thread->result.size = mem.size; | |
args->thread->complete = true; | |
//The memory is allocated in the download_file_async function and must be freed here | |
free(args->url); | |
free(args); | |
} | |
struct ThreadedTaskMemory *download_file_async(const char *url) | |
{ | |
//Must be on heap because it needs ot be allocated on the other thread | |
struct ThreadedTaskMemory *thrdmem = calloc(1, sizeof(*thrdmem)); | |
struct DownloadFileAsync_Arguments *args = malloc(sizeof(*args)); | |
args->thread = thrdmem; | |
//Because we need to allocate and then copy, it's faster to get the length | |
// and use it with strncpy | |
size_t urllen = strlen(url); | |
args->url = malloc(sizeof(char) * urllen); | |
args->url_length = urllen; | |
strncpy(args->url, url, urllen); | |
pthread_create(&thrdmem->_thread, NULL, (void *)async_download, args); | |
return thrdmem; | |
} | |
void free_memory(struct Memory memory) | |
{ | |
free(memory.data.ptr); | |
} | |
void free_threaded_task_memory(struct ThreadedTaskMemory *task) | |
{ | |
pthread_join(task->_thread, NULL); | |
free_memory(task->result); | |
free(task); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment