|
@@ -0,0 +1,419 @@
|
|
|
|
+// Copyright 2020 Alexey Kutepov <[email protected]>
|
|
|
|
+//
|
|
|
|
+// Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
+// a copy of this software and associated documentation files (the
|
|
|
|
+// "Software"), to deal in the Software without restriction, including
|
|
|
|
+// without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
+// distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
+// permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
+// the following conditions:
|
|
|
|
+//
|
|
|
|
+// The above copyright notice and this permission notice shall be
|
|
|
|
+// included in all copies or substantial portions of the Software.
|
|
|
|
+//
|
|
|
|
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
+//
|
|
|
|
+// ============================================================
|
|
|
|
+//
|
|
|
|
+// aids — 0.2.0 — std replacement for C++. Designed to aid developers
|
|
|
|
+// to a better programming experience.
|
|
|
|
+//
|
|
|
|
+// https://github.com/rexim/aids
|
|
|
|
+//
|
|
|
|
+// ============================================================
|
|
|
|
+//
|
|
|
|
+// ChangeLog (https://semver.org/ is implied)
|
|
|
|
+//
|
|
|
|
+// 0.2.0 unwrap_into
|
|
|
|
+// print1 for long int
|
|
|
|
+// 0.1.0 print1 for long unsigned int
|
|
|
|
+// print1 for int
|
|
|
|
+// Pad
|
|
|
|
+// 0.0.3 bugfix for print1 of Maybe<T>
|
|
|
|
+// 0.0.2 fix sign-unsigned integer comparison in aids::read_file_as_string_view
|
|
|
|
+// 0.0.1 min, max, clamp,
|
|
|
|
+// defer,
|
|
|
|
+// Maybe<T>,
|
|
|
|
+// String_View,
|
|
|
|
+// print, println
|
|
|
|
+
|
|
|
|
+#ifndef AIDS_HPP_
|
|
|
|
+#define AIDS_HPP_
|
|
|
|
+
|
|
|
|
+#include <cassert>
|
|
|
|
+#include <cctype>
|
|
|
|
+#include <cerrno>
|
|
|
|
+#include <cstdint>
|
|
|
|
+#include <cstdio>
|
|
|
|
+#include <cstdlib>
|
|
|
|
+#include <cstring>
|
|
|
|
+
|
|
|
|
+namespace aids
|
|
|
|
+{
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+ // ALGORITHM
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+ template <typename T>
|
|
|
|
+ T min(T a, T b)
|
|
|
|
+ {
|
|
|
|
+ return a < b ? a : b;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename T>
|
|
|
|
+ T max(T a, T b)
|
|
|
|
+ {
|
|
|
|
+ return a > b ? a : b;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename T>
|
|
|
|
+ T clamp(T x, T low, T high)
|
|
|
|
+ {
|
|
|
|
+ return min(max(low, x), high);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+ // DEFER
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+ // https://www.reddit.com/r/ProgrammerTIL/comments/58c6dx/til_how_to_defer_in_c/
|
|
|
|
+ template <typename F>
|
|
|
|
+ struct saucy_defer {
|
|
|
|
+ F f;
|
|
|
|
+ saucy_defer(F f) : f(f) {}
|
|
|
|
+ ~saucy_defer() { f(); }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ template <typename F>
|
|
|
|
+ saucy_defer<F> defer_func(F f)
|
|
|
|
+ {
|
|
|
|
+ return saucy_defer<F>(f);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+#define DEFER_1(x, y) x##y
|
|
|
|
+#define DEFER_2(x, y) DEFER_1(x, y)
|
|
|
|
+#define DEFER_3(x) DEFER_2(x, __COUNTER__)
|
|
|
|
+#define defer(code) auto DEFER_3(_defer_) = ::aids::defer_func([&](){code;})
|
|
|
|
+
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+ // MAYBE
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+ template <typename T>
|
|
|
|
+ struct Maybe
|
|
|
|
+ {
|
|
|
|
+ bool has_value;
|
|
|
|
+ T unwrap;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+#define unwrap_into(lvalue, maybe) \
|
|
|
|
+ do { \
|
|
|
|
+ auto maybe_var = (maybe); \
|
|
|
|
+ if (!maybe_var.has_value) return {}; \
|
|
|
|
+ (lvalue) = maybe_var.unwrap; \
|
|
|
|
+ } while (0)
|
|
|
|
+
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+ // STRING_VIEW
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+ struct String_View
|
|
|
|
+ {
|
|
|
|
+ size_t count;
|
|
|
|
+ const char *data;
|
|
|
|
+
|
|
|
|
+ [[nodiscard]]
|
|
|
|
+ String_View trim_begin(void) const
|
|
|
|
+ {
|
|
|
|
+ String_View view = *this;
|
|
|
|
+
|
|
|
|
+ while (view.count != 0 && isspace(*view.data)) {
|
|
|
|
+ view.data += 1;
|
|
|
|
+ view.count -= 1;
|
|
|
|
+ }
|
|
|
|
+ return view;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ [[nodiscard]]
|
|
|
|
+ String_View trim_end(void) const
|
|
|
|
+ {
|
|
|
|
+ String_View view = *this;
|
|
|
|
+
|
|
|
|
+ while (view.count != 0 && isspace(*(view.data + view.count - 1))) {
|
|
|
|
+ view.count -= 1;
|
|
|
|
+ }
|
|
|
|
+ return view;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ [[nodiscard]]
|
|
|
|
+ String_View trim(void) const
|
|
|
|
+ {
|
|
|
|
+ return trim_begin().trim_end();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void chop_back(size_t n)
|
|
|
|
+ {
|
|
|
|
+ count -= n < count ? n : count;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void chop(size_t n)
|
|
|
|
+ {
|
|
|
|
+ if (n > count) {
|
|
|
|
+ data += count;
|
|
|
|
+ count = 0;
|
|
|
|
+ } else {
|
|
|
|
+ data += n;
|
|
|
|
+ count -= n;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void grow(size_t n)
|
|
|
|
+ {
|
|
|
|
+ count += n;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String_View chop_by_delim(char delim)
|
|
|
|
+ {
|
|
|
|
+ assert(data);
|
|
|
|
+
|
|
|
|
+ size_t i = 0;
|
|
|
|
+ while (i < count && data[i] != delim) i++;
|
|
|
|
+ String_View result = {i, data};
|
|
|
|
+ chop(i + 1);
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String_View chop_word(void)
|
|
|
|
+ {
|
|
|
|
+ *this = trim_begin();
|
|
|
|
+
|
|
|
|
+ size_t i = 0;
|
|
|
|
+ while (i < count && !isspace(data[i])) i++;
|
|
|
|
+
|
|
|
|
+ String_View result = { i, data };
|
|
|
|
+
|
|
|
|
+ count -= i;
|
|
|
|
+ data += i;
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename Integer>
|
|
|
|
+ Maybe<Integer> from_hex() const
|
|
|
|
+ {
|
|
|
|
+ Integer result = {};
|
|
|
|
+
|
|
|
|
+ for (size_t i = 0; i < count; ++i) {
|
|
|
|
+ Integer x = data[i];
|
|
|
|
+ if ('0' <= x && x <= '9') {
|
|
|
|
+ x = (Integer) (x - '0');
|
|
|
|
+ } else if ('a' <= x && x <= 'f') {
|
|
|
|
+ x = (Integer) (x - 'a' + 10);
|
|
|
|
+ } else if ('A' <= x && x <= 'F') {
|
|
|
|
+ x = (Integer) (x - 'A' + 10);
|
|
|
|
+ } else {
|
|
|
|
+ return {};
|
|
|
|
+ }
|
|
|
|
+ result = result * (Integer) 0x10 + x;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {true, result};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename Integer>
|
|
|
|
+ Maybe<Integer> as_integer() const
|
|
|
|
+ {
|
|
|
|
+ Integer sign = 1;
|
|
|
|
+ Integer number = {};
|
|
|
|
+ String_View view = *this;
|
|
|
|
+
|
|
|
|
+ if (view.count == 0) {
|
|
|
|
+ return {};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (*view.data == '-') {
|
|
|
|
+ sign = -1;
|
|
|
|
+ view.chop(1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ while (view.count) {
|
|
|
|
+ if (!isdigit(*view.data)) {
|
|
|
|
+ return {};
|
|
|
|
+ }
|
|
|
|
+ number = number * 10 + (*view.data - '0');
|
|
|
|
+ view.chop(1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return { true, number * sign };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Maybe<float> as_float() const
|
|
|
|
+ {
|
|
|
|
+ char buffer[300] = {};
|
|
|
|
+ memcpy(buffer, data, min(sizeof(buffer) - 1, count));
|
|
|
|
+ char *endptr = NULL;
|
|
|
|
+ float result = strtof(buffer, &endptr);
|
|
|
|
+
|
|
|
|
+ if (buffer > endptr || (size_t) (endptr - buffer) != count) {
|
|
|
|
+ return {};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {true, result};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ String_View subview(size_t start, size_t count) const
|
|
|
|
+ {
|
|
|
|
+ if (start + count <= this->count) {
|
|
|
|
+ return {count, data + start};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bool operator==(String_View view) const
|
|
|
|
+ {
|
|
|
|
+ if (this->count != view.count) return false;
|
|
|
|
+ return memcmp(this->data, view.data, this->count) == 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bool operator!=(String_View view) const
|
|
|
|
+ {
|
|
|
|
+ return !(*this == view);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bool has_prefix(String_View prefix) const
|
|
|
|
+ {
|
|
|
|
+ return prefix.count <= this->count
|
|
|
|
+ && this->subview(0, prefix.count) == prefix;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ String_View operator ""_sv(const char *data, size_t count)
|
|
|
|
+ {
|
|
|
|
+ return {count, data};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String_View cstr_as_string_view(const char *cstr)
|
|
|
|
+ {
|
|
|
|
+ return {strlen(cstr), cstr};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, String_View view)
|
|
|
|
+ {
|
|
|
|
+ fwrite(view.data, 1, view.count, stream);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Maybe<String_View> read_file_as_string_view(const char *filename)
|
|
|
|
+ {
|
|
|
|
+ FILE *f = fopen(filename, "rb");
|
|
|
|
+ if (!f) return {};
|
|
|
|
+ defer(fclose(f));
|
|
|
|
+
|
|
|
|
+ int err = fseek(f, 0, SEEK_END);
|
|
|
|
+ if (err < 0) return {};
|
|
|
|
+
|
|
|
|
+ long size = ftell(f);
|
|
|
|
+ if (size < 0) return {};
|
|
|
|
+
|
|
|
|
+ err = fseek(f, 0, SEEK_SET);
|
|
|
|
+ if (err < 0) return {};
|
|
|
|
+
|
|
|
|
+ auto data = malloc(size);
|
|
|
|
+ if (!data) return {};
|
|
|
|
+
|
|
|
|
+ size_t read_size = fread(data, 1, size, f);
|
|
|
|
+ if (read_size != (size_t) size && ferror(f)) return {};
|
|
|
|
+
|
|
|
|
+ return {true, {static_cast<size_t>(size), static_cast<const char*>(data)}};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+ // PRINT
|
|
|
|
+ ////////////////////////////////////////////////////////////
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, const char *s)
|
|
|
|
+ {
|
|
|
|
+ fwrite(s, 1, strlen(s), stream);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, char *s)
|
|
|
|
+ {
|
|
|
|
+ fwrite(s, 1, strlen(s), stream);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, char c)
|
|
|
|
+ {
|
|
|
|
+ fputc(c, stream);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, float f)
|
|
|
|
+ {
|
|
|
|
+ fprintf(stream, "%f", f);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, unsigned long long x)
|
|
|
|
+ {
|
|
|
|
+ fprintf(stream, "%lld", x);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, long unsigned int x)
|
|
|
|
+ {
|
|
|
|
+ fprintf(stream, "%lu", x);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, int x)
|
|
|
|
+ {
|
|
|
|
+ fprintf(stream, "%d", x);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, long int x)
|
|
|
|
+ {
|
|
|
|
+ fprintf(stream, "%ld", x);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename ... Types>
|
|
|
|
+ void print(FILE *stream, Types... args)
|
|
|
|
+ {
|
|
|
|
+ (print1(stream, args), ...);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename T>
|
|
|
|
+ void print1(FILE *stream, Maybe<T> maybe)
|
|
|
|
+ {
|
|
|
|
+ if (!maybe.has_value) {
|
|
|
|
+ print(stream, "None");
|
|
|
|
+ } else {
|
|
|
|
+ print(stream, "Some(", maybe.unwrap, ")");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ template <typename ... Types>
|
|
|
|
+ void println(FILE *stream, Types... args)
|
|
|
|
+ {
|
|
|
|
+ (print1(stream, args), ...);
|
|
|
|
+ print1(stream, '\n');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ struct Pad
|
|
|
|
+ {
|
|
|
|
+ size_t n;
|
|
|
|
+ char c;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ void print1(FILE *stream, Pad pad)
|
|
|
|
+ {
|
|
|
|
+ for (size_t i = 0; i < pad.n; ++i) {
|
|
|
|
+ fputc(pad.c, stream);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#endif // AIDS_HPP_
|