commit 35f91a7fe6cd2699a800a3b38ee6e8fabbf00d58 Author: Alexander Zubkov Date: Wed Nov 24 23:59:09 2021 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d73bffe --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/plag diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ac28fb9 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +plag: plag.c config.h + cc -O3 -o plag plag.c + +clean: + rm plag diff --git a/config.h b/config.h new file mode 100644 index 0000000..2751e73 --- /dev/null +++ b/config.h @@ -0,0 +1,6 @@ +#include + +#define SIZE (10 * 1<<20) +#define BUFLEN 1024 +typedef uint32_t pos_t; +typedef int mask_t; diff --git a/plag.c b/plag.c new file mode 100644 index 0000000..fa8f820 --- /dev/null +++ b/plag.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#define IP4LEN 32 +#define IP6LEN 128 +#define IPMAXLEN IP6LEN + +struct in4_addr { + unsigned char s4_addr[4]; +}; + +struct node { pos_t a[2]; }; +typedef struct node *iptree; +typedef int *bits; +typedef void (*bitsfun)(bits, mask_t); + +pos_t maxpos = 0; + +iptree iptree_new(pos_t size) { + iptree t = malloc(size * sizeof(struct node)); + if (t == 0) error(1, errno, "iptree_new: malloc failed"); + pos_t i; + for(i = 0; i < size; ++i) { + t[i].a[0] = i + 1; + t[i].a[1] = 0; + } + t[i].a[0] = 0; + return t; +} + +pos_t iptree_popfree(iptree t) { + pos_t f = t[0].a[0]; + if (f > maxpos) maxpos = f; + if (!f) error(1, 0, "iptree_popfree: no free nodes left"); + t[0].a[0] = t[f].a[0]; + t[f].a[0] = 0; + return f; +} + +void iptree_pushfree(iptree t, pos_t f) { + t[f].a[0] = t[0].a[0]; + t[f].a[1] = 0; + t[0].a[0] = f; +} + +int iptree_is_term(iptree t, pos_t h) { + return (h && !t[h].a[0] && !t[h].a[1]); +} + +void iptree_sub_del(iptree t, pos_t h) { + for (int i = 0; i < 2; ++i) { + int j = t[h].a[i]; + if (j) iptree_sub_del(t, j); + } + iptree_pushfree(t, h); +} + +void iptree_add_new(iptree t, pos_t h, bits b, mask_t m) { + if (m) { + // there are bits left + int l = b[0]; // our bit is left + int r = 1 - l; // sibling bit is right + // allocate next node element + pos_t jl = iptree_popfree(t); + // fill current node + t[h].a[l] = jl; + // recurse to add other bits + iptree_add_new(t, jl, b + 1, m - 1); + } else { + // no bits left, this node is terminal + t[h].a[0] = 0; + t[h].a[1] = 0; + } +} + +void iptree_add_rec(iptree t, pos_t h, bits b, mask_t m) { + if (m) { + // bits to check are left + int l = b[0]; // our bit is left + int r = 1 - l; // sibling bit is right + pos_t jl = t[h].a[l]; // left subtree head + pos_t jr = t[h].a[r]; // right subtree head + if (jl) { + // tree for our bit is not empty, recurse + iptree_add_rec(t, jl, b + 1, m - 1); + } else { + // tree for our bit is missing + // check if this element is terminal (less specific here) + if (!jr) return; + // otherwise fill new subtree + iptree_add_new(t, h, b, m); + } + // check if we can fold + if (iptree_is_term(t, t[h].a[0]) && iptree_is_term(t, t[h].a[1])) { + for (int i = 0; i < 2; ++i) { + iptree_pushfree(t, t[h].a[i]); + t[h].a[i] = 0; + } + } + } else { + // no bits left, this node must be terminal + for (int i = 0; i < 2; ++i) { + pos_t j = t[h].a[i]; // subtree head + // delete subtree if exist + if (j) { + iptree_sub_del(t, j); + t[h].a[i] = 0; + } + } + } +} + +void iptree_add(iptree t, pos_t h, bits b, mask_t m) { + if (t[h].a[0]) { + // tree is not empty already + iptree_add_rec(t, t[h].a[0], b, m); + } else { + // tree is empty, initialize it + t[h].a[0] = iptree_popfree(t); + iptree_add_new(t, t[h].a[0], b, m); + } +} + +void iptree_traverse_rec(iptree t, bitsfun f, pos_t h, bits b, mask_t m) { + int flag = 1; + for (int i = 0; i < 2; ++i) { + pos_t j = t[h].a[i]; + // if subtree exists + if (j) { + flag = 0; // node is not terminal + b[m] = i; // set bit + // recurse + iptree_traverse_rec(t, f, j, b, m + 1); + } + } + if (flag) { + // terminal node + f(b, m); + } +} + +void iptree_traverse(iptree t, pos_t h, bitsfun f) { + int b[IPMAXLEN]; + if (t[h].a[0]) { + // tree is not empty + iptree_traverse_rec(t, f, t[h].a[0], b, 0); + } +} + +void bits2ip4(bits b, mask_t m, char *s, int size) { + // convert bits to in4_addr + struct in4_addr ip; + for (int i = 0; i < 4; ++i) { + ip.s4_addr[i] = 0; + for (int j = 0; j < 8; ++j) { + ip.s4_addr[i] <<= 1; + int p = i*8 + j; + ip.s4_addr[i] |= (p >= m) ? 0 : b[p]; + } + } + // get test representation of ip + char s_ip[INET_ADDRSTRLEN]; + if (!inet_ntop(AF_INET, &ip, s_ip, INET_ADDRSTRLEN)) error(1, errno, "bits2ip4"); + // print ip with mask to buffer + int res = snprintf(s, size, "%s/%d", s_ip, m); + // if printing to buffer has failed + if (res < 0 || res >= size) error(1, errno, "bits2ip4"); +} + +void bits2ip6(bits b, mask_t m, char *s, int size) { + // convert bits to in6_addr + struct in6_addr ip; + for (int i = 0; i < 16; ++i) { + ip.s6_addr[i] = 0; + for (int j = 0; j < 8; ++j) { + ip.s6_addr[i] <<= 1; + int p = i*8 + j; + ip.s6_addr[i] |= (p >= m) ? 0 : b[p]; + } + } + // get test representation of ip + char s_ip[INET6_ADDRSTRLEN]; + if (!inet_ntop(AF_INET6, &ip, s_ip, INET6_ADDRSTRLEN)) error(1, errno, "bits2ip6"); + // print ip with mask to buffer + int res = snprintf(s, size, "%s/%d", s_ip, m); + // if printing to buffer has failed + if (res < 0 || res >= size) error(1, errno, "bits2ip6"); +} + +void ip42bits(char *s, bits b, mask_t *m) { + // split ip and mask + char *p = strchr(s, '/'); + if (p) { + *p = 0; + ++p; + unsigned long l = strtol(p, &p, 10); + if (*p || l > IP4LEN) error(1, errno, "ip42bits: bad mask"); + *m = l; + } else { + *m = IP4LEN; + } + // convert ip to binary + struct in4_addr ip; + if (!inet_pton(AF_INET, s, &ip)) error(1, errno, "ip42bits"); + // convert binary ip to bits + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 8; ++j) { + int p = i*8 + 8-1-j; + b[p] = (p > *m) ? 0 : (ip.s4_addr[i] & 1); + ip.s4_addr[i] >>= 1; + } + } +} + +void ip62bits(char *s, bits b, mask_t *m) { + // split ip and mask + char *p = strchr(s, '/'); + if (p) { + *p = 0; + ++p; + unsigned long l = strtol(p, &p, 10); + if (*p || l > IP6LEN) error(1, errno, "ip62bits: bad mask"); + *m = l; + } else { + *m = IP6LEN; + } + // convert ip to binary + struct in6_addr ip; + if (!inet_pton(AF_INET6, s, &ip)) error(1, errno, "ip62bits"); + // convert binary ip to bits + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 8; ++j) { + int p = i*8 + 8-1-j; + b[p] = (p > *m) ? 0 : (ip.s6_addr[i] & 1); + ip.s6_addr[i] >>= 1; + } + } +} + +void print4(bits b, mask_t m) { + char s_ip[BUFLEN]; + bits2ip4(b, m, s_ip, BUFLEN); + puts(s_ip); +} + +void print6(bits b, mask_t m) { + char s_ip[BUFLEN]; + bits2ip6(b, m, s_ip, BUFLEN); + puts(s_ip); +} + +double nodes2mb(pos_t n) { + return sizeof(struct node) * (double)n / (double)(1 << 20); +} + +int main(void) { + int b[IPMAXLEN]; + mask_t m; + char s[BUFLEN]; + + iptree t = iptree_new(SIZE); + pos_t ip4 = iptree_popfree(t); + pos_t ip6 = iptree_popfree(t); + + while (1) { + if (!fgets(s, BUFLEN, stdin)) { + if (!feof(stdin)) error(1, errno, "input"); + break; + } + int l = strlen(s); + if (!l) continue; + if (s[l - 1] == '\n') s[l - 1] = 0; + if (!strchr(s, ':')) { + // ipv4 + ip42bits(s, b, &m); + iptree_add(t, ip4, b, m); + } else { + // ipv6 + ip62bits(s, b, &m); + iptree_add(t, ip6, b, m); + } + } + + iptree_traverse(t, ip4, print4); + iptree_traverse(t, ip6, print6); + + double mb_used = nodes2mb(maxpos); + double mb_all = nodes2mb(SIZE); + fprintf(stderr, "max nodes: %.2f%%, %d / %d, %.2fMB / %.2fMB\n", maxpos / (double)SIZE * 100.0, maxpos, SIZE, mb_used, mb_all); + + return 0; +}