nghttpx: Use BlockAllocator per DownstreamConfig
This commit is contained in:
parent
f5285d1f5a
commit
272cfa320e
|
@ -804,7 +804,7 @@ namespace {
|
||||||
// as catch-all. We also parse protocol specified in |src_proto|.
|
// as catch-all. We also parse protocol specified in |src_proto|.
|
||||||
//
|
//
|
||||||
// This function returns 0 if it succeeds, or -1.
|
// This function returns 0 if it succeeds, or -1.
|
||||||
int parse_mapping(Config *config, DownstreamAddrConfig addr,
|
int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||||
const StringRef &src_pattern, const StringRef &src_params) {
|
const StringRef &src_pattern, const StringRef &src_params) {
|
||||||
// This returns at least 1 element (it could be empty string). We
|
// This returns at least 1 element (it could be empty string). We
|
||||||
// will append '/' to all patterns, so it becomes catch-all pattern.
|
// will append '/' to all patterns, so it becomes catch-all pattern.
|
||||||
|
@ -824,7 +824,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
|
||||||
addr.rise = params.rise;
|
addr.rise = params.rise;
|
||||||
addr.proto = params.proto;
|
addr.proto = params.proto;
|
||||||
addr.tls = params.tls;
|
addr.tls = params.tls;
|
||||||
addr.sni = ImmutableString{std::begin(params.sni), std::end(params.sni)};
|
addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
|
||||||
|
|
||||||
auto &routerconf = downstreamconf.router;
|
auto &routerconf = downstreamconf.router;
|
||||||
auto &router = routerconf.router;
|
auto &router = routerconf.router;
|
||||||
|
@ -833,18 +833,31 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
|
||||||
|
|
||||||
for (const auto &raw_pattern : mapping) {
|
for (const auto &raw_pattern : mapping) {
|
||||||
auto done = false;
|
auto done = false;
|
||||||
std::string pattern;
|
StringRef pattern;
|
||||||
auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
|
auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
|
||||||
if (slash == std::end(raw_pattern)) {
|
if (slash == std::end(raw_pattern)) {
|
||||||
// This effectively makes empty pattern to "/".
|
// This effectively makes empty pattern to "/". 2 for '/' and
|
||||||
pattern.assign(std::begin(raw_pattern), std::end(raw_pattern));
|
// terminal NULL character.
|
||||||
util::inp_strlower(pattern);
|
auto iov = make_byte_ref(downstreamconf.balloc, raw_pattern.size() + 2);
|
||||||
pattern += '/';
|
auto p = iov.base;
|
||||||
|
p = std::copy(std::begin(raw_pattern), std::end(raw_pattern), p);
|
||||||
|
util::inp_strlower(iov.base, p);
|
||||||
|
*p++ = '/';
|
||||||
|
*p = '\0';
|
||||||
|
pattern = StringRef{iov.base, p};
|
||||||
} else {
|
} else {
|
||||||
pattern.assign(std::begin(raw_pattern), slash);
|
auto path = http2::normalize_path(downstreamconf.balloc,
|
||||||
util::inp_strlower(pattern);
|
StringRef{slash, std::end(raw_pattern)},
|
||||||
pattern += http2::normalize_path(StringRef{slash, std::end(raw_pattern)},
|
StringRef{});
|
||||||
StringRef{});
|
auto iov = make_byte_ref(downstreamconf.balloc,
|
||||||
|
std::distance(std::begin(raw_pattern), slash) +
|
||||||
|
path.size() + 1);
|
||||||
|
auto p = iov.base;
|
||||||
|
p = std::copy(std::begin(raw_pattern), slash, p);
|
||||||
|
util::inp_strlower(iov.base, p);
|
||||||
|
p = std::copy(std::begin(path), std::end(path), p);
|
||||||
|
*p = '\0';
|
||||||
|
pattern = StringRef{iov.base, p};
|
||||||
}
|
}
|
||||||
for (auto &g : addr_groups) {
|
for (auto &g : addr_groups) {
|
||||||
if (g.pattern == pattern) {
|
if (g.pattern == pattern) {
|
||||||
|
@ -863,7 +876,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
auto idx = addr_groups.size();
|
auto idx = addr_groups.size();
|
||||||
addr_groups.emplace_back(StringRef{pattern});
|
addr_groups.emplace_back(pattern);
|
||||||
auto &g = addr_groups.back();
|
auto &g = addr_groups.back();
|
||||||
g.addrs.push_back(addr);
|
g.addrs.push_back(addr);
|
||||||
g.affinity = params.affinity;
|
g.affinity = params.affinity;
|
||||||
|
@ -886,10 +899,13 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
|
||||||
auto &router = wildcard_patterns.back().router;
|
auto &router = wildcard_patterns.back().router;
|
||||||
router.add_route(path, idx);
|
router.add_route(path, idx);
|
||||||
|
|
||||||
auto rev_host = host.str();
|
auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1);
|
||||||
std::reverse(std::begin(rev_host), std::end(rev_host));
|
auto p = iov.base;
|
||||||
|
p = std::reverse_copy(std::begin(host), std::end(host), p);
|
||||||
|
*p = '\0';
|
||||||
|
auto rev_host = StringRef{iov.base, p};
|
||||||
|
|
||||||
rw_router.add_route(StringRef{rev_host}, wildcard_patterns.size() - 1);
|
rw_router.add_route(rev_host, wildcard_patterns.size() - 1);
|
||||||
} else {
|
} else {
|
||||||
(*it).router.add_route(path, idx);
|
(*it).router.add_route(path, idx);
|
||||||
}
|
}
|
||||||
|
@ -897,7 +913,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
router.add_route(StringRef{g.pattern}, idx);
|
router.add_route(g.pattern, idx);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1804,12 +1820,14 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
|
|
||||||
switch (optid) {
|
switch (optid) {
|
||||||
case SHRPX_OPTID_BACKEND: {
|
case SHRPX_OPTID_BACKEND: {
|
||||||
|
auto &downstreamconf = *config->conn.downstream;
|
||||||
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
|
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
|
||||||
|
|
||||||
DownstreamAddrConfig addr{};
|
DownstreamAddrConfig addr{};
|
||||||
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
|
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
|
||||||
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
|
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
|
||||||
addr.host = ImmutableString(path, addr_end);
|
addr.host =
|
||||||
|
make_string_ref(downstreamconf.balloc, StringRef{path, addr_end});
|
||||||
addr.host_unix = true;
|
addr.host_unix = true;
|
||||||
} else {
|
} else {
|
||||||
if (split_host_port(host, sizeof(host), &port,
|
if (split_host_port(host, sizeof(host), &port,
|
||||||
|
@ -1817,7 +1835,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr.host = ImmutableString(host);
|
addr.host = make_string_ref(downstreamconf.balloc, StringRef{host});
|
||||||
addr.port = port;
|
addr.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3071,13 +3089,13 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||||
|
|
||||||
if (addr_groups.empty()) {
|
if (addr_groups.empty()) {
|
||||||
DownstreamAddrConfig addr{};
|
DownstreamAddrConfig addr{};
|
||||||
addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST);
|
addr.host = StringRef::from_lit(DEFAULT_DOWNSTREAM_HOST);
|
||||||
addr.port = DEFAULT_DOWNSTREAM_PORT;
|
addr.port = DEFAULT_DOWNSTREAM_PORT;
|
||||||
addr.proto = PROTO_HTTP1;
|
addr.proto = PROTO_HTTP1;
|
||||||
|
|
||||||
DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
|
DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
|
||||||
g.addrs.push_back(std::move(addr));
|
g.addrs.push_back(std::move(addr));
|
||||||
router.add_route(StringRef{g.pattern}, addr_groups.size());
|
router.add_route(g.pattern, addr_groups.size());
|
||||||
addr_groups.push_back(std::move(g));
|
addr_groups.push_back(std::move(g));
|
||||||
} else if (http2_proxy) {
|
} else if (http2_proxy) {
|
||||||
// We don't support host mapping in these cases. Move all
|
// We don't support host mapping in these cases. Move all
|
||||||
|
@ -3090,7 +3108,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||||
std::vector<DownstreamAddrGroupConfig>().swap(addr_groups);
|
std::vector<DownstreamAddrGroupConfig>().swap(addr_groups);
|
||||||
// maybe not necessary?
|
// maybe not necessary?
|
||||||
routerconf = RouterConfig{};
|
routerconf = RouterConfig{};
|
||||||
router.add_route(StringRef{catch_all.pattern}, addr_groups.size());
|
router.add_route(catch_all.pattern, addr_groups.size());
|
||||||
addr_groups.push_back(std::move(catch_all));
|
addr_groups.push_back(std::move(catch_all));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3100,7 +3118,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||||
auto &sni = tlsconf.backend_sni_name;
|
auto &sni = tlsconf.backend_sni_name;
|
||||||
for (auto &addr_group : addr_groups) {
|
for (auto &addr_group : addr_groups) {
|
||||||
for (auto &addr : addr_group.addrs) {
|
for (auto &addr : addr_group.addrs) {
|
||||||
addr.sni = ImmutableString{sni};
|
addr.sni = StringRef{sni};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3147,7 +3165,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||||
// for AF_UNIX socket, we use "localhost" as host for backend
|
// for AF_UNIX socket, we use "localhost" as host for backend
|
||||||
// hostport. This is used as Host header field to backend and
|
// hostport. This is used as Host header field to backend and
|
||||||
// not going to be passed to any syscalls.
|
// not going to be passed to any syscalls.
|
||||||
addr.hostport = ImmutableString::from_lit("localhost");
|
addr.hostport = StringRef::from_lit("localhost");
|
||||||
|
|
||||||
auto path = addr.host.c_str();
|
auto path = addr.host.c_str();
|
||||||
auto pathlen = addr.host.size();
|
auto pathlen = addr.host.size();
|
||||||
|
@ -3171,10 +3189,11 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr.hostport = ImmutableString(
|
addr.hostport =
|
||||||
util::make_http_hostport(StringRef(addr.host), addr.port));
|
util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port);
|
||||||
|
|
||||||
auto hostport = util::make_hostport(StringRef{addr.host}, addr.port);
|
auto hostport =
|
||||||
|
util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
|
||||||
|
|
||||||
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
|
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
|
||||||
downstreamconf.family, resolve_flags) == -1) {
|
downstreamconf.family, resolve_flags) == -1) {
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include "template.h"
|
#include "template.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
|
#include "allocator.h"
|
||||||
|
|
||||||
using namespace nghttp2;
|
using namespace nghttp2;
|
||||||
|
|
||||||
|
@ -375,13 +376,13 @@ struct UpstreamAddr {
|
||||||
struct DownstreamAddrConfig {
|
struct DownstreamAddrConfig {
|
||||||
Address addr;
|
Address addr;
|
||||||
// backend address. If |host_unix| is true, this is UNIX domain
|
// backend address. If |host_unix| is true, this is UNIX domain
|
||||||
// socket path.
|
// socket path. This must be NULL terminated string.
|
||||||
ImmutableString host;
|
StringRef host;
|
||||||
// <HOST>:<PORT>. This does not treat 80 and 443 specially. If
|
// <HOST>:<PORT>. This does not treat 80 and 443 specially. If
|
||||||
// |host_unix| is true, this is "localhost".
|
// |host_unix| is true, this is "localhost".
|
||||||
ImmutableString hostport;
|
StringRef hostport;
|
||||||
// hostname sent as SNI field
|
// hostname sent as SNI field
|
||||||
ImmutableString sni;
|
StringRef sni;
|
||||||
size_t fall;
|
size_t fall;
|
||||||
size_t rise;
|
size_t rise;
|
||||||
// Application protocol used in this group
|
// Application protocol used in this group
|
||||||
|
@ -404,9 +405,9 @@ struct AffinityHash {
|
||||||
|
|
||||||
struct DownstreamAddrGroupConfig {
|
struct DownstreamAddrGroupConfig {
|
||||||
DownstreamAddrGroupConfig(const StringRef &pattern)
|
DownstreamAddrGroupConfig(const StringRef &pattern)
|
||||||
: pattern(pattern.c_str(), pattern.size()), affinity(AFFINITY_NONE) {}
|
: pattern(pattern), affinity(AFFINITY_NONE) {}
|
||||||
|
|
||||||
ImmutableString pattern;
|
StringRef pattern;
|
||||||
std::vector<DownstreamAddrConfig> addrs;
|
std::vector<DownstreamAddrConfig> addrs;
|
||||||
// Bunch of session affinity hash. Only used if affinity ==
|
// Bunch of session affinity hash. Only used if affinity ==
|
||||||
// AFFINITY_IP.
|
// AFFINITY_IP.
|
||||||
|
@ -655,10 +656,11 @@ struct RateLimitConfig {
|
||||||
// field. router includes all path patterns sharing the same wildcard
|
// field. router includes all path patterns sharing the same wildcard
|
||||||
// host.
|
// host.
|
||||||
struct WildcardPattern {
|
struct WildcardPattern {
|
||||||
WildcardPattern(const StringRef &host)
|
WildcardPattern(const StringRef &host) : host(host) {}
|
||||||
: host(std::begin(host), std::end(host)) {}
|
|
||||||
|
|
||||||
ImmutableString host;
|
// This may not be NULL terminated. Currently it is only used for
|
||||||
|
// comparison.
|
||||||
|
StringRef host;
|
||||||
Router router;
|
Router router;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -676,7 +678,8 @@ struct RouterConfig {
|
||||||
|
|
||||||
struct DownstreamConfig {
|
struct DownstreamConfig {
|
||||||
DownstreamConfig()
|
DownstreamConfig()
|
||||||
: timeout{},
|
: balloc(1024, 1024),
|
||||||
|
timeout{},
|
||||||
addr_group_catch_all{0},
|
addr_group_catch_all{0},
|
||||||
connections_per_host{0},
|
connections_per_host{0},
|
||||||
connections_per_frontend{0},
|
connections_per_frontend{0},
|
||||||
|
@ -684,6 +687,12 @@ struct DownstreamConfig {
|
||||||
response_buffer_size{0},
|
response_buffer_size{0},
|
||||||
family{0} {}
|
family{0} {}
|
||||||
|
|
||||||
|
DownstreamConfig(const DownstreamConfig &) = delete;
|
||||||
|
DownstreamConfig(DownstreamConfig &&) = delete;
|
||||||
|
DownstreamConfig &operator=(const DownstreamConfig &) = delete;
|
||||||
|
DownstreamConfig &operator=(DownstreamConfig &&) = delete;
|
||||||
|
|
||||||
|
BlockAllocator balloc;
|
||||||
struct {
|
struct {
|
||||||
ev_tstamp read;
|
ev_tstamp read;
|
||||||
ev_tstamp write;
|
ev_tstamp write;
|
||||||
|
|
|
@ -182,7 +182,8 @@ void Worker::replace_downstream_config(
|
||||||
auto &dst = downstream_addr_groups_[i];
|
auto &dst = downstream_addr_groups_[i];
|
||||||
|
|
||||||
dst = std::make_shared<DownstreamAddrGroup>();
|
dst = std::make_shared<DownstreamAddrGroup>();
|
||||||
dst->pattern = src.pattern;
|
dst->pattern =
|
||||||
|
ImmutableString{std::begin(src.pattern), std::end(src.pattern)};
|
||||||
|
|
||||||
auto shared_addr = std::make_shared<SharedDownstreamAddr>();
|
auto shared_addr = std::make_shared<SharedDownstreamAddr>();
|
||||||
|
|
||||||
|
@ -198,16 +199,14 @@ void Worker::replace_downstream_config(
|
||||||
auto &dst_addr = shared_addr->addrs[j];
|
auto &dst_addr = shared_addr->addrs[j];
|
||||||
|
|
||||||
dst_addr.addr = src_addr.addr;
|
dst_addr.addr = src_addr.addr;
|
||||||
dst_addr.host =
|
dst_addr.host = make_string_ref(shared_addr->balloc, src_addr.host);
|
||||||
make_string_ref(shared_addr->balloc, StringRef{src_addr.host});
|
|
||||||
dst_addr.hostport =
|
dst_addr.hostport =
|
||||||
make_string_ref(shared_addr->balloc, StringRef{src_addr.hostport});
|
make_string_ref(shared_addr->balloc, src_addr.hostport);
|
||||||
dst_addr.port = src_addr.port;
|
dst_addr.port = src_addr.port;
|
||||||
dst_addr.host_unix = src_addr.host_unix;
|
dst_addr.host_unix = src_addr.host_unix;
|
||||||
dst_addr.proto = src_addr.proto;
|
dst_addr.proto = src_addr.proto;
|
||||||
dst_addr.tls = src_addr.tls;
|
dst_addr.tls = src_addr.tls;
|
||||||
dst_addr.sni =
|
dst_addr.sni = make_string_ref(shared_addr->balloc, src_addr.sni);
|
||||||
make_string_ref(shared_addr->balloc, StringRef{src_addr.sni});
|
|
||||||
dst_addr.fall = src_addr.fall;
|
dst_addr.fall = src_addr.fall;
|
||||||
dst_addr.rise = src_addr.rise;
|
dst_addr.rise = src_addr.rise;
|
||||||
|
|
||||||
|
|
54
src/util.cc
54
src/util.cc
|
@ -1172,6 +1172,32 @@ std::string make_http_hostport(const StringRef &host, uint16_t port) {
|
||||||
return hostport;
|
return hostport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host,
|
||||||
|
uint16_t port) {
|
||||||
|
if (port != 80 && port != 443) {
|
||||||
|
return make_hostport(balloc, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ipv6 = ipv6_numeric_addr(host.c_str());
|
||||||
|
|
||||||
|
auto iov = make_byte_ref(balloc, host.size() + (ipv6 ? 2 : 0) + 1);
|
||||||
|
auto p = iov.base;
|
||||||
|
|
||||||
|
if (ipv6) {
|
||||||
|
*p++ = '[';
|
||||||
|
}
|
||||||
|
|
||||||
|
p = std::copy(std::begin(host), std::end(host), p);
|
||||||
|
|
||||||
|
if (ipv6) {
|
||||||
|
*p++ = ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
return StringRef{iov.base, p};
|
||||||
|
}
|
||||||
|
|
||||||
std::string make_hostport(const StringRef &host, uint16_t port) {
|
std::string make_hostport(const StringRef &host, uint16_t port) {
|
||||||
auto ipv6 = ipv6_numeric_addr(host.c_str());
|
auto ipv6 = ipv6_numeric_addr(host.c_str());
|
||||||
auto serv = utos(port);
|
auto serv = utos(port);
|
||||||
|
@ -1197,6 +1223,34 @@ std::string make_hostport(const StringRef &host, uint16_t port) {
|
||||||
return hostport;
|
return hostport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringRef make_hostport(BlockAllocator &balloc, const StringRef &host,
|
||||||
|
uint16_t port) {
|
||||||
|
auto ipv6 = ipv6_numeric_addr(host.c_str());
|
||||||
|
auto serv = utos(port);
|
||||||
|
|
||||||
|
auto iov =
|
||||||
|
make_byte_ref(balloc, host.size() + (ipv6 ? 2 : 0) + 1 + serv.size());
|
||||||
|
auto p = iov.base;
|
||||||
|
|
||||||
|
if (ipv6) {
|
||||||
|
*p++ = '[';
|
||||||
|
}
|
||||||
|
|
||||||
|
p = std::copy(std::begin(host), std::end(host), p);
|
||||||
|
|
||||||
|
if (ipv6) {
|
||||||
|
*p++ = ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
*p++ = ':';
|
||||||
|
|
||||||
|
std::copy(std::begin(serv), std::end(serv), p);
|
||||||
|
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
return StringRef{iov.base, p};
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) {
|
void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) {
|
||||||
auto stop = std::min(first + 8, last);
|
auto stop = std::min(first + 8, last);
|
||||||
|
|
|
@ -635,10 +635,16 @@ std::string format_duration(double t);
|
||||||
// and "]". If |port| is 80 or 443, port part is omitted.
|
// and "]". If |port| is 80 or 443, port part is omitted.
|
||||||
std::string make_http_hostport(const StringRef &host, uint16_t port);
|
std::string make_http_hostport(const StringRef &host, uint16_t port);
|
||||||
|
|
||||||
|
StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host,
|
||||||
|
uint16_t port);
|
||||||
|
|
||||||
// Just like make_http_hostport(), but doesn't treat 80 and 443
|
// Just like make_http_hostport(), but doesn't treat 80 and 443
|
||||||
// specially.
|
// specially.
|
||||||
std::string make_hostport(const StringRef &host, uint16_t port);
|
std::string make_hostport(const StringRef &host, uint16_t port);
|
||||||
|
|
||||||
|
StringRef make_hostport(BlockAllocator &balloc, const StringRef &host,
|
||||||
|
uint16_t port);
|
||||||
|
|
||||||
// Dumps |src| of length |len| in the format similar to `hexdump -C`.
|
// Dumps |src| of length |len| in the format similar to `hexdump -C`.
|
||||||
void hexdump(FILE *out, const uint8_t *src, size_t len);
|
void hexdump(FILE *out, const uint8_t *src, size_t len);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue