utils.hpp

View source code here on GitHub!

Includes

std::string get_data_file(const std::string name)

Return a character array containing the whole contents of a file found in _data.

std::string get_parent_directory(const std::string &path, uint32_t levels)
Answer get_answer(const uint16_t id)

Return an Answer representing a given problem's solution.

struct Answer

The Answer object represents the solution to a Project Euler problems. It can hold different types of values and an identifier.

union value
uint8_t int8

Represents an 8-bit signed integer.

uint16_t int16

Represents a 16-bit signed integer.

uint32_t int32

Represents a 32-bit signed integer.

uint64_t int64

Represents a 64-bit signed integer.

uint8_t uint8

Represents an 8-bit unsigned integer.

uint16_t uint16

Represents a 16-bit unsigned integer.

uint32_t uint32

Represents a 32-bit unsigned integer.

uint64_t uint64

Represents a 64-bit unsigned integer.

char *string

Represents a c-string value.

uint16_t id

Represents the problem ID.

AnswerType type

Specifies the type of value stored in the value union. It is of type AnswerType.

enum struct AnswerType

The AnswerType enum defines various types of values that can be stored in an Answer.

enumerator ERROR

Represents an error state.

enumerator INT8

Represents an 8-bit signed integer.

enumerator INT16

Represents a 16-bit signed integer.

enumerator INT32

Represents a 32-bit signed integer.

enumerator INT64

Represents a 64-bit signed integer.

enumerator UINT8

Represents an 8-bit unsigned integer.

enumerator UINT16

Represents a 16-bit unsigned integer.

enumerator UINT32

Represents a 32-bit unsigned integer.

enumerator UINT64

Represents a 64-bit unsigned integer.

enumerator STR

Represents a c-string value.

  1#pragma once
  2
  3#include <cerrno>
  4#include <iostream>
  5#include <stdexcept>
  6#include <stdint.h>
  7#include <inttypes.h>
  8#include <cstring>
  9#include <string>
 10#include <sstream>
 11#include <fstream>
 12#include "macros.hpp"
 13
 14#ifdef _WIN32
 15#include <windows.h>
 16#define PATH_SEPARATOR "\\"
 17#else
 18#include <libgen.h>
 19#include <unistd.h>
 20#define PATH_SEPARATOR "/"
 21#endif
 22
 23#ifndef MAX_PATH
 24#define MAX_PATH 4096
 25#endif
 26
 27std::string get_parent_directory(const std::string &path, const uint32_t levels) {
 28    std::string dir = path;
 29    for (uint32_t i = 0; i < levels; ++i) {
 30        size_t pos = dir.find_last_of(PATH_SEPARATOR);
 31        if (pos != std::string::npos)
 32            dir.erase(pos);
 33        else
 34            break;
 35    }
 36    return dir;
 37}
 38
 39std::string get_data_file(const std::string &name) {
 40    char absolute_path[MAX_PATH];
 41    std::ostringstream oss;
 42#ifdef _WIN32
 43    if (!_fullpath(absolute_path, __FILE__, sizeof(absolute_path))) {
 44        oss << "_fullpath failed with error code " << GetLastError();
 45        throw std::runtime_error(oss.str());
 46    }
 47#else
 48    if (!realpath(__FILE__, absolute_path)) {
 49        oss << "realpath failed with error code " << errno;
 50        throw std::runtime_error(oss.str());
 51    }
 52#endif
 53
 54    std::string file_path = get_parent_directory(std::string(absolute_path), 4) + PATH_SEPARATOR "_data" PATH_SEPARATOR + name;
 55    std::ifstream file(file_path.c_str(), std::ios::in | std::ios::binary);
 56    if (!file) {
 57        oss << "Failed to open file: " << file_path << " with error code " << errno;
 58        throw std::runtime_error(oss.str());
 59    }
 60
 61    std::ostringstream content_stream;
 62    content_stream << file.rdbuf();
 63    if (!file) {
 64        oss << "Error reading file: " << file_path << " with error code " << errno;
 65        throw std::runtime_error(oss.str());
 66    }
 67
 68    return content_stream.str();
 69}
 70
 71typedef enum {
 72	ERRORT,
 73	INT8T,
 74	INT16T,
 75	INT32T,
 76	INT64T,
 77	UINT8T,
 78	UINT16T,
 79	UINT32T,
 80	UINT64T,
 81	STRINGT
 82} AnswerType;
 83
 84typedef struct {
 85    union {
 86        uint8_t uint8;
 87        uint16_t uint16;
 88        uint32_t uint32;
 89        uint64_t uint64;
 90        int8_t int8;
 91        int16_t int16;
 92        int32_t int32;
 93        int64_t int64;
 94        char *string;
 95    } value;
 96	uint16_t id;
 97	AnswerType type;
 98} Answer;
 99
100Answer EMSCRIPTEN_KEEPALIVE get_answer(const uint16_t id) {
101    Answer answer;
102    char c_id[6];
103    snprintf(c_id, sizeof(c_id), "%" PRIu16, id);
104    std::string s_id(c_id);
105    std::string answers = get_data_file("answers.tsv");
106    if (answers.empty()) {
107        std::cerr << "Error: Failed to read data from file\n";
108        return answer;
109    }
110
111    std::string line;
112    std::istringstream stream(answers);
113
114    // Read and skip the header
115    if (!std::getline(stream, line)) {
116        std::cerr << "Error: Empty or invalid input\n";
117        return answer;
118    }
119
120    while (std::getline(stream, line)) {
121        std::istringstream lineStream(line);
122        std::string token;
123
124        if (!std::getline(lineStream, token, '\t') || token != s_id)
125            continue;
126
127        if (!std::getline(lineStream, token, '\t'))
128            continue;
129
130        if (token == "uint")
131            answer.type = UINT8T;  // will adjust size later
132        else if (token == "int")
133            answer.type = INT8T;  // will adjust size later
134        else if (token == "str")
135            answer.type = STRINGT;
136        else {
137            std::cerr << "Error: Unknown type '" << token << "'\n";
138            return answer;
139        }
140
141        if (!std::getline(lineStream, token, '\t'))
142            continue;
143        size_t size = strtoull(token.c_str(), NULL, 10);
144
145        if (!std::getline(lineStream, token, '\t'))
146            continue;
147
148        switch (answer.type) {
149            case UINT8T:
150                switch (size) {
151                    case 8:
152                        answer.value.uint8 = (uint8_t)strtoul(token.c_str(), NULL, 10);
153                        break;
154                    case 16:
155                        answer.value.uint16 = (uint16_t)strtoul(token.c_str(), NULL, 10);
156                        answer.type = UINT16T;
157                        break;
158                    case 32:
159                        answer.value.uint32 = strtoul(token.c_str(), NULL, 10);
160                        answer.type = UINT32T;
161                        break;
162                    case 64:
163                        answer.value.uint64 = strtoull(token.c_str(), NULL, 10);
164                        answer.type = UINT64T;
165                        break;
166                    default:
167                        std::cerr << "Error: Unsupported int size " << size << "'\n";
168                        Answer err = {{0}};
169                        return err;
170                }
171                break;
172            case INT8T:
173                switch (size) {
174                    case 8:
175                        answer.value.int8 = (int8_t)strtol(token.c_str(), NULL, 10);
176                        break;
177                    case 16:
178                        answer.value.int16 = (int16_t)strtol(token.c_str(), NULL, 10);
179                        answer.type = INT16T;
180                        break;
181                    case 32:
182                        answer.value.int32 = strtol(token.c_str(), NULL, 10);
183                        answer.type = INT32T;
184                        break;
185                    case 64:
186                        answer.value.int64 = strtoll(token.c_str(), NULL, 10);
187                        answer.type = INT64T;
188                        break;
189                    default:
190                        std::cerr << "Error: Unsupported uint size " << size << "'\n";
191                        Answer err = {{0}};
192                        return err;
193                }
194                break;
195            case STRINGT:
196                answer.value.string = (char *)malloc(size + 1);
197                if (answer.value.string) {
198                    strncpy(answer.value.string, token.c_str(), size);
199                    answer.value.string[size] = 0;
200                } else {
201                    std::cerr << "Error: Memory allocation failed for string\n";
202                    Answer err = {{0}};
203                    return err;
204                }
205                break;
206            default:
207                std::cerr << "Error: Unknown type (should be unreachable)\n";
208                Answer err = {{0}};
209                return err;
210        }
211    }
212
213    return answer;
214}

Tags: file-io