Skip to content

Instantly share code, notes, and snippets.

@Frityet
Last active April 12, 2022 00:03
Show Gist options
  • Save Frityet/d3f4a9f3e2742adae9ac038f34ec42f0 to your computer and use it in GitHub Desktop.
Save Frityet/d3f4a9f3e2742adae9ac038f34ec42f0 to your computer and use it in GitHub Desktop.
#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