Move all sources to subdirs
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/types.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <set>
|
||||
|
||||
#include "addr_util.h"
|
||||
|
||||
bool string_to_addr(std::string str, bool parse_port, int default_port, struct sockaddr_storage *addr)
|
||||
{
|
||||
if (parse_port)
|
||||
{
|
||||
int p = str.rfind(':');
|
||||
if (p != std::string::npos && !(str.length() > 0 && str[p-1] == ']')) // "[ipv6]" which contains ':'
|
||||
{
|
||||
char null_byte = 0;
|
||||
int scanned = sscanf(str.c_str()+p+1, "%d%c", &default_port, &null_byte);
|
||||
if (scanned != 1 || default_port >= 0x10000)
|
||||
return false;
|
||||
str = str.substr(0, p);
|
||||
}
|
||||
}
|
||||
if (inet_pton(AF_INET, str.c_str(), &((struct sockaddr_in*)addr)->sin_addr) == 1)
|
||||
{
|
||||
addr->ss_family = AF_INET;
|
||||
((struct sockaddr_in*)addr)->sin_port = htons(default_port);
|
||||
return true;
|
||||
}
|
||||
if (str.length() >= 2 && str[0] == '[' && str[str.length()-1] == ']')
|
||||
str = str.substr(1, str.length()-2);
|
||||
if (inet_pton(AF_INET6, str.c_str(), &((struct sockaddr_in6*)addr)->sin6_addr) == 1)
|
||||
{
|
||||
addr->ss_family = AF_INET6;
|
||||
((struct sockaddr_in6*)addr)->sin6_port = htons(default_port);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string addr_to_string(const sockaddr_storage &addr)
|
||||
{
|
||||
char peer_str[256];
|
||||
bool ok = false;
|
||||
int port;
|
||||
if (addr.ss_family == AF_INET)
|
||||
{
|
||||
ok = !!inet_ntop(AF_INET, &((sockaddr_in*)&addr)->sin_addr, peer_str, 256);
|
||||
port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
||||
}
|
||||
else if (addr.ss_family == AF_INET6)
|
||||
{
|
||||
ok = !!inet_ntop(AF_INET6, &((sockaddr_in6*)&addr)->sin6_addr, peer_str, 256);
|
||||
port = ntohs(((sockaddr_in6*)&addr)->sin6_port);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Unknown address family "+std::to_string(addr.ss_family));
|
||||
if (!ok)
|
||||
throw std::runtime_error(std::string("inet_ntop: ") + strerror(errno));
|
||||
return std::string(peer_str)+":"+std::to_string(port);
|
||||
}
|
||||
|
||||
static bool cidr_match(const in_addr &addr, const in_addr &net, uint8_t bits)
|
||||
{
|
||||
if (bits == 0)
|
||||
{
|
||||
// C99 6.5.7 (3): u32 << 32 is undefined behaviour
|
||||
return true;
|
||||
}
|
||||
return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
|
||||
}
|
||||
|
||||
static bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits)
|
||||
{
|
||||
const uint32_t *a = address.s6_addr32;
|
||||
const uint32_t *n = network.s6_addr32;
|
||||
int bits_whole, bits_incomplete;
|
||||
bits_whole = bits >> 5; // number of whole u32
|
||||
bits_incomplete = bits & 0x1F; // number of bits in incomplete u32
|
||||
if (bits_whole && memcmp(a, n, bits_whole << 2))
|
||||
return false;
|
||||
if (bits_incomplete)
|
||||
{
|
||||
uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
|
||||
if ((a[bits_whole] ^ n[bits_whole]) & mask)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct addr_mask_t
|
||||
{
|
||||
sa_family_t family;
|
||||
in_addr ipv4;
|
||||
in6_addr ipv6;
|
||||
uint8_t bits;
|
||||
};
|
||||
|
||||
std::vector<std::string> getifaddr_list(std::vector<std::string> mask_cfg, bool include_v6)
|
||||
{
|
||||
std::vector<addr_mask_t> masks;
|
||||
for (auto mask: mask_cfg)
|
||||
{
|
||||
unsigned bits = 0;
|
||||
int p = mask.find('/');
|
||||
if (p != std::string::npos)
|
||||
{
|
||||
char null_byte = 0;
|
||||
if (sscanf(mask.c_str()+p+1, "%u%c", &bits, &null_byte) != 1 || bits > 128)
|
||||
{
|
||||
throw std::runtime_error((include_v6 ? "Invalid IPv4 address mask: " : "Invalid IP address mask: ") + mask);
|
||||
}
|
||||
mask = mask.substr(0, p);
|
||||
}
|
||||
in_addr ipv4;
|
||||
in6_addr ipv6;
|
||||
if (inet_pton(AF_INET, mask.c_str(), &ipv4) == 1)
|
||||
{
|
||||
if (bits > 32)
|
||||
{
|
||||
throw std::runtime_error((include_v6 ? "Invalid IPv4 address mask: " : "Invalid IP address mask: ") + mask);
|
||||
}
|
||||
masks.push_back((addr_mask_t){ .family = AF_INET, .ipv4 = ipv4, .bits = (uint8_t)bits });
|
||||
}
|
||||
else if (include_v6 && inet_pton(AF_INET6, mask.c_str(), &ipv6) == 1)
|
||||
{
|
||||
masks.push_back((addr_mask_t){ .family = AF_INET6, .ipv6 = ipv6, .bits = (uint8_t)bits });
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error((include_v6 ? "Invalid IPv4 address mask: " : "Invalid IP address mask: ") + mask);
|
||||
}
|
||||
}
|
||||
std::set<std::string> addresses;
|
||||
ifaddrs *list, *ifa;
|
||||
if (getifaddrs(&list) == -1)
|
||||
{
|
||||
throw std::runtime_error(std::string("getifaddrs: ") + strerror(errno));
|
||||
}
|
||||
for (ifa = list; ifa != NULL; ifa = ifa->ifa_next)
|
||||
{
|
||||
if (!ifa->ifa_addr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int family = ifa->ifa_addr->sa_family;
|
||||
if ((family == AF_INET || family == AF_INET6 && include_v6) &&
|
||||
// Do not skip loopback addresses if the address filter is specified
|
||||
(ifa->ifa_flags & (IFF_UP | IFF_RUNNING | (masks.size() ? 0 : IFF_LOOPBACK))) == (IFF_UP | IFF_RUNNING))
|
||||
{
|
||||
void *addr_ptr;
|
||||
if (family == AF_INET)
|
||||
{
|
||||
addr_ptr = &((sockaddr_in *)ifa->ifa_addr)->sin_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr_ptr = &((sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
|
||||
}
|
||||
if (masks.size() > 0)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < masks.size(); i++)
|
||||
{
|
||||
if (masks[i].family == family && (family == AF_INET
|
||||
? cidr_match(*(in_addr*)addr_ptr, masks[i].ipv4, masks[i].bits)
|
||||
: cidr6_match(*(in6_addr*)addr_ptr, masks[i].ipv6, masks[i].bits)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= masks.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
char addr[INET6_ADDRSTRLEN];
|
||||
if (!inet_ntop(family, addr_ptr, addr, INET6_ADDRSTRLEN))
|
||||
{
|
||||
throw std::runtime_error(std::string("inet_ntop: ") + strerror(errno));
|
||||
}
|
||||
addresses.insert(std::string(addr));
|
||||
}
|
||||
}
|
||||
freeifaddrs(list);
|
||||
return std::vector<std::string>(addresses.begin(), addresses.end());
|
||||
}
|
||||
|
||||
int create_and_bind_socket(std::string bind_address, int bind_port, int listen_backlog, int *listening_port)
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
if (!string_to_addr(bind_address, 0, bind_port, &addr))
|
||||
{
|
||||
throw std::runtime_error("bind address "+bind_address+" is not valid");
|
||||
}
|
||||
|
||||
int listen_fd = socket(addr.ss_family, SOCK_STREAM, 0);
|
||||
if (listen_fd < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("socket: ") + strerror(errno));
|
||||
}
|
||||
int enable = 1;
|
||||
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||
|
||||
if (bind(listen_fd, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
close(listen_fd);
|
||||
throw std::runtime_error(std::string("bind: ") + strerror(errno));
|
||||
}
|
||||
if (listening_port)
|
||||
{
|
||||
if (bind_port == 0)
|
||||
{
|
||||
socklen_t len = sizeof(addr);
|
||||
if (getsockname(listen_fd, (sockaddr *)&addr, &len) == -1)
|
||||
{
|
||||
close(listen_fd);
|
||||
throw std::runtime_error(std::string("getsockname: ") + strerror(errno));
|
||||
}
|
||||
*listening_port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
*listening_port = bind_port;
|
||||
}
|
||||
}
|
||||
|
||||
if (listen(listen_fd, listen_backlog ? listen_backlog : 128) < 0)
|
||||
{
|
||||
close(listen_fd);
|
||||
throw std::runtime_error(std::string("listen: ") + strerror(errno));
|
||||
}
|
||||
|
||||
return listen_fd;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool string_to_addr(std::string str, bool parse_port, int default_port, struct sockaddr_storage *addr);
|
||||
std::string addr_to_string(const sockaddr_storage &addr);
|
||||
std::vector<std::string> getifaddr_list(std::vector<std::string> mask_cfg = std::vector<std::string>(), bool include_v6 = false);
|
||||
int create_and_bind_socket(std::string bind_address, int bind_port, int listen_backlog, int *listening_port);
|
||||
@@ -0,0 +1,225 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include <stdexcept>
|
||||
#include "allocator.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
allocator::allocator(uint64_t blocks)
|
||||
{
|
||||
if (blocks >= 0x80000000 || blocks <= 1)
|
||||
{
|
||||
throw std::invalid_argument("blocks");
|
||||
}
|
||||
uint64_t p2 = 1;
|
||||
total = 0;
|
||||
while (p2 * 64 < blocks)
|
||||
{
|
||||
total += p2;
|
||||
p2 = p2 * 64;
|
||||
}
|
||||
total += (blocks+63) / 64;
|
||||
mask = new uint64_t[total];
|
||||
size = free = blocks;
|
||||
last_one_mask = (blocks % 64) == 0
|
||||
? UINT64_MAX
|
||||
: (((uint64_t)1 << (blocks % 64)) - 1);
|
||||
for (uint64_t i = 0; i < total; i++)
|
||||
{
|
||||
mask[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
allocator::~allocator()
|
||||
{
|
||||
delete[] mask;
|
||||
}
|
||||
|
||||
bool allocator::get(uint64_t addr)
|
||||
{
|
||||
if (addr >= size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint64_t p2 = 1, offset = 0;
|
||||
while (p2 * 64 < size)
|
||||
{
|
||||
offset += p2;
|
||||
p2 = p2 * 64;
|
||||
}
|
||||
return ((mask[offset + addr/64] >> (addr % 64)) & 1);
|
||||
}
|
||||
|
||||
void allocator::set(uint64_t addr, bool value)
|
||||
{
|
||||
if (addr >= size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
uint64_t p2 = 1, offset = 0;
|
||||
while (p2 * 64 < size)
|
||||
{
|
||||
offset += p2;
|
||||
p2 = p2 * 64;
|
||||
}
|
||||
uint64_t cur_addr = addr;
|
||||
bool is_last = true;
|
||||
uint64_t value64 = value ? 1 : 0;
|
||||
while (1)
|
||||
{
|
||||
uint64_t last = offset + cur_addr/64;
|
||||
uint64_t bit = cur_addr % 64;
|
||||
if (((mask[last] >> bit) & 1) != value64)
|
||||
{
|
||||
if (is_last)
|
||||
{
|
||||
free += value ? -1 : 1;
|
||||
}
|
||||
if (value)
|
||||
{
|
||||
mask[last] = mask[last] | ((uint64_t)1 << bit);
|
||||
if (mask[last] != (!is_last || cur_addr/64 < size/64
|
||||
? UINT64_MAX : last_one_mask))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mask[last] = mask[last] & ~((uint64_t)1 << bit);
|
||||
}
|
||||
is_last = false;
|
||||
if (p2 > 1)
|
||||
{
|
||||
p2 = p2 / 64;
|
||||
offset -= p2;
|
||||
cur_addr /= 64;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t allocator::find_free()
|
||||
{
|
||||
uint64_t p2 = 1, offset = 0, addr = 0, f, i;
|
||||
while (p2 < size)
|
||||
{
|
||||
if (offset+addr >= total)
|
||||
{
|
||||
return UINT64_MAX;
|
||||
}
|
||||
uint64_t m = mask[offset + addr];
|
||||
for (i = 0, f = 1; i < 64; i++, f <<= 1)
|
||||
{
|
||||
if (!(m & f))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 64)
|
||||
{
|
||||
// No space
|
||||
return UINT64_MAX;
|
||||
}
|
||||
addr = (addr * 64) | i;
|
||||
offset += p2;
|
||||
p2 = p2 * 64;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
uint64_t allocator::get_free_count()
|
||||
{
|
||||
return free;
|
||||
}
|
||||
|
||||
// FIXME: Move to utils?
|
||||
void bitmap_set(void *bitmap, uint64_t start, uint64_t len, uint64_t bitmap_granularity)
|
||||
{
|
||||
if (start == 0 && len == 32*bitmap_granularity)
|
||||
*((uint32_t*)bitmap) = UINT32_MAX;
|
||||
else if (start == 0 && len == 64*bitmap_granularity)
|
||||
*((uint64_t*)bitmap) = UINT64_MAX;
|
||||
else
|
||||
{
|
||||
unsigned bit_start = start / bitmap_granularity;
|
||||
unsigned bit_end = ((start + len) + bitmap_granularity - 1) / bitmap_granularity;
|
||||
while (bit_start < bit_end)
|
||||
{
|
||||
if (!(bit_start & 7) && bit_end >= bit_start+8)
|
||||
{
|
||||
((uint8_t*)bitmap)[bit_start / 8] = UINT8_MAX;
|
||||
bit_start += 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
((uint8_t*)bitmap)[bit_start / 8] |= 1 << (bit_start % 8);
|
||||
bit_start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bitmap_clear(void *bitmap, uint64_t start, uint64_t len, uint64_t bitmap_granularity)
|
||||
{
|
||||
if (start == 0 && len == 32*bitmap_granularity)
|
||||
*((uint32_t*)bitmap) = 0;
|
||||
else if (start == 0 && len == 64*bitmap_granularity)
|
||||
*((uint64_t*)bitmap) = 0;
|
||||
else
|
||||
{
|
||||
unsigned bit_start = start / bitmap_granularity;
|
||||
unsigned bit_end = ((start + len) + bitmap_granularity - 1) / bitmap_granularity;
|
||||
while (bit_start < bit_end)
|
||||
{
|
||||
if (!(bit_start & 7) && bit_end >= bit_start+8)
|
||||
{
|
||||
((uint8_t*)bitmap)[bit_start / 8] = 0;
|
||||
bit_start += 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
((uint8_t*)bitmap)[bit_start / 8] &= (0xFF ^ (1 << (bit_start % 8)));
|
||||
bit_start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bitmap_check(void *bitmap, uint64_t start, uint64_t len, uint64_t bitmap_granularity)
|
||||
{
|
||||
bool r = false;
|
||||
if (start == 0 && len == 32*bitmap_granularity)
|
||||
r = !!*((uint32_t*)bitmap);
|
||||
else if (start == 0 && len == 64*bitmap_granularity)
|
||||
r = !!*((uint64_t*)bitmap);
|
||||
else
|
||||
{
|
||||
unsigned bit_start = start / bitmap_granularity;
|
||||
unsigned bit_end = ((start + len) + bitmap_granularity - 1) / bitmap_granularity;
|
||||
while (bit_start < bit_end)
|
||||
{
|
||||
if (!(bit_start & 7) && bit_end >= bit_start+8)
|
||||
{
|
||||
r = r || !!((uint8_t*)bitmap)[bit_start / 8];
|
||||
bit_start += 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = r || (((uint8_t*)bitmap)[bit_start / 8] & (1 << (bit_start % 8)));
|
||||
bit_start++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Hierarchical bitmap allocator
|
||||
class allocator
|
||||
{
|
||||
uint64_t total;
|
||||
uint64_t size;
|
||||
uint64_t free;
|
||||
uint64_t last_one_mask;
|
||||
uint64_t *mask;
|
||||
public:
|
||||
allocator(uint64_t blocks);
|
||||
~allocator();
|
||||
bool get(uint64_t addr);
|
||||
void set(uint64_t addr, bool value);
|
||||
uint64_t find_free();
|
||||
uint64_t get_free_count();
|
||||
};
|
||||
|
||||
void bitmap_set(void *bitmap, uint64_t start, uint64_t len, uint64_t bitmap_granularity);
|
||||
void bitmap_clear(void *bitmap, uint64_t start, uint64_t len, uint64_t bitmap_granularity);
|
||||
bool bitmap_check(void *bitmap, uint64_t start, uint64_t len, uint64_t bitmap_granularity);
|
||||
@@ -0,0 +1,374 @@
|
||||
/* crc32c.c -- compute CRC-32C using the Intel crc32 instruction
|
||||
* Copyright (C) 2013 Mark Adler
|
||||
* Version 1.1 1 Aug 2013 Mark Adler
|
||||
*/
|
||||
|
||||
/*
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Mark Adler
|
||||
madler@alumni.caltech.edu
|
||||
*/
|
||||
|
||||
/* Use hardware CRC instruction on Intel SSE 4.2 processors. This computes a
|
||||
CRC-32C, *not* the CRC-32 used by Ethernet and zip, gzip, etc. A software
|
||||
version is provided as a fall-back, as well as for speed comparisons. */
|
||||
|
||||
/* Version history:
|
||||
1.0 10 Feb 2013 First version
|
||||
1.1 1 Aug 2013 Correct comments on why three crc instructions in parallel
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include "crc32c.h"
|
||||
|
||||
/* CRC-32C (iSCSI) polynomial in reversed bit order. */
|
||||
#define POLY 0x82f63b78
|
||||
|
||||
/* Table for a quadword-at-a-time software crc. */
|
||||
static __thread int crc32_sw_init = 0;
|
||||
static uint32_t crc32c_table[8][256];
|
||||
|
||||
/* Construct table for software CRC-32C calculation. */
|
||||
static void crc32c_init_sw(void)
|
||||
{
|
||||
uint32_t n, crc, k;
|
||||
crc32_sw_init = 1;
|
||||
for (n = 0; n < 256; n++)
|
||||
{
|
||||
crc = n;
|
||||
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
|
||||
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
|
||||
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
|
||||
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
|
||||
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
|
||||
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
|
||||
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
|
||||
crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
|
||||
crc32c_table[0][n] = crc;
|
||||
}
|
||||
for (n = 0; n < 256; n++)
|
||||
{
|
||||
crc = crc32c_table[0][n];
|
||||
for (k = 1; k < 8; k++)
|
||||
{
|
||||
crc = crc32c_table[0][crc & 0xff] ^ (crc >> 8);
|
||||
crc32c_table[k][n] = crc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Table-driven software version as a fall-back. This is about 15 times slower
|
||||
than using the hardware instructions. This assumes little-endian integers,
|
||||
as is the case on Intel processors that the assembler code here is for. */
|
||||
static uint32_t crc32c_sw(uint32_t crci, const void *buf, size_t len)
|
||||
{
|
||||
const unsigned char *next = (const unsigned char*)buf;
|
||||
uint64_t crc;
|
||||
|
||||
if (!crc32_sw_init)
|
||||
crc32c_init_sw();
|
||||
crc = crci ^ 0xffffffff;
|
||||
while (len && ((uintptr_t)next & 7) != 0)
|
||||
{
|
||||
crc = crc32c_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
while (len >= 8)
|
||||
{
|
||||
crc ^= *(uint64_t *)next;
|
||||
crc = crc32c_table[7][crc & 0xff] ^
|
||||
crc32c_table[6][(crc >> 8) & 0xff] ^
|
||||
crc32c_table[5][(crc >> 16) & 0xff] ^
|
||||
crc32c_table[4][(crc >> 24) & 0xff] ^
|
||||
crc32c_table[3][(crc >> 32) & 0xff] ^
|
||||
crc32c_table[2][(crc >> 40) & 0xff] ^
|
||||
crc32c_table[1][(crc >> 48) & 0xff] ^
|
||||
crc32c_table[0][crc >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
while (len)
|
||||
{
|
||||
crc = crc32c_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
return (uint32_t)crc ^ 0xffffffff;
|
||||
}
|
||||
|
||||
/* Multiply a matrix times a vector over the Galois field of two elements,
|
||||
GF(2). Each element is a bit in an unsigned integer. mat must have at
|
||||
least as many entries as the power of two for most significant one bit in
|
||||
vec. */
|
||||
static inline uint32_t gf2_matrix_times(uint32_t *mat, uint32_t vec)
|
||||
{
|
||||
uint32_t sum;
|
||||
|
||||
sum = 0;
|
||||
while (vec)
|
||||
{
|
||||
if (vec & 1)
|
||||
sum ^= *mat;
|
||||
vec >>= 1;
|
||||
mat++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* Multiply a matrix by itself over GF(2). Both mat and square must have 32
|
||||
rows. */
|
||||
static inline void gf2_matrix_square(uint32_t *square, uint32_t *mat)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 32; n++)
|
||||
square[n] = gf2_matrix_times(mat, mat[n]);
|
||||
}
|
||||
|
||||
/* Construct an operator to apply len zeros to a crc. len must be a power of
|
||||
two. If len is not a power of two, then the result is the same as for the
|
||||
largest power of two less than len. The result for len == 0 is the same as
|
||||
for len == 1. A version of this routine could be easily written for any
|
||||
len, but that is not needed for this application. */
|
||||
static void crc32c_zeros_op(uint32_t *even, size_t len)
|
||||
{
|
||||
int n;
|
||||
uint32_t row;
|
||||
uint32_t odd[32]; /* odd-power-of-two zeros operator */
|
||||
|
||||
/* put operator for one zero bit in odd */
|
||||
odd[0] = POLY; /* CRC-32C polynomial */
|
||||
row = 1;
|
||||
for (n = 1; n < 32; n++)
|
||||
{
|
||||
odd[n] = row;
|
||||
row <<= 1;
|
||||
}
|
||||
|
||||
/* put operator for two zero bits in even */
|
||||
gf2_matrix_square(even, odd);
|
||||
|
||||
/* put operator for four zero bits in odd */
|
||||
gf2_matrix_square(odd, even);
|
||||
|
||||
/* first square will put the operator for one zero byte (eight zero bits),
|
||||
in even -- next square puts operator for two zero bytes in odd, and so
|
||||
on, until len has been rotated down to zero */
|
||||
do
|
||||
{
|
||||
gf2_matrix_square(even, odd);
|
||||
len >>= 1;
|
||||
if (len == 0)
|
||||
return;
|
||||
gf2_matrix_square(odd, even);
|
||||
len >>= 1;
|
||||
} while (len);
|
||||
|
||||
/* answer ended up in odd -- copy to even */
|
||||
for (n = 0; n < 32; n++)
|
||||
even[n] = odd[n];
|
||||
}
|
||||
|
||||
/* Take a length and build four lookup tables for applying the zeros operator
|
||||
for that length, byte-by-byte on the operand. */
|
||||
static void crc32c_zeros(uint32_t zeros[][256], size_t len)
|
||||
{
|
||||
uint32_t n;
|
||||
uint32_t op[32];
|
||||
|
||||
crc32c_zeros_op(op, len);
|
||||
for (n = 0; n < 256; n++)
|
||||
{
|
||||
zeros[0][n] = gf2_matrix_times(op, n);
|
||||
zeros[1][n] = gf2_matrix_times(op, n << 8);
|
||||
zeros[2][n] = gf2_matrix_times(op, n << 16);
|
||||
zeros[3][n] = gf2_matrix_times(op, n << 24);
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply the zeros operator table to crc. */
|
||||
static inline uint32_t crc32c_shift(uint32_t zeros[][256], uint32_t crc)
|
||||
{
|
||||
return zeros[0][crc & 0xff] ^ zeros[1][(crc >> 8) & 0xff] ^
|
||||
zeros[2][(crc >> 16) & 0xff] ^ zeros[3][crc >> 24];
|
||||
}
|
||||
|
||||
/* Block sizes for three-way parallel crc computation. LONG and SHORT must
|
||||
both be powers of two. The associated string constants must be set
|
||||
accordingly, for use in constructing the assembler instructions. */
|
||||
#define LONG 8192
|
||||
#define LONGx1 "8192"
|
||||
#define LONGx2 "16384"
|
||||
#define SHORT 256
|
||||
#define SHORTx1 "256"
|
||||
#define SHORTx2 "512"
|
||||
|
||||
/* Tables for hardware crc that shift a crc by LONG and SHORT zeros. */
|
||||
static __thread int crc32c_hw_init = 0;
|
||||
static uint32_t crc32c_long[4][256];
|
||||
static uint32_t crc32c_short[4][256];
|
||||
|
||||
/* Initialize tables for shifting crcs. */
|
||||
static void crc32c_init_hw(void)
|
||||
{
|
||||
crc32c_hw_init = 1;
|
||||
crc32c_zeros(crc32c_long, LONG);
|
||||
crc32c_zeros(crc32c_short, SHORT);
|
||||
}
|
||||
|
||||
/* Compute CRC-32C using the Intel hardware instruction. */
|
||||
static uint32_t crc32c_hw(uint32_t crc, const void *buf, size_t len)
|
||||
{
|
||||
#ifndef __x86_64__
|
||||
return 0;
|
||||
#else
|
||||
const unsigned char *next = (const unsigned char*)buf;
|
||||
const unsigned char *end;
|
||||
uint64_t crc0, crc1, crc2; /* need to be 64 bits for crc32q */
|
||||
|
||||
/* populate shift tables the first time through */
|
||||
if (!crc32c_hw_init)
|
||||
crc32c_init_hw();
|
||||
|
||||
/* pre-process the crc */
|
||||
crc0 = crc ^ 0xffffffff;
|
||||
|
||||
/* compute the crc for up to seven leading bytes to bring the data pointer
|
||||
to an eight-byte boundary */
|
||||
while (len && ((uintptr_t)next & 7) != 0)
|
||||
{
|
||||
__asm__(
|
||||
"crc32b\t" "(%1), %0"
|
||||
: "=r"(crc0)
|
||||
: "r"(next), "0"(crc0)
|
||||
);
|
||||
next++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* compute the crc on sets of LONG*3 bytes, executing three independent crc
|
||||
instructions, each on LONG bytes -- this is optimized for the Nehalem,
|
||||
Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
|
||||
throughput of one crc per cycle, but a latency of three cycles */
|
||||
while (len >= LONG*3)
|
||||
{
|
||||
crc1 = 0;
|
||||
crc2 = 0;
|
||||
end = next + LONG;
|
||||
do
|
||||
{
|
||||
__asm__(
|
||||
"crc32q\t" "(%3), %0\n\t"
|
||||
"crc32q\t" LONGx1 "(%3), %1\n\t"
|
||||
"crc32q\t" LONGx2 "(%3), %2"
|
||||
: "=r"(crc0), "=r"(crc1), "=r"(crc2)
|
||||
: "r"(next), "0"(crc0), "1"(crc1), "2"(crc2)
|
||||
);
|
||||
next += 8;
|
||||
} while (next < end);
|
||||
crc0 = crc32c_shift(crc32c_long, crc0) ^ crc1;
|
||||
crc0 = crc32c_shift(crc32c_long, crc0) ^ crc2;
|
||||
next += LONG*2;
|
||||
len -= LONG*3;
|
||||
}
|
||||
|
||||
/* do the same thing, but now on SHORT*3 blocks for the remaining data less
|
||||
than a LONG*3 block */
|
||||
while (len >= SHORT*3)
|
||||
{
|
||||
crc1 = 0;
|
||||
crc2 = 0;
|
||||
end = next + SHORT;
|
||||
do
|
||||
{
|
||||
__asm__(
|
||||
"crc32q\t" "(%3), %0\n\t"
|
||||
"crc32q\t" SHORTx1 "(%3), %1\n\t"
|
||||
"crc32q\t" SHORTx2 "(%3), %2"
|
||||
: "=r"(crc0), "=r"(crc1), "=r"(crc2)
|
||||
: "r"(next), "0"(crc0), "1"(crc1), "2"(crc2)
|
||||
);
|
||||
next += 8;
|
||||
} while (next < end);
|
||||
crc0 = crc32c_shift(crc32c_short, crc0) ^ crc1;
|
||||
crc0 = crc32c_shift(crc32c_short, crc0) ^ crc2;
|
||||
next += SHORT*2;
|
||||
len -= SHORT*3;
|
||||
}
|
||||
|
||||
/* compute the crc on the remaining eight-byte units less than a SHORT*3
|
||||
block */
|
||||
end = next + (len - (len & 7));
|
||||
while (next < end)
|
||||
{
|
||||
__asm__(
|
||||
"crc32q\t" "(%1), %0"
|
||||
: "=r"(crc0)
|
||||
: "r"(next), "0"(crc0)
|
||||
);
|
||||
next += 8;
|
||||
}
|
||||
len &= 7;
|
||||
|
||||
/* compute the crc for up to seven trailing bytes */
|
||||
while (len)
|
||||
{
|
||||
__asm__(
|
||||
"crc32b\t" "(%1), %0"
|
||||
: "=r"(crc0)
|
||||
: "r"(next), "0"(crc0)
|
||||
);
|
||||
next++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* return a post-processed crc */
|
||||
return (uint32_t)crc0 ^ 0xffffffff;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check for SSE 4.2. SSE 4.2 was first supported in Nehalem processors
|
||||
introduced in November, 2008. This does not check for the existence of the
|
||||
cpuid instruction itself, which was introduced on the 486SL in 1992, so this
|
||||
will fail on earlier x86 processors. cpuid works on all Pentium and later
|
||||
processors. */
|
||||
#define SSE42(have) \
|
||||
do { \
|
||||
uint32_t eax, ecx; \
|
||||
eax = 1; \
|
||||
__asm__("cpuid" \
|
||||
: "=c"(ecx) \
|
||||
: "a"(eax) \
|
||||
: "%ebx", "%edx"); \
|
||||
(have) = (ecx >> 20) & 1; \
|
||||
} while (0)
|
||||
|
||||
/* Compute a CRC-32C. If the crc32 instruction is available, use the hardware
|
||||
version. Otherwise, use the software version. */
|
||||
uint32_t crc32c(uint32_t crc, const void *buf, size_t len)
|
||||
{
|
||||
#ifndef __x86_64__
|
||||
return crc32c_sw(crc, buf, len);
|
||||
#else
|
||||
int sse42;
|
||||
SSE42(sse42);
|
||||
return sse42 ? crc32c_hw(crc, buf, len) : crc32c_sw(crc, buf, len);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// https://software.intel.com/sites/landingpage/IntrinsicsGuide/
|
||||
// unsigned int _mm_crc32_u16 (unsigned int crc, unsigned short v)
|
||||
// unsigned int _mm_crc32_u32 (unsigned int crc, unsigned int v)
|
||||
// unsigned __int64 _mm_crc32_u64 (unsigned __int64 crc, unsigned __int64 v)
|
||||
// unsigned int _mm_crc32_u8 (unsigned int crc, unsigned char v)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
uint32_t crc32c(uint32_t crc, const void *buf, size_t len);
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "epoll_manager.h"
|
||||
|
||||
#define MAX_EPOLL_EVENTS 64
|
||||
|
||||
epoll_manager_t::epoll_manager_t(ring_loop_t *ringloop)
|
||||
{
|
||||
this->ringloop = ringloop;
|
||||
this->pending = false;
|
||||
|
||||
epoll_fd = epoll_create(1);
|
||||
if (epoll_fd < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("epoll_create: ") + strerror(errno));
|
||||
}
|
||||
|
||||
tfd = new timerfd_manager_t([this](int fd, bool wr, std::function<void(int, int)> handler) { set_fd_handler(fd, wr, handler); });
|
||||
|
||||
if (ringloop)
|
||||
{
|
||||
consumer.loop = [this]()
|
||||
{
|
||||
if (pending)
|
||||
handle_uring_event();
|
||||
};
|
||||
ringloop->register_consumer(&consumer);
|
||||
handle_uring_event();
|
||||
}
|
||||
}
|
||||
|
||||
epoll_manager_t::~epoll_manager_t()
|
||||
{
|
||||
if (ringloop)
|
||||
{
|
||||
ringloop->unregister_consumer(&consumer);
|
||||
}
|
||||
if (tfd)
|
||||
{
|
||||
delete tfd;
|
||||
tfd = NULL;
|
||||
}
|
||||
close(epoll_fd);
|
||||
}
|
||||
|
||||
int epoll_manager_t::get_fd()
|
||||
{
|
||||
return epoll_fd;
|
||||
}
|
||||
|
||||
void epoll_manager_t::set_fd_handler(int fd, bool wr, std::function<void(int, int)> handler)
|
||||
{
|
||||
if (handler != NULL)
|
||||
{
|
||||
bool exists = epoll_handlers.find(fd) != epoll_handlers.end();
|
||||
epoll_event ev;
|
||||
ev.data.fd = fd;
|
||||
ev.events = (wr ? EPOLLOUT : 0) | EPOLLIN | EPOLLRDHUP | EPOLLET;
|
||||
if (epoll_ctl(epoll_fd, exists ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev) < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
// The FD is probably already closed
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
|
||||
epoll_handlers.erase(fd);
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error(std::string("epoll_ctl: ") + strerror(errno));
|
||||
}
|
||||
epoll_handlers[fd] = handler;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0 && errno != ENOENT)
|
||||
{
|
||||
throw std::runtime_error(std::string("epoll_ctl: ") + strerror(errno));
|
||||
}
|
||||
epoll_handlers.erase(fd);
|
||||
}
|
||||
}
|
||||
|
||||
void epoll_manager_t::handle_uring_event()
|
||||
{
|
||||
io_uring_sqe *sqe = ringloop->get_sqe();
|
||||
if (!sqe)
|
||||
{
|
||||
// Don't handle epoll events until we manage to post the next event handler
|
||||
// otherwise we'll fall out of sync with EPOLLET
|
||||
pending = true;
|
||||
ringloop->wakeup();
|
||||
return;
|
||||
}
|
||||
pending = false;
|
||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
||||
my_uring_prep_poll_add(sqe, epoll_fd, POLLIN);
|
||||
data->callback = [this](ring_data_t *data)
|
||||
{
|
||||
if (data->res < 0 && data->res != -ECANCELED)
|
||||
{
|
||||
throw std::runtime_error(std::string("epoll failed: ") + strerror(-data->res));
|
||||
}
|
||||
handle_uring_event();
|
||||
};
|
||||
ringloop->submit();
|
||||
handle_events(0);
|
||||
}
|
||||
|
||||
void epoll_manager_t::handle_events(int timeout)
|
||||
{
|
||||
int nfds;
|
||||
epoll_event events[MAX_EPOLL_EVENTS];
|
||||
do
|
||||
{
|
||||
nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout);
|
||||
timeout = 0;
|
||||
for (int i = 0; i < nfds; i++)
|
||||
{
|
||||
auto cb_it = epoll_handlers.find(events[i].data.fd);
|
||||
if (cb_it != epoll_handlers.end())
|
||||
{
|
||||
auto & cb = cb_it->second;
|
||||
cb(events[i].data.fd, events[i].events);
|
||||
}
|
||||
}
|
||||
} while (nfds == MAX_EPOLL_EVENTS);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "ringloop.h"
|
||||
#include "timerfd_manager.h"
|
||||
|
||||
class epoll_manager_t
|
||||
{
|
||||
int epoll_fd;
|
||||
bool pending;
|
||||
ring_consumer_t consumer;
|
||||
ring_loop_t *ringloop;
|
||||
std::map<int, std::function<void(int, int)>> epoll_handlers;
|
||||
|
||||
void handle_uring_event();
|
||||
public:
|
||||
epoll_manager_t(ring_loop_t *ringloop);
|
||||
~epoll_manager_t();
|
||||
int get_fd();
|
||||
void set_fd_handler(int fd, bool wr, std::function<void(int, int)> handler);
|
||||
void handle_events(int timeout);
|
||||
|
||||
timerfd_manager_t *tfd;
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
// Kill atomics in fio headers
|
||||
#define _STDATOMIC_H
|
||||
#include "fio/arch/arch.h"
|
||||
|
||||
#undef atomic_load_acquire
|
||||
#undef atomic_store_release
|
||||
#define atomic_load_acquire(p) *(p)
|
||||
#define atomic_store_release(p, v) (*(p)) = (v)
|
||||
|
||||
#define CONFIG_HAVE_GETTID
|
||||
#define CONFIG_SYNC_FILE_RANGE
|
||||
#define CONFIG_PWRITEV2
|
||||
extern "C" {
|
||||
#include "fio/fio.h"
|
||||
#include "fio/optgroup.h"
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
inline void* memalign_or_die(size_t alignment, size_t size)
|
||||
{
|
||||
void *buf = memalign(alignment, size);
|
||||
if (!buf)
|
||||
{
|
||||
printf("Failed to allocate %zu bytes\n", size);
|
||||
exit(1);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline void* malloc_or_die(size_t size)
|
||||
{
|
||||
void *buf = malloc(size);
|
||||
if (!buf)
|
||||
{
|
||||
printf("Failed to allocate %zu bytes\n", size);
|
||||
exit(1);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline void* realloc_or_die(void *ptr, size_t size)
|
||||
{
|
||||
void *buf = realloc(ptr, size);
|
||||
if (!buf)
|
||||
{
|
||||
printf("Failed to allocate %zu bytes\n", size);
|
||||
exit(1);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline void* calloc_or_die(size_t nmemb, size_t size)
|
||||
{
|
||||
void *buf = calloc(nmemb, size);
|
||||
if (!buf)
|
||||
{
|
||||
printf("Failed to allocate %zu bytes\n", size * nmemb);
|
||||
exit(1);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include "ringloop.h"
|
||||
|
||||
ring_loop_t::ring_loop_t(int qd)
|
||||
{
|
||||
int ret = io_uring_queue_init(qd, &ring, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("io_uring_queue_init: ") + strerror(-ret));
|
||||
}
|
||||
free_ring_data_ptr = *ring.sq.kring_entries;
|
||||
ring_datas = (struct ring_data_t*)calloc(free_ring_data_ptr, sizeof(ring_data_t));
|
||||
free_ring_data = (int*)malloc(sizeof(int) * free_ring_data_ptr);
|
||||
if (!ring_datas || !free_ring_data)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
for (int i = 0; i < free_ring_data_ptr; i++)
|
||||
{
|
||||
free_ring_data[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
ring_loop_t::~ring_loop_t()
|
||||
{
|
||||
free(free_ring_data);
|
||||
free(ring_datas);
|
||||
io_uring_queue_exit(&ring);
|
||||
if (ring_eventfd)
|
||||
{
|
||||
close(ring_eventfd);
|
||||
}
|
||||
}
|
||||
|
||||
void ring_loop_t::register_consumer(ring_consumer_t *consumer)
|
||||
{
|
||||
unregister_consumer(consumer);
|
||||
consumers.push_back(consumer);
|
||||
}
|
||||
|
||||
void ring_loop_t::wakeup()
|
||||
{
|
||||
loop_again = true;
|
||||
}
|
||||
|
||||
void ring_loop_t::unregister_consumer(ring_consumer_t *consumer)
|
||||
{
|
||||
for (int i = 0; i < consumers.size(); i++)
|
||||
{
|
||||
if (consumers[i] == consumer)
|
||||
{
|
||||
consumers.erase(consumers.begin()+i, consumers.begin()+i+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ring_loop_t::loop()
|
||||
{
|
||||
if (ring_eventfd >= 0)
|
||||
{
|
||||
// Reset eventfd counter
|
||||
uint64_t ctr = 0;
|
||||
int r = read(ring_eventfd, &ctr, 8);
|
||||
if (r < 0 && errno != EAGAIN && errno != EINTR)
|
||||
{
|
||||
fprintf(stderr, "Error resetting eventfd: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
struct io_uring_cqe *cqe;
|
||||
while (!io_uring_peek_cqe(&ring, &cqe))
|
||||
{
|
||||
struct ring_data_t *d = (struct ring_data_t*)cqe->user_data;
|
||||
if (d->callback)
|
||||
{
|
||||
// First free ring_data item, then call the callback
|
||||
// so it has at least 1 free slot for the next event
|
||||
// which is required for EPOLLET to function properly
|
||||
struct ring_data_t dl;
|
||||
dl.iov = d->iov;
|
||||
dl.res = cqe->res;
|
||||
dl.callback.swap(d->callback);
|
||||
free_ring_data[free_ring_data_ptr++] = d - ring_datas;
|
||||
dl.callback(&dl);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Warning: empty callback in SQE\n");
|
||||
free_ring_data[free_ring_data_ptr++] = d - ring_datas;
|
||||
}
|
||||
io_uring_cqe_seen(&ring, cqe);
|
||||
}
|
||||
do
|
||||
{
|
||||
loop_again = false;
|
||||
for (int i = 0; i < consumers.size(); i++)
|
||||
{
|
||||
consumers[i]->loop();
|
||||
if (immediate_queue.size())
|
||||
{
|
||||
immediate_queue2.swap(immediate_queue);
|
||||
for (auto & cb: immediate_queue2)
|
||||
cb();
|
||||
immediate_queue2.clear();
|
||||
}
|
||||
}
|
||||
} while (loop_again);
|
||||
}
|
||||
|
||||
unsigned ring_loop_t::save()
|
||||
{
|
||||
return ring.sq.sqe_tail;
|
||||
}
|
||||
|
||||
void ring_loop_t::restore(unsigned sqe_tail)
|
||||
{
|
||||
assert(ring.sq.sqe_tail >= sqe_tail);
|
||||
for (unsigned i = sqe_tail; i < ring.sq.sqe_tail; i++)
|
||||
{
|
||||
free_ring_data[free_ring_data_ptr++] = ((ring_data_t*)ring.sq.sqes[i & *ring.sq.kring_mask].user_data) - ring_datas;
|
||||
}
|
||||
ring.sq.sqe_tail = sqe_tail;
|
||||
}
|
||||
|
||||
int ring_loop_t::sqes_left()
|
||||
{
|
||||
struct io_uring_sq *sq = &ring.sq;
|
||||
unsigned int head = io_uring_smp_load_acquire(sq->khead);
|
||||
unsigned int next = sq->sqe_tail + 1;
|
||||
int left = *sq->kring_entries - (next - head);
|
||||
if (left > free_ring_data_ptr)
|
||||
{
|
||||
// return min(sqes left, ring_datas left)
|
||||
return free_ring_data_ptr;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
int ring_loop_t::register_eventfd()
|
||||
{
|
||||
if (ring_eventfd >= 0)
|
||||
{
|
||||
return ring_eventfd;
|
||||
}
|
||||
ring_eventfd = eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK);
|
||||
if (ring_eventfd < 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
int r = io_uring_register_eventfd(&ring, ring_eventfd);
|
||||
if (r < 0)
|
||||
{
|
||||
close(ring_eventfd);
|
||||
ring_eventfd = -1;
|
||||
return r;
|
||||
}
|
||||
return ring_eventfd;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _LARGEFILE64_SOURCE
|
||||
#define _LARGEFILE64_SOURCE
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <liburing.h>
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#define RINGLOOP_DEFAULT_SIZE 1024
|
||||
|
||||
static inline void my_uring_prep_rw(int op, struct io_uring_sqe *sqe, int fd, const void *addr, unsigned len, off_t offset)
|
||||
{
|
||||
// Prepare a read/write operation without clearing user_data
|
||||
// Very recently, 22 Dec 2021, liburing finally got this change too (8ecd3fd959634df81d66af8b3a69c16202a014e8)
|
||||
// But all versions prior to it (sadly) clear user_data
|
||||
__u64 user_data = sqe->user_data;
|
||||
io_uring_prep_rw(op, sqe, fd, addr, len, offset);
|
||||
sqe->user_data = user_data;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_readv(struct io_uring_sqe *sqe, int fd, const struct iovec *iovecs, unsigned nr_vecs, off_t offset)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_READV, sqe, fd, iovecs, nr_vecs, offset);
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_read_fixed(struct io_uring_sqe *sqe, int fd, void *buf, unsigned nbytes, off_t offset, int buf_index)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_READ_FIXED, sqe, fd, buf, nbytes, offset);
|
||||
sqe->buf_index = buf_index;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_writev(struct io_uring_sqe *sqe, int fd, const struct iovec *iovecs, unsigned nr_vecs, off_t offset)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_WRITEV, sqe, fd, iovecs, nr_vecs, offset);
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_write_fixed(struct io_uring_sqe *sqe, int fd, const void *buf, unsigned nbytes, off_t offset, int buf_index)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_WRITE_FIXED, sqe, fd, buf, nbytes, offset);
|
||||
sqe->buf_index = buf_index;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_recvmsg(struct io_uring_sqe *sqe, int fd, struct msghdr *msg, unsigned flags)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_RECVMSG, sqe, fd, msg, 1, 0);
|
||||
sqe->msg_flags = flags;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_sendmsg(struct io_uring_sqe *sqe, int fd, const struct msghdr *msg, unsigned flags)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_SENDMSG, sqe, fd, msg, 1, 0);
|
||||
sqe->msg_flags = flags;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_poll_add(struct io_uring_sqe *sqe, int fd, short poll_mask)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_POLL_ADD, sqe, fd, NULL, 0, 0);
|
||||
sqe->poll_events = poll_mask;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_poll_remove(struct io_uring_sqe *sqe, void *user_data)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_POLL_REMOVE, sqe, 0, user_data, 0, 0);
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_fsync(struct io_uring_sqe *sqe, int fd, unsigned fsync_flags)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_FSYNC, sqe, fd, NULL, 0, 0);
|
||||
sqe->fsync_flags = fsync_flags;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_nop(struct io_uring_sqe *sqe)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_NOP, sqe, 0, NULL, 0, 0);
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_timeout(struct io_uring_sqe *sqe, struct __kernel_timespec *ts, unsigned count, unsigned flags)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_TIMEOUT, sqe, 0, ts, 1, count);
|
||||
sqe->timeout_flags = flags;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_timeout_remove(struct io_uring_sqe *sqe, __u64 user_data, unsigned flags)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_TIMEOUT_REMOVE, sqe, 0, (void *)user_data, 0, 0);
|
||||
sqe->timeout_flags = flags;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_accept(struct io_uring_sqe *sqe, int fd, struct sockaddr *addr, socklen_t *addrlen, int flags)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_ACCEPT, sqe, fd, addr, 0, (__u64) addrlen);
|
||||
sqe->accept_flags = flags;
|
||||
}
|
||||
|
||||
static inline void my_uring_prep_cancel(struct io_uring_sqe *sqe, void *user_data, int flags)
|
||||
{
|
||||
my_uring_prep_rw(IORING_OP_ASYNC_CANCEL, sqe, 0, user_data, 0, 0);
|
||||
sqe->cancel_flags = flags;
|
||||
}
|
||||
|
||||
struct ring_data_t
|
||||
{
|
||||
struct iovec iov; // for single-entry read/write operations
|
||||
int res;
|
||||
std::function<void(ring_data_t*)> callback;
|
||||
};
|
||||
|
||||
struct ring_consumer_t
|
||||
{
|
||||
std::function<void(void)> loop;
|
||||
};
|
||||
|
||||
class ring_loop_t
|
||||
{
|
||||
std::vector<std::function<void()>> immediate_queue, immediate_queue2;
|
||||
std::vector<ring_consumer_t*> consumers;
|
||||
struct ring_data_t *ring_datas;
|
||||
int *free_ring_data;
|
||||
unsigned free_ring_data_ptr;
|
||||
bool loop_again;
|
||||
struct io_uring ring;
|
||||
int ring_eventfd = -1;
|
||||
public:
|
||||
ring_loop_t(int qd);
|
||||
~ring_loop_t();
|
||||
void register_consumer(ring_consumer_t *consumer);
|
||||
void unregister_consumer(ring_consumer_t *consumer);
|
||||
int register_eventfd();
|
||||
|
||||
inline struct io_uring_sqe* get_sqe()
|
||||
{
|
||||
if (free_ring_data_ptr == 0)
|
||||
return NULL;
|
||||
struct io_uring_sqe* sqe = io_uring_get_sqe(&ring);
|
||||
assert(sqe);
|
||||
*sqe = { 0 };
|
||||
io_uring_sqe_set_data(sqe, ring_datas + free_ring_data[--free_ring_data_ptr]);
|
||||
return sqe;
|
||||
}
|
||||
inline void set_immediate(const std::function<void()> cb)
|
||||
{
|
||||
immediate_queue.push_back(cb);
|
||||
}
|
||||
inline int submit()
|
||||
{
|
||||
return io_uring_submit(&ring);
|
||||
}
|
||||
inline int wait()
|
||||
{
|
||||
struct io_uring_cqe *cqe;
|
||||
return io_uring_wait_cqe(&ring, &cqe);
|
||||
}
|
||||
int sqes_left();
|
||||
inline unsigned space_left()
|
||||
{
|
||||
return free_ring_data_ptr;
|
||||
}
|
||||
inline bool has_work()
|
||||
{
|
||||
return loop_again;
|
||||
}
|
||||
|
||||
void loop();
|
||||
void wakeup();
|
||||
|
||||
unsigned save();
|
||||
void restore(unsigned sqe_tail);
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "rw_blocking.h"
|
||||
|
||||
int read_blocking(int fd, void *read_buf, size_t remaining)
|
||||
{
|
||||
size_t done = 0;
|
||||
while (done < remaining)
|
||||
{
|
||||
ssize_t r = read(fd, read_buf, remaining-done);
|
||||
if (r <= 0)
|
||||
{
|
||||
if (!errno)
|
||||
{
|
||||
// EOF
|
||||
return done;
|
||||
}
|
||||
else if (errno != EINTR && errno != EAGAIN && errno != EPIPE)
|
||||
{
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
done += r;
|
||||
read_buf = (uint8_t*)read_buf + r;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
int write_blocking(int fd, void *write_buf, size_t remaining)
|
||||
{
|
||||
size_t done = 0;
|
||||
while (done < remaining)
|
||||
{
|
||||
ssize_t r = write(fd, write_buf, remaining-done);
|
||||
if (r < 0)
|
||||
{
|
||||
if (errno != EINTR && errno != EAGAIN && errno != EPIPE)
|
||||
{
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
done += r;
|
||||
write_buf = (uint8_t*)write_buf + r;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
int readv_blocking(int fd, iovec *iov, int iovcnt)
|
||||
{
|
||||
int v = 0;
|
||||
int done = 0;
|
||||
while (v < iovcnt)
|
||||
{
|
||||
ssize_t r = readv(fd, iov+v, iovcnt-v);
|
||||
if (r < 0)
|
||||
{
|
||||
if (errno != EINTR && errno != EAGAIN && errno != EPIPE)
|
||||
{
|
||||
perror("writev");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
done += r;
|
||||
while (v < iovcnt)
|
||||
{
|
||||
if (iov[v].iov_len > r)
|
||||
{
|
||||
iov[v].iov_len -= r;
|
||||
iov[v].iov_base = (uint8_t*)iov[v].iov_base + r;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
r -= iov[v].iov_len;
|
||||
v++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
int writev_blocking(int fd, iovec *iov, int iovcnt)
|
||||
{
|
||||
int v = 0;
|
||||
int done = 0;
|
||||
while (v < iovcnt)
|
||||
{
|
||||
ssize_t r = writev(fd, iov+v, iovcnt-v);
|
||||
if (r < 0)
|
||||
{
|
||||
if (errno != EINTR && errno != EAGAIN && errno != EPIPE)
|
||||
{
|
||||
perror("writev");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
done += r;
|
||||
while (v < iovcnt)
|
||||
{
|
||||
if (iov[v].iov_len > r)
|
||||
{
|
||||
iov[v].iov_len -= r;
|
||||
iov[v].iov_base = (uint8_t*)iov[v].iov_base + r;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
r -= iov[v].iov_len;
|
||||
v++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
int sendv_blocking(int fd, iovec *iov, int iovcnt, int flags)
|
||||
{
|
||||
struct msghdr msg = { 0 };
|
||||
int v = 0;
|
||||
int done = 0;
|
||||
while (v < iovcnt)
|
||||
{
|
||||
msg.msg_iov = iov+v;
|
||||
msg.msg_iovlen = iovcnt-v;
|
||||
ssize_t r = sendmsg(fd, &msg, flags);
|
||||
if (r < 0)
|
||||
{
|
||||
if (errno != EINTR && errno != EAGAIN && errno != EPIPE)
|
||||
{
|
||||
perror("sendmsg");
|
||||
exit(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
done += r;
|
||||
while (v < iovcnt)
|
||||
{
|
||||
if (iov[v].iov_len > r)
|
||||
{
|
||||
iov[v].iov_len -= r;
|
||||
iov[v].iov_base = (uint8_t*)iov[v].iov_base + r;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
r -= iov[v].iov_len;
|
||||
v++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
int read_blocking(int fd, void *read_buf, size_t remaining);
|
||||
int write_blocking(int fd, void *write_buf, size_t remaining);
|
||||
int readv_blocking(int fd, iovec *iov, int iovcnt);
|
||||
int writev_blocking(int fd, iovec *iov, int iovcnt);
|
||||
int sendv_blocking(int fd, iovec *iov, int iovcnt, int flags);
|
||||
@@ -0,0 +1,158 @@
|
||||
/*********************************************************************
|
||||
* Filename: sha256.c
|
||||
* Author: Brad Conte (brad AT bradconte.com)
|
||||
* Copyright:
|
||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
||||
* Details: Implementation of the SHA-256 hashing algorithm.
|
||||
SHA-256 is one of the three algorithms in the SHA2
|
||||
specification. The others, SHA-384 and SHA-512, are not
|
||||
offered in this implementation.
|
||||
Algorithm specification can be found here:
|
||||
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
|
||||
This implementation uses little endian byte order.
|
||||
*********************************************************************/
|
||||
|
||||
/*************************** HEADER FILES ***************************/
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include "sha256.h"
|
||||
|
||||
/****************************** MACROS ******************************/
|
||||
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
|
||||
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
|
||||
|
||||
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
|
||||
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
|
||||
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
|
||||
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
|
||||
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
|
||||
|
||||
/**************************** VARIABLES *****************************/
|
||||
static const WORD k[64] = {
|
||||
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
|
||||
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
|
||||
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
|
||||
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
|
||||
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
|
||||
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
|
||||
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
|
||||
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
|
||||
};
|
||||
|
||||
/*********************** FUNCTION DEFINITIONS ***********************/
|
||||
void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
|
||||
{
|
||||
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
|
||||
|
||||
for (i = 0, j = 0; i < 16; ++i, j += 4)
|
||||
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
|
||||
for ( ; i < 64; ++i)
|
||||
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
|
||||
|
||||
a = ctx->state[0];
|
||||
b = ctx->state[1];
|
||||
c = ctx->state[2];
|
||||
d = ctx->state[3];
|
||||
e = ctx->state[4];
|
||||
f = ctx->state[5];
|
||||
g = ctx->state[6];
|
||||
h = ctx->state[7];
|
||||
|
||||
for (i = 0; i < 64; ++i) {
|
||||
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
|
||||
t2 = EP0(a) + MAJ(a,b,c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
ctx->state[0] += a;
|
||||
ctx->state[1] += b;
|
||||
ctx->state[2] += c;
|
||||
ctx->state[3] += d;
|
||||
ctx->state[4] += e;
|
||||
ctx->state[5] += f;
|
||||
ctx->state[6] += g;
|
||||
ctx->state[7] += h;
|
||||
}
|
||||
|
||||
void sha256_init(SHA256_CTX *ctx)
|
||||
{
|
||||
ctx->datalen = 0;
|
||||
ctx->bitlen = 0;
|
||||
ctx->state[0] = 0x6a09e667;
|
||||
ctx->state[1] = 0xbb67ae85;
|
||||
ctx->state[2] = 0x3c6ef372;
|
||||
ctx->state[3] = 0xa54ff53a;
|
||||
ctx->state[4] = 0x510e527f;
|
||||
ctx->state[5] = 0x9b05688c;
|
||||
ctx->state[6] = 0x1f83d9ab;
|
||||
ctx->state[7] = 0x5be0cd19;
|
||||
}
|
||||
|
||||
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
|
||||
{
|
||||
WORD i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
ctx->data[ctx->datalen] = data[i];
|
||||
ctx->datalen++;
|
||||
if (ctx->datalen == 64) {
|
||||
sha256_transform(ctx, ctx->data);
|
||||
ctx->bitlen += 512;
|
||||
ctx->datalen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
||||
{
|
||||
WORD i;
|
||||
|
||||
i = ctx->datalen;
|
||||
|
||||
// Pad whatever data is left in the buffer.
|
||||
if (ctx->datalen < 56) {
|
||||
ctx->data[i++] = 0x80;
|
||||
while (i < 56)
|
||||
ctx->data[i++] = 0x00;
|
||||
}
|
||||
else {
|
||||
ctx->data[i++] = 0x80;
|
||||
while (i < 64)
|
||||
ctx->data[i++] = 0x00;
|
||||
sha256_transform(ctx, ctx->data);
|
||||
memset(ctx->data, 0, 56);
|
||||
}
|
||||
|
||||
// Append to the padding the total message's length in bits and transform.
|
||||
ctx->bitlen += ctx->datalen * 8;
|
||||
ctx->data[63] = ctx->bitlen;
|
||||
ctx->data[62] = ctx->bitlen >> 8;
|
||||
ctx->data[61] = ctx->bitlen >> 16;
|
||||
ctx->data[60] = ctx->bitlen >> 24;
|
||||
ctx->data[59] = ctx->bitlen >> 32;
|
||||
ctx->data[58] = ctx->bitlen >> 40;
|
||||
ctx->data[57] = ctx->bitlen >> 48;
|
||||
ctx->data[56] = ctx->bitlen >> 56;
|
||||
sha256_transform(ctx, ctx->data);
|
||||
|
||||
// Since this implementation uses little endian byte ordering and SHA uses big endian,
|
||||
// reverse all the bytes when copying the final state to the output hash.
|
||||
for (i = 0; i < 4; ++i) {
|
||||
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*********************************************************************
|
||||
* Filename: sha256.h
|
||||
* Author: Brad Conte (brad AT bradconte.com)
|
||||
* Copyright:
|
||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
||||
* Details: Defines the API for the corresponding SHA1 implementation.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef SHA256_H
|
||||
#define SHA256_H
|
||||
|
||||
/*************************** HEADER FILES ***************************/
|
||||
#include <stddef.h>
|
||||
|
||||
/****************************** MACROS ******************************/
|
||||
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**************************** DATA TYPES ****************************/
|
||||
typedef unsigned char BYTE; // 8-bit byte
|
||||
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
|
||||
|
||||
typedef struct {
|
||||
BYTE data[64];
|
||||
WORD datalen;
|
||||
unsigned long long bitlen;
|
||||
WORD state[8];
|
||||
} SHA256_CTX;
|
||||
|
||||
/*********************** FUNCTION DECLARATIONS **********************/
|
||||
void sha256_init(SHA256_CTX *ctx);
|
||||
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
|
||||
void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // SHA256_H
|
||||
@@ -0,0 +1,465 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "str_util.h"
|
||||
|
||||
std::string base64_encode(const std::string &in)
|
||||
{
|
||||
std::string out;
|
||||
unsigned val = 0;
|
||||
int valb = -6;
|
||||
for (unsigned char c: in)
|
||||
{
|
||||
val = (val << 8) + c;
|
||||
valb += 8;
|
||||
while (valb >= 0)
|
||||
{
|
||||
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val>>valb) & 0x3F]);
|
||||
valb -= 6;
|
||||
}
|
||||
}
|
||||
if (valb > -6)
|
||||
out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val<<8)>>(valb+8)) & 0x3F]);
|
||||
while (out.size() % 4)
|
||||
out.push_back('=');
|
||||
return out;
|
||||
}
|
||||
|
||||
static char T[256] = { 0 };
|
||||
|
||||
std::string base64_decode(const std::string &in)
|
||||
{
|
||||
std::string out;
|
||||
if (T[0] == 0)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
T[i] = -1;
|
||||
for (int i = 0; i < 64; i++)
|
||||
T[(unsigned char)("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i])] = i;
|
||||
}
|
||||
unsigned val = 0;
|
||||
int valb = -8;
|
||||
for (unsigned char c: in)
|
||||
{
|
||||
if (T[c] == -1)
|
||||
break;
|
||||
val = (val<<6) + T[c];
|
||||
valb += 6;
|
||||
if (valb >= 0)
|
||||
{
|
||||
out.push_back(char((val >> valb) & 0xFF));
|
||||
valb -= 8;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string strtoupper(const std::string & in)
|
||||
{
|
||||
std::string s = in;
|
||||
for (int i = 0; i < s.length(); i++)
|
||||
{
|
||||
s[i] = toupper(s[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string strtolower(const std::string & in)
|
||||
{
|
||||
std::string s = in;
|
||||
for (int i = 0; i < s.length(); i++)
|
||||
{
|
||||
s[i] = tolower(s[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string trim(const std::string & in, const char *rm_chars)
|
||||
{
|
||||
int begin = in.find_first_not_of(rm_chars);
|
||||
if (begin == -1)
|
||||
return "";
|
||||
int end = in.find_last_not_of(rm_chars);
|
||||
return in.substr(begin, end+1-begin);
|
||||
}
|
||||
|
||||
std::string str_replace(const std::string & in, const std::string & needle, const std::string & replacement)
|
||||
{
|
||||
std::string res;
|
||||
int pos = 0, p2;
|
||||
while ((p2 = in.find(needle, pos)) >= 0)
|
||||
{
|
||||
res += in.substr(pos, p2-pos);
|
||||
res += replacement;
|
||||
pos = p2 + replacement.size();
|
||||
}
|
||||
if (!pos)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
return res + in.substr(pos);
|
||||
}
|
||||
|
||||
uint64_t stoull_full(const std::string & str, int base)
|
||||
{
|
||||
if (isspace(str[0]))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
char *end = NULL;
|
||||
uint64_t r = strtoull(str.c_str(), &end, base);
|
||||
if (end != str.c_str()+str.length())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
uint64_t parse_size(std::string size_str, bool *ok)
|
||||
{
|
||||
if (!size_str.length())
|
||||
{
|
||||
if (ok)
|
||||
*ok = false;
|
||||
return 0;
|
||||
}
|
||||
uint64_t mul = 1;
|
||||
char type_char = tolower(size_str[size_str.length()-1]);
|
||||
if (type_char == 'k' || type_char == 'm' || type_char == 'g' || type_char == 't')
|
||||
{
|
||||
if (type_char == 'k')
|
||||
mul = (uint64_t)1<<10;
|
||||
else if (type_char == 'm')
|
||||
mul = (uint64_t)1<<20;
|
||||
else if (type_char == 'g')
|
||||
mul = (uint64_t)1<<30;
|
||||
else /*if (type_char == 't')*/
|
||||
mul = (uint64_t)1<<40;
|
||||
size_str = size_str.substr(0, size_str.length()-1);
|
||||
}
|
||||
uint64_t size = stoull_full(size_str, 0) * mul;
|
||||
if (ok)
|
||||
*ok = !(size == 0 && size_str != "0" && (size_str != "" || mul != 1));
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint64_t size_thresh[] = { (uint64_t)1024*1024*1024*1024, (uint64_t)1024*1024*1024, (uint64_t)1024*1024, 1024, 0 };
|
||||
static uint64_t size_thresh_d[] = { (uint64_t)1000000000000, (uint64_t)1000000000, (uint64_t)1000000, (uint64_t)1000, 0 };
|
||||
static const int size_thresh_n = sizeof(size_thresh)/sizeof(size_thresh[0]);
|
||||
static const char *size_unit = "TGMKB";
|
||||
|
||||
std::string format_size(uint64_t size, bool nobytes)
|
||||
{
|
||||
uint64_t *thr = nobytes ? size_thresh_d : size_thresh;
|
||||
char buf[256];
|
||||
for (int i = 0; i < size_thresh_n; i++)
|
||||
{
|
||||
if (size >= thr[i] || i >= size_thresh_n-1)
|
||||
{
|
||||
double value = thr[i] ? (double)size/thr[i] : size;
|
||||
int l = snprintf(buf, sizeof(buf), "%.1f", value);
|
||||
assert(l < sizeof(buf)-2);
|
||||
if (buf[l-1] == '0')
|
||||
l -= 2;
|
||||
buf[l] = i == size_thresh_n-1 && nobytes ? 0 : ' ';
|
||||
buf[l+1] = i == size_thresh_n-1 && nobytes ? 0 : size_unit[i];
|
||||
buf[l+2] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
void print_help(const char *help_text, std::string exe_name, std::string cmd, bool all)
|
||||
{
|
||||
if (cmd == "" && all)
|
||||
{
|
||||
fwrite(help_text, strlen(help_text), 1, stdout);
|
||||
exit(0);
|
||||
}
|
||||
std::string filtered_text = "";
|
||||
const char *head_end = strstr(help_text, "COMMANDS:\n");
|
||||
if (head_end)
|
||||
{
|
||||
filtered_text += std::string(help_text, head_end-help_text);
|
||||
head_end += strlen("COMMANDS:\n");
|
||||
}
|
||||
const char *next_line = head_end ? head_end : help_text;
|
||||
if (cmd != "")
|
||||
{
|
||||
const char *cmd_start = NULL;
|
||||
bool matched = false, started = true, found = false;
|
||||
while ((next_line = strchr(next_line, '\n')))
|
||||
{
|
||||
next_line++;
|
||||
if (*next_line && !strncmp(next_line, exe_name.c_str(), exe_name.size()))
|
||||
{
|
||||
if (started)
|
||||
{
|
||||
if (cmd_start && matched)
|
||||
filtered_text += std::string(cmd_start, next_line-cmd_start);
|
||||
cmd_start = next_line;
|
||||
matched = started = false;
|
||||
}
|
||||
const char *var_start = next_line+exe_name.size()+1;
|
||||
const char *var_end = var_start;
|
||||
while (*var_end && !isspace(*var_end))
|
||||
var_end++;
|
||||
if (("|"+std::string(var_start, var_end-var_start)+"|").find("|"+cmd+"|") != std::string::npos)
|
||||
found = matched = true;
|
||||
}
|
||||
else if (*next_line && isspace(*next_line))
|
||||
started = true;
|
||||
else if (cmd_start && matched)
|
||||
{
|
||||
filtered_text += std::string(cmd_start, next_line-cmd_start);
|
||||
matched = started = false;
|
||||
}
|
||||
}
|
||||
while (filtered_text.size() > 1 &&
|
||||
filtered_text[filtered_text.size()-1] == '\n' &&
|
||||
filtered_text[filtered_text.size()-2] == '\n')
|
||||
{
|
||||
filtered_text.resize(filtered_text.size()-1);
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
filtered_text = "Unknown command: "+cmd+". Use "+exe_name+" --help for usage\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filtered_text += "COMMANDS:\n\n";
|
||||
while ((next_line = strchr(next_line, '\n')))
|
||||
{
|
||||
next_line++;
|
||||
if (*next_line && !strncmp(next_line, exe_name.c_str(), exe_name.size()))
|
||||
{
|
||||
const char *line_end = strchr(next_line, '\n');
|
||||
line_end = line_end ? line_end : next_line+strlen(next_line);
|
||||
filtered_text += " "+(line_end ? std::string(next_line, line_end-next_line) : std::string(next_line));
|
||||
filtered_text += "\n";
|
||||
}
|
||||
else if (*next_line && !isspace(next_line[0]))
|
||||
{
|
||||
filtered_text += "\n"+std::string(next_line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fwrite(filtered_text.data(), filtered_text.size(), 1, stdout);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
uint64_t parse_time(std::string time_str, bool *ok)
|
||||
{
|
||||
if (!time_str.length())
|
||||
{
|
||||
if (ok)
|
||||
*ok = false;
|
||||
return 0;
|
||||
}
|
||||
uint64_t mul = 1;
|
||||
char type_char = tolower(time_str[time_str.length()-1]);
|
||||
if (type_char == 's' || type_char == 'm' || type_char == 'h' || type_char == 'd' || type_char == 'y')
|
||||
{
|
||||
if (type_char == 's')
|
||||
mul = 1;
|
||||
else if (time_str[time_str.length()-1] == 'M')
|
||||
mul = 30*86400;
|
||||
else if (type_char == 'm')
|
||||
mul = 60;
|
||||
else if (type_char == 'h')
|
||||
mul = 3600;
|
||||
else if (type_char == 'd')
|
||||
mul = 86400;
|
||||
else /*if (type_char == 'y')*/
|
||||
mul = 86400*365;
|
||||
time_str = time_str.substr(0, time_str.length()-1);
|
||||
}
|
||||
uint64_t ts = stoull_full(time_str, 0) * mul;
|
||||
if (ok)
|
||||
*ok = !(ts == 0 && time_str != "0" && (time_str != "" || mul != 1));
|
||||
return ts;
|
||||
}
|
||||
|
||||
std::string read_all_fd(int fd)
|
||||
{
|
||||
int res_size = 0, res_alloc = 0;
|
||||
std::string res;
|
||||
while (1)
|
||||
{
|
||||
if (res_size >= res_alloc)
|
||||
res.resize((res_alloc = (res_alloc ? res_alloc*2 : 1024)));
|
||||
int r = read(fd, (char*)res.data()+res_size, res_alloc-res_size);
|
||||
if (r > 0)
|
||||
res_size += r;
|
||||
else if (!r || errno != EAGAIN && errno != EINTR)
|
||||
break;
|
||||
}
|
||||
res.resize(res_size);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string read_file(std::string file, bool allow_enoent)
|
||||
{
|
||||
std::string res;
|
||||
int fd = open(file.c_str(), O_RDONLY);
|
||||
if (fd < 0 || (res = read_all_fd(fd)) == "")
|
||||
{
|
||||
int err = errno;
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (!allow_enoent || err != ENOENT)
|
||||
fprintf(stderr, "Failed to read %s: %s (code %d)\n", file.c_str(), strerror(err), err);
|
||||
return "";
|
||||
}
|
||||
close(fd);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string str_repeat(const std::string & str, int times)
|
||||
{
|
||||
std::string r;
|
||||
for (int i = 0; i < times; i++)
|
||||
r += str;
|
||||
return r;
|
||||
}
|
||||
|
||||
size_t utf8_length(const std::string & s)
|
||||
{
|
||||
size_t len = 0;
|
||||
for (size_t i = 0; i < s.size(); i++)
|
||||
len += (s[i] & 0xC0) != 0x80;
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t utf8_length(const char *s)
|
||||
{
|
||||
size_t len = 0;
|
||||
for (; *s; s++)
|
||||
len += (*s & 0xC0) != 0x80;
|
||||
return len;
|
||||
}
|
||||
|
||||
std::vector<std::string> explode(const std::string & sep, const std::string & value, bool trim)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
size_t prev = 0;
|
||||
while (prev < value.size())
|
||||
{
|
||||
while (trim && prev < value.size() && isspace(value[prev]))
|
||||
prev++;
|
||||
size_t pos = value.find(sep, prev);
|
||||
if (pos == std::string::npos)
|
||||
pos = value.size();
|
||||
size_t next = pos+sep.size();
|
||||
while (trim && pos > prev && isspace(value[pos-1]))
|
||||
pos--;
|
||||
if (!trim || pos > prev)
|
||||
res.push_back(value.substr(prev, pos-prev));
|
||||
prev = next;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string scan_escaped(const std::string & cmd, size_t & pos, bool allow_unquoted)
|
||||
{
|
||||
return scan_escaped(cmd.data(), cmd.size(), pos, allow_unquoted);
|
||||
}
|
||||
|
||||
// extract possibly single- or double-quoted part of string with escape characters
|
||||
std::string scan_escaped(const char *cmd, size_t size, size_t & pos, bool allow_unquoted)
|
||||
{
|
||||
auto orig = pos;
|
||||
while (pos < size && is_white(cmd[pos]))
|
||||
pos++;
|
||||
if (pos >= size)
|
||||
{
|
||||
pos = orig;
|
||||
return "";
|
||||
}
|
||||
if (cmd[pos] != '"' && cmd[pos] != '\'')
|
||||
{
|
||||
if (!allow_unquoted)
|
||||
{
|
||||
pos = orig;
|
||||
return "";
|
||||
}
|
||||
auto pos2 = pos;
|
||||
while (pos2 < size && !is_white(cmd[pos2]))
|
||||
pos2++;
|
||||
auto key = std::string(cmd+pos, pos2-pos);
|
||||
pos = pos2;
|
||||
return key;
|
||||
}
|
||||
char quot = cmd[pos];
|
||||
pos++;
|
||||
std::string key;
|
||||
while (true)
|
||||
{
|
||||
auto pos2 = pos;
|
||||
while (pos2 < size && cmd[pos2] != '\\' && cmd[pos2] != quot)
|
||||
pos2++;
|
||||
if (pos2 >= size || pos2 == size-1 && cmd[pos2] == '\\')
|
||||
{
|
||||
// Unfinished string literal
|
||||
pos = orig;
|
||||
return "";
|
||||
}
|
||||
if (pos2 > pos)
|
||||
key += std::string(cmd+pos, pos2-pos);
|
||||
pos = pos2;
|
||||
if (cmd[pos] == quot)
|
||||
{
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
else /* if (cmd[pos] == '\\') */
|
||||
{
|
||||
key += cmd[++pos];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
std::string auto_addslashes(const std::string & str, const char *toescape)
|
||||
{
|
||||
auto pos = str.find_first_of(toescape);
|
||||
if (pos == std::string::npos)
|
||||
return str;
|
||||
return addslashes(str, toescape);
|
||||
}
|
||||
|
||||
std::string addslashes(const std::string & str, const char *toescape)
|
||||
{
|
||||
std::string res = "\"";
|
||||
auto pos = 0;
|
||||
while (pos < str.size())
|
||||
{
|
||||
auto pos2 = str.find_first_of(toescape, pos);
|
||||
if (pos2 == std::string::npos)
|
||||
return res + str.substr(pos) + "\"";
|
||||
res += str.substr(pos, pos2-pos)+"\\"+str[pos2];
|
||||
pos = pos2+1;
|
||||
}
|
||||
return res+"\"";
|
||||
}
|
||||
|
||||
std::string realpath_str(std::string path, bool nofail)
|
||||
{
|
||||
char *p = realpath((char*)path.c_str(), NULL);
|
||||
if (!p)
|
||||
{
|
||||
fprintf(stderr, "Failed to resolve %s: %s\n", path.c_str(), strerror(errno));
|
||||
return nofail ? path : "";
|
||||
}
|
||||
std::string rp(p);
|
||||
free(p);
|
||||
return rp;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define is_white(a) ((a) == ' ' || (a) == '\t' || (a) == '\r' || (a) == '\n')
|
||||
|
||||
std::string base64_encode(const std::string &in);
|
||||
std::string base64_decode(const std::string &in);
|
||||
uint64_t parse_size(std::string size_str, bool *ok = NULL);
|
||||
std::string strtoupper(const std::string & in);
|
||||
std::string strtolower(const std::string & in);
|
||||
std::string trim(const std::string & in, const char *rm_chars = " \n\r\t");
|
||||
std::string str_replace(const std::string & in, const std::string & needle, const std::string & replacement);
|
||||
uint64_t stoull_full(const std::string & str, int base = 0);
|
||||
std::string format_size(uint64_t size, bool nobytes = false);
|
||||
void print_help(const char *help_text, std::string exe_name, std::string cmd, bool all);
|
||||
uint64_t parse_time(std::string time_str, bool *ok = NULL);
|
||||
std::string read_all_fd(int fd);
|
||||
std::string read_file(std::string file, bool allow_enoent = false);
|
||||
std::string str_repeat(const std::string & str, int times);
|
||||
size_t utf8_length(const std::string & s);
|
||||
size_t utf8_length(const char *s);
|
||||
std::vector<std::string> explode(const std::string & sep, const std::string & value, bool trim);
|
||||
std::string scan_escaped(const char *cmd, size_t size, size_t & pos, bool allow_unquoted = true);
|
||||
std::string scan_escaped(const std::string & cmd, size_t & pos, bool allow_unquoted = true);
|
||||
std::string auto_addslashes(const std::string & str, const char *toescape = "\\\"");
|
||||
std::string addslashes(const std::string & str, const char *toescape = "\\\"");
|
||||
std::string realpath_str(std::string path, bool nofail = true);
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include "timerfd_manager.h"
|
||||
|
||||
timerfd_manager_t::timerfd_manager_t(std::function<void(int, bool, std::function<void(int, int)>)> set_fd_handler)
|
||||
{
|
||||
this->set_fd_handler = set_fd_handler;
|
||||
wait_state = 0;
|
||||
timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
|
||||
if (timerfd < 0)
|
||||
{
|
||||
throw std::runtime_error(std::string("timerfd_create: ") + strerror(errno));
|
||||
}
|
||||
set_fd_handler(timerfd, false, [this](int fd, int events)
|
||||
{
|
||||
handle_readable();
|
||||
});
|
||||
}
|
||||
|
||||
timerfd_manager_t::~timerfd_manager_t()
|
||||
{
|
||||
set_fd_handler(timerfd, false, NULL);
|
||||
close(timerfd);
|
||||
}
|
||||
|
||||
void timerfd_manager_t::inc_timer(timerfd_timer_t & t)
|
||||
{
|
||||
t.next.tv_sec += t.micros/1000000;
|
||||
t.next.tv_nsec += (t.micros%1000000)*1000;
|
||||
if (t.next.tv_nsec > 1000000000)
|
||||
{
|
||||
t.next.tv_sec++;
|
||||
t.next.tv_nsec -= 1000000000;
|
||||
}
|
||||
}
|
||||
|
||||
int timerfd_manager_t::set_timer(uint64_t millis, bool repeat, std::function<void(int)> callback)
|
||||
{
|
||||
return set_timer_us(millis*1000, repeat, callback);
|
||||
}
|
||||
|
||||
int timerfd_manager_t::set_timer_us(uint64_t micros, bool repeat, std::function<void(int)> callback)
|
||||
{
|
||||
int timer_id = id++;
|
||||
timespec start;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
timers.push_back({
|
||||
.id = timer_id,
|
||||
.micros = micros,
|
||||
.start = start,
|
||||
.next = start,
|
||||
.repeat = repeat,
|
||||
.callback = callback,
|
||||
});
|
||||
inc_timer(timers[timers.size()-1]);
|
||||
set_nearest();
|
||||
return timer_id;
|
||||
}
|
||||
|
||||
void timerfd_manager_t::clear_timer(int timer_id)
|
||||
{
|
||||
for (int i = 0; i < timers.size(); i++)
|
||||
{
|
||||
if (timers[i].id == timer_id)
|
||||
{
|
||||
timers.erase(timers.begin()+i, timers.begin()+i+1);
|
||||
if (nearest == i)
|
||||
{
|
||||
nearest = -1;
|
||||
wait_state = wait_state & ~1;
|
||||
}
|
||||
else if (nearest > i)
|
||||
{
|
||||
nearest--;
|
||||
}
|
||||
set_nearest();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void timerfd_manager_t::set_nearest()
|
||||
{
|
||||
if (onstack > 0)
|
||||
{
|
||||
// Prevent re-entry
|
||||
return;
|
||||
}
|
||||
onstack++;
|
||||
again:
|
||||
if (!timers.size())
|
||||
{
|
||||
nearest = -1;
|
||||
itimerspec exp = {};
|
||||
if (timerfd_settime(timerfd, 0, &exp, NULL))
|
||||
{
|
||||
throw std::runtime_error(std::string("timerfd_settime: ") + strerror(errno));
|
||||
}
|
||||
wait_state = wait_state & ~1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nearest = 0;
|
||||
for (int i = 1; i < timers.size(); i++)
|
||||
{
|
||||
if (timers[i].next.tv_sec < timers[nearest].next.tv_sec ||
|
||||
timers[i].next.tv_sec == timers[nearest].next.tv_sec &&
|
||||
timers[i].next.tv_nsec < timers[nearest].next.tv_nsec)
|
||||
{
|
||||
nearest = i;
|
||||
}
|
||||
}
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
itimerspec exp = {
|
||||
.it_interval = { 0 },
|
||||
.it_value = timers[nearest].next,
|
||||
};
|
||||
exp.it_value.tv_sec -= now.tv_sec;
|
||||
exp.it_value.tv_nsec -= now.tv_nsec;
|
||||
if (exp.it_value.tv_nsec < 0)
|
||||
{
|
||||
exp.it_value.tv_sec--;
|
||||
exp.it_value.tv_nsec += 1000000000;
|
||||
}
|
||||
if (exp.it_value.tv_sec < 0 || exp.it_value.tv_sec == 0 && exp.it_value.tv_nsec <= 0)
|
||||
{
|
||||
// It already happened
|
||||
// FIXME: Postpone to setImmediate/BH to avoid reenterability problems
|
||||
trigger_nearest();
|
||||
goto again;
|
||||
}
|
||||
if (timerfd_settime(timerfd, 0, &exp, NULL))
|
||||
{
|
||||
throw std::runtime_error(std::string("timerfd_settime: ") + strerror(errno));
|
||||
}
|
||||
wait_state = wait_state | 1;
|
||||
}
|
||||
onstack--;
|
||||
}
|
||||
|
||||
void timerfd_manager_t::handle_readable()
|
||||
{
|
||||
uint64_t n;
|
||||
size_t res = read(timerfd, &n, 8);
|
||||
if (res == 8 && nearest >= 0)
|
||||
{
|
||||
trigger_nearest();
|
||||
}
|
||||
wait_state = 0;
|
||||
set_nearest();
|
||||
}
|
||||
|
||||
void timerfd_manager_t::trigger_nearest()
|
||||
{
|
||||
int nearest_id = timers[nearest].id;
|
||||
auto cb = timers[nearest].callback;
|
||||
if (timers[nearest].repeat)
|
||||
{
|
||||
inc_timer(timers[nearest]);
|
||||
}
|
||||
else
|
||||
{
|
||||
timers.erase(timers.begin()+nearest, timers.begin()+nearest+1);
|
||||
}
|
||||
nearest = -1;
|
||||
cb(nearest_id);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
struct timerfd_timer_t
|
||||
{
|
||||
int id;
|
||||
uint64_t micros;
|
||||
timespec start, next;
|
||||
bool repeat;
|
||||
std::function<void(int)> callback;
|
||||
};
|
||||
|
||||
class timerfd_manager_t
|
||||
{
|
||||
int wait_state = 0;
|
||||
int timerfd;
|
||||
int nearest = -1;
|
||||
int id = 1;
|
||||
int onstack = 0;
|
||||
std::vector<timerfd_timer_t> timers;
|
||||
|
||||
void inc_timer(timerfd_timer_t & t);
|
||||
void set_nearest();
|
||||
void trigger_nearest();
|
||||
void handle_readable();
|
||||
public:
|
||||
std::function<void(int, bool, std::function<void(int, int)>)> set_fd_handler;
|
||||
|
||||
timerfd_manager_t(std::function<void(int, bool, std::function<void(int, int)>)> set_fd_handler);
|
||||
~timerfd_manager_t();
|
||||
int set_timer(uint64_t millis, bool repeat, std::function<void(int)> callback);
|
||||
int set_timer_us(uint64_t micros, bool repeat, std::function<void(int)> callback);
|
||||
void clear_timer(int timer_id);
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
inline void memxor(const void *r1, const void *r2, void *res, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
((uint8_t*)res)[i] = ((uint8_t*)r1)[i] ^ ((uint8_t*)r2)[i];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user