Last active
February 17, 2024 13:22
-
-
Save niXman/a6ae063335202d42c11f883493832df5 to your computer and use it in GitHub Desktop.
parse IP at compile-time
This file contains 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 <array> | |
#include <iostream> | |
#include <cassert> | |
#include <cstring> | |
#include <cstdint> | |
#if !defined(__fallthrough) && defined(__has_cpp_attribute) | |
#if __has_cpp_attribute(fallthrough) | |
#define __FJ__FALLTHROUGH [[fallthrough]] | |
#elif __has_cpp_attribute(gnu::fallthrough) | |
#define __FJ__FALLTHROUGH [[gnu::fallthrough]] | |
#endif | |
#endif | |
#if !defined(__FJ__FALLTHROUGH) && defined(__has_attribute) | |
#if __has_attribute(__fallthrough__) | |
#define __FJ__FALLTHROUGH __attribute__((__fallthrough__)) | |
#endif | |
#endif | |
#if !defined(__FJ__FALLTHROUGH) /* well, we tried */ | |
#define __FJ__FALLTHROUGH ((void)0) | |
#endif | |
constexpr const char* strchr(const char *s, char c) { | |
for ( ; *s && (*s != c); ++s ) | |
; | |
return (*s) ? s : nullptr; | |
} | |
constexpr std::uint64_t atou64(const char *ptr, std::size_t len) { | |
# define __FJ__PER_CHAR_EXPR(n) ((res << 1) + (res << 3) + (str[len - n] - '0')) | |
const auto *str = reinterpret_cast<const std::uint8_t *>(ptr); | |
std::uint64_t res = 0; | |
switch ( len ) { | |
case 20: res = __FJ__PER_CHAR_EXPR(20); __FJ__FALLTHROUGH; | |
case 19: res = __FJ__PER_CHAR_EXPR(19); __FJ__FALLTHROUGH; | |
case 18: res = __FJ__PER_CHAR_EXPR(18); __FJ__FALLTHROUGH; | |
case 17: res = __FJ__PER_CHAR_EXPR(17); __FJ__FALLTHROUGH; | |
case 16: res = __FJ__PER_CHAR_EXPR(16); __FJ__FALLTHROUGH; | |
case 15: res = __FJ__PER_CHAR_EXPR(15); __FJ__FALLTHROUGH; | |
case 14: res = __FJ__PER_CHAR_EXPR(14); __FJ__FALLTHROUGH; | |
case 13: res = __FJ__PER_CHAR_EXPR(13); __FJ__FALLTHROUGH; | |
case 12: res = __FJ__PER_CHAR_EXPR(12); __FJ__FALLTHROUGH; | |
case 11: res = __FJ__PER_CHAR_EXPR(11); __FJ__FALLTHROUGH; | |
case 10: res = __FJ__PER_CHAR_EXPR(10); __FJ__FALLTHROUGH; | |
case 9 : res = __FJ__PER_CHAR_EXPR( 9); __FJ__FALLTHROUGH; | |
case 8 : res = __FJ__PER_CHAR_EXPR( 8); __FJ__FALLTHROUGH; | |
case 7 : res = __FJ__PER_CHAR_EXPR( 7); __FJ__FALLTHROUGH; | |
case 6 : res = __FJ__PER_CHAR_EXPR( 6); __FJ__FALLTHROUGH; | |
case 5 : res = __FJ__PER_CHAR_EXPR( 5); __FJ__FALLTHROUGH; | |
case 4 : res = __FJ__PER_CHAR_EXPR( 4); __FJ__FALLTHROUGH; | |
case 3 : res = __FJ__PER_CHAR_EXPR( 3); __FJ__FALLTHROUGH; | |
case 2 : res = __FJ__PER_CHAR_EXPR( 2); __FJ__FALLTHROUGH; | |
case 1 : res = __FJ__PER_CHAR_EXPR( 1); | |
} | |
# undef __FJ__PER_CHAR_EXPR | |
return res; | |
} | |
constexpr std::array<std::uint8_t, 4> split(const char *s, std::size_t slen, char sep = '.') { | |
std::array<std::uint8_t, 4> dst{}; | |
std::uint8_t *d = dst.data(); | |
const char *start = s; | |
for ( const char *p = strchr(s, sep); p; p = strchr(s, sep) ) { | |
std::size_t len = p-s; | |
*d++ = atou64(s, len); | |
s = p+1; | |
} | |
std::size_t len = slen - (s - start); | |
*d = atou64(s, len); | |
return dst; | |
} | |
#define TEST(exp, str, _0, _1, _2, _3) { \ | |
constexpr auto ip = split(str, sizeof(str)-1); \ | |
static_assert(ip[0] == _0); \ | |
static_assert(ip[1] == _1); \ | |
static_assert(ip[2] == _2); \ | |
static_assert(ip[3] == _3); \ | |
std::cout << "expression \"" str "\" parsed as " << (std::size_t)ip[0] << '.' << (std::size_t)ip[1] << '.' << (std::size_t)ip[2] << '.' << (std::size_t)ip[3] << std::endl; \ | |
} | |
int main() { | |
TEST(true, "0.0.0.0", 0, 0, 0, 0) | |
TEST(true, "1.1.1.1", 1, 1, 1, 1) | |
TEST(true, "10.10.10.10", 10, 10, 10, 10) | |
TEST(true, "11.11.11.11", 11, 11, 11, 11) | |
TEST(true, "110.110.110.110", 110, 110, 110, 110) | |
TEST(false, "0", 0, 0, 0, 0) | |
//TEST(false, "0:0:0:0", 0, 0, 0, 0) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment