mirror of
https://github.com/ElementsProject/lightning.git
synced 2026-08-20 13:27:36 +02:00
CCAN: update to post-kimi audit.
Lots of changes, so I've summarized the individual upstream commits.
May alter (fix!) CLN behavior:
* 6b4e62ec tal/path: path_simplify() ".." handling
* 4ceeef2b io: don't busy-spin with exclusive conn + pending always plan
* 64822afc io: listener fds O_NONBLOCK
* b02c1147, 2f04573d, 42486358 pipecmd: _exit, pre-fork fd leak, EINTR
* f7062e12, 6da9c78d fdpass: reject-path fd leak, errno sign
* 339b359a breakpoint: blocked-SIGTRAP + fork staleness (--dev-debugger only)
* 018a4977 take: keep labelarr in sync in taken() (debug builds would give wrong taken() error)
* 28f7c3fe check_type: hard-fail (would cause build-time failure)
The rest are not patterns used by CLN, or only happen on allocation failure (which always causes immediate exit):
* 4122d490 tal/str: count regex capture groups via re_nsub, not text scan. No such complex regexes used.
* 5b6fca2c tal: allow tal_steal() to rescue an object being destroyed; we don't do this.
* tal: 9da96c88 (CCAN_LIST_DEBUG only), ca38d33a (notifier edge), 328d96ff (needs type-mismatched tal_expand), 39d1a0a0 (harmless thinko), df66b082
(path_readlink unused), 80416994 (UB never dereferenced), 0e926004 (doc), 3217faca (zero st_size; no /proc reads in CLN), 5d86e1fe (doc)
* c0c9797f str: strcount empty needle - both CLN call sites pass constant non-empty needles
* htable: af8ff95e (init_sized unused), d3da4521 (32-bit), ba8c4ad2 (needs billions of entries)
* opt: 6c3faecc (bare '-' argv), dae5f5f4 (attribute/doc; CLN passes real args)
* timer: f7b21467 (36k years at default granularity), 1127eb30 (needs failing custom allocator - CLN aborts on OOM)
* mem: 351072ac (memcchr unused), dde9474c (fallback memrchr - glibc/musl provide it), 228161d9 (test), f5b48d0f (doc)
* json_out: f14769e3, e9893f9e, d0401012, c91ae47b - all OOM/encoding-error paths; CLN aborts on OOM
* membuf: 4ee105f0 (UB, correct results anyway), 4b2d55b8 (2^62-element buffers)
* rune: cc63dceb (rune_dup unused), 51257cd9 (CLN fieldnames are alnum), fb4dbfa6 (needs embedded-NUL field values)
* strset/strmap iterator rework: b5dcf30e, 19b73d88, 51c4da92, c127ff33 - additive API + recursion fix; CLN's maps are far too small to hit deep
trees
* crypto: 1c377ffb (32-bit only), 344ba97c (needs 8160-byte HKDF requests), b4316b44 (default SHACHAIN_BITS=64 immune)
* f2b445b3 str/base32: CLN's one call site passes an exact-size buffer
* intmap 1a1bb440 (sintmap unused), bitmap ab052a14 (2^61 bits), likely 8cfa5f8c (likely_stats unused), take e30e011d (only take_cleanup() on exit), rbuf 748a0692 (rbuf_fill unused), ptr_valid x4 (unused), cdump x3 (cdump_extract unused)
* asort: 0d54244f, a13c9b97, d878b036 - fallback/build-hygiene only; CLN on glibc takes the qsort_r path and already compiled
* Doc-only: 2b988863, 3e034139, 8fee253f, e3155f93, 6e830d68, 172ae94c, cbbd603e, 6f0b1b03
* Test-only: 46acc9b6, 9df9809e, 4c23c54c
* 5b2773b2 build_assert: _Static_assert - compile-time only; all 138 CLN uses are constants (same 138-use build check applies as check_type)
* 01fbf784 container_of: NULL+0 UB fix - behavior identical
* 0421034f cppmagic sentinel, 43cfe853 ilog ilog64 - portability/performance only
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
5a56a976b4
commit
6fbfdf1add
68 changed files with 1128 additions and 221 deletions
|
|
@ -1,3 +1,3 @@
|
|||
CCAN imported from https://github.com/rustyrussell/ccan.
|
||||
|
||||
CCAN version: fe99a8e0
|
||||
CCAN version: 08af1cab
|
||||
|
|
|
|||
|
|
@ -34,11 +34,15 @@
|
|||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Vendored glibc code uses GNU void * arithmetic. */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-arith"
|
||||
|
||||
/* glibc-internal type, mapped to ccan's equivalent. */
|
||||
typedef _total_order_cb __compar_d_fn_t;
|
||||
|
||||
/* glibc-internal helpers, not available outside glibc. */
|
||||
static inline void *__mempcpy(void *dst, const void *src, size_t n)
|
||||
static inline void *asort_mempcpy(void *dst, const void *src, size_t n)
|
||||
{
|
||||
return (char *) memcpy (dst, src, n) + n;
|
||||
}
|
||||
|
|
@ -54,8 +58,8 @@ __memswap (void *__restrict p1, void *__restrict p2, size_t n)
|
|||
while (n > SWAP_GENERIC_SIZE)
|
||||
{
|
||||
memcpy (tmp, p1, SWAP_GENERIC_SIZE);
|
||||
p1 = __mempcpy (p1, p2, SWAP_GENERIC_SIZE);
|
||||
p2 = __mempcpy (p2, tmp, SWAP_GENERIC_SIZE);
|
||||
p1 = asort_mempcpy (p1, p2, SWAP_GENERIC_SIZE);
|
||||
p2 = asort_mempcpy (p2, tmp, SWAP_GENERIC_SIZE);
|
||||
n -= SWAP_GENERIC_SIZE;
|
||||
}
|
||||
while (n > 0)
|
||||
|
|
@ -316,13 +320,13 @@ msort_with_tmp (const struct msort_param *p, void *b, size_t n)
|
|||
{
|
||||
if (cmp (b1, b2, arg) <= 0)
|
||||
{
|
||||
tmp = (char *) __mempcpy (tmp, b1, s);
|
||||
tmp = (char *) asort_mempcpy (tmp, b1, s);
|
||||
b1 += s;
|
||||
--n1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = (char *) __mempcpy (tmp, b2, s);
|
||||
tmp = (char *) asort_mempcpy (tmp, b2, s);
|
||||
b2 += s;
|
||||
--n2;
|
||||
}
|
||||
|
|
@ -452,4 +456,6 @@ _asort (void *const pbase, size_t total_elems, size_t size,
|
|||
}
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif /* !HAVE_QSORT_R_PRIVATE_LAST */
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ _asort((base), (num), sizeof(*(base)), \
|
|||
total_order_cast((cmp), *(base), (ctx)), (ctx))
|
||||
|
||||
#if HAVE_QSORT_R_PRIVATE_LAST
|
||||
/* qsort_r is only declared under _GNU_SOURCE, which must precede the
|
||||
* first libc include — we can't control our includers, so declare it
|
||||
* ourselves (the configurator only sets this where this GNU signature
|
||||
* was detected). */
|
||||
void qsort_r(void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *, void *), void *arg);
|
||||
#define _asort(b, n, s, cmp, ctx) qsort_r(b, n, s, cmp, ctx)
|
||||
#else
|
||||
void _asort(void *base, size_t nmemb, size_t size,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ typedef unsigned long bitmap_word;
|
|||
|
||||
#define BITMAP_WORD_BITS (sizeof(bitmap_word) * CHAR_BIT)
|
||||
#define BITMAP_NWORDS(_n) \
|
||||
(((_n) + BITMAP_WORD_BITS - 1) / BITMAP_WORD_BITS)
|
||||
(((_n) / BITMAP_WORD_BITS) + (((_n) % BITMAP_WORD_BITS) != 0))
|
||||
|
||||
#define BITMAP_WORD_0 (0)
|
||||
#define BITMAP_WORD_1 ((bitmap_word)-1UL)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ static inline int bitops_ffs32(uint32_t u)
|
|||
/**
|
||||
* bitops_ffs64: find lowest set bit in a uint64_t
|
||||
*
|
||||
* Returns 1 for least significant bit, 32 for most significant bit, 0
|
||||
* Returns 1 for least significant bit, 64 for most significant bit, 0
|
||||
* for no bits set.
|
||||
*/
|
||||
static inline int bitops_ffs64(uint64_t u)
|
||||
|
|
|
|||
|
|
@ -7,26 +7,39 @@
|
|||
|
||||
bool breakpoint_initialized;
|
||||
bool breakpoint_under_debug;
|
||||
pid_t breakpoint_pid;
|
||||
|
||||
static volatile sig_atomic_t trapped;
|
||||
|
||||
/* This doesn't get called if we're under GDB. */
|
||||
static void trap(int signum)
|
||||
{
|
||||
breakpoint_initialized = true;
|
||||
trapped = true;
|
||||
}
|
||||
|
||||
void breakpoint_init(void)
|
||||
{
|
||||
struct sigaction old, new;
|
||||
sigset_t mask, oldmask;
|
||||
|
||||
new.sa_handler = trap;
|
||||
new.sa_flags = 0;
|
||||
sigemptyset(&new.sa_mask);
|
||||
sigaction(SIGTRAP, &new, &old);
|
||||
|
||||
/* If SIGTRAP is blocked, the probe would pend (and kill us when
|
||||
* the caller restores its mask), not run the handler. */
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGTRAP);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, &oldmask);
|
||||
|
||||
trapped = false;
|
||||
kill(getpid(), SIGTRAP);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
sigaction(SIGTRAP, &old, NULL);
|
||||
|
||||
if (!breakpoint_initialized) {
|
||||
breakpoint_initialized = true;
|
||||
breakpoint_under_debug = true;
|
||||
}
|
||||
breakpoint_pid = getpid();
|
||||
breakpoint_initialized = true;
|
||||
breakpoint_under_debug = !trapped;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,13 +10,20 @@
|
|||
void breakpoint_init(void) COLD;
|
||||
extern bool breakpoint_initialized;
|
||||
extern bool breakpoint_under_debug;
|
||||
extern pid_t breakpoint_pid;
|
||||
|
||||
/**
|
||||
* breakpoint - stop if running under the debugger.
|
||||
*
|
||||
* The first call detects the debugger via a SIGTRAP probe. This is
|
||||
* not thread-safe: either call breakpoint_init() explicitly at
|
||||
* program start (before creating threads), or don't let first use
|
||||
* race.
|
||||
*/
|
||||
static inline void breakpoint(void)
|
||||
{
|
||||
if (!breakpoint_initialized)
|
||||
/* Detection state doesn't carry across fork(). */
|
||||
if (!breakpoint_initialized || breakpoint_pid != getpid())
|
||||
breakpoint_init();
|
||||
if (breakpoint_under_debug)
|
||||
kill(getpid(), SIGTRAP);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
/* CC0 (Public domain) - see LICENSE file for details */
|
||||
#ifndef CCAN_BUILD_ASSERT_H
|
||||
#define CCAN_BUILD_ASSERT_H
|
||||
#include "config.h"
|
||||
|
||||
/**
|
||||
* BUILD_ASSERT - assert a build-time dependency.
|
||||
* @cond: the compile-time condition which must be true.
|
||||
*
|
||||
* Your compile will fail if the condition isn't true, or can't be evaluated
|
||||
* by the compiler. This can only be used within a function.
|
||||
* Your compile will fail if the condition isn't true. When the
|
||||
* compiler supports C11 _Static_assert it will also fail if the
|
||||
* condition can't be evaluated by the compiler; otherwise (older
|
||||
* compilers) a non-constant condition is silently accepted (and is
|
||||
* undefined behavior if false at runtime).
|
||||
*
|
||||
* This can only be used within a function.
|
||||
*
|
||||
* Example:
|
||||
* #include <stddef.h>
|
||||
|
|
@ -19,22 +25,38 @@
|
|||
* return (char *)foo;
|
||||
* }
|
||||
*/
|
||||
#if HAVE_STATIC_ASSERT
|
||||
/* _Static_assert is a declaration, so do-while wrap avoids breaking if (x) BUILD_ASSERT... */
|
||||
#define BUILD_ASSERT(cond) \
|
||||
do { _Static_assert(cond, "BUILD_ASSERT"); } while(0)
|
||||
#else
|
||||
#define BUILD_ASSERT(cond) \
|
||||
do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.
|
||||
* @cond: the compile-time condition which must be true.
|
||||
*
|
||||
* Your compile will fail if the condition isn't true, or can't be evaluated
|
||||
* by the compiler. This can be used in an expression: its value is "0".
|
||||
* Your compile will fail if the condition isn't true. When the
|
||||
* compiler supports C11 _Static_assert it will also fail if the
|
||||
* condition can't be evaluated by the compiler; otherwise (older
|
||||
* compilers) a non-constant condition is silently accepted (and is
|
||||
* undefined behavior if false at runtime).
|
||||
*
|
||||
* This can be used in an expression: its value is "0".
|
||||
*
|
||||
* Example:
|
||||
* #define foo_to_char(foo) \
|
||||
* ((char *)(foo) \
|
||||
* + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
|
||||
*/
|
||||
#if HAVE_STATIC_ASSERT
|
||||
#define BUILD_ASSERT_OR_ZERO(cond) \
|
||||
(sizeof(struct { _Static_assert(cond, "BUILD_ASSERT_OR_ZERO"); char c; }) - 1)
|
||||
#else
|
||||
#define BUILD_ASSERT_OR_ZERO(cond) \
|
||||
(sizeof(char [1 - 2*!(cond)]) - 1)
|
||||
#endif
|
||||
|
||||
#endif /* CCAN_BUILD_ASSERT_H */
|
||||
|
|
|
|||
|
|
@ -48,9 +48,10 @@ static struct token *tokenize(const void *ctx, const char *code)
|
|||
} else if (code[i] == '/' && code[i+1] == '*') {
|
||||
/* Multi-line comment. */
|
||||
const char *end = strstr(code+i+2, "*/");
|
||||
len = (end + 2) - (code + i);
|
||||
if (!end)
|
||||
len = strlen(code + i);
|
||||
else
|
||||
len = (end + 2) - (code + i);
|
||||
if (tok_start != -1U) {
|
||||
add_token(&toks, code+tok_start, i - tok_start);
|
||||
tok_start = -1U;
|
||||
|
|
@ -82,6 +83,10 @@ static struct token *tokenize(const void *ctx, const char *code)
|
|||
start_of_line = false;
|
||||
}
|
||||
|
||||
/* A trailing identifier running to EOF is still a token. */
|
||||
if (tok_start != -1U)
|
||||
add_token(&toks, code+tok_start, i - tok_start);
|
||||
|
||||
/* Add terminating NULL. */
|
||||
tal_resizez(&toks, tal_count(toks) + 1);
|
||||
return toks;
|
||||
|
|
@ -92,6 +97,7 @@ struct parse_state {
|
|||
const struct token *toks;
|
||||
struct cdump_definitions *defs;
|
||||
char *complaints;
|
||||
unsigned int depth;
|
||||
};
|
||||
|
||||
static const struct token *tok_peek(const struct token **toks)
|
||||
|
|
@ -277,17 +283,28 @@ static void tok_take_unknown_statement(struct parse_state *ps)
|
|||
|
||||
static bool tok_take_expr(struct parse_state *ps, const char *term)
|
||||
{
|
||||
/* Recursion is one frame per nested ( or [: bound it. */
|
||||
if (ps->depth++ == 100) {
|
||||
complain(ps, "Expression nested too deeply");
|
||||
goto fail;
|
||||
}
|
||||
while (!tok_is(&ps->toks, term)) {
|
||||
if (tok_take_if(&ps->toks, "(")) {
|
||||
if (!tok_take_expr(ps, ")"))
|
||||
return false;
|
||||
goto fail;
|
||||
} else if (tok_take_if(&ps->toks, "[")) {
|
||||
if (!tok_take_expr(ps, "]"))
|
||||
return false;
|
||||
goto fail;
|
||||
} else if (!tok_take(&ps->toks))
|
||||
return false;
|
||||
goto fail;
|
||||
}
|
||||
return tok_take(&ps->toks);
|
||||
if (!tok_take(&ps->toks))
|
||||
goto fail;
|
||||
ps->depth--;
|
||||
return true;
|
||||
fail:
|
||||
ps->depth--;
|
||||
return false;
|
||||
}
|
||||
|
||||
static char *tok_take_expr_str(const tal_t *ctx,
|
||||
|
|
@ -347,7 +364,12 @@ static bool tok_take_type(struct parse_state *ps, struct cdump_type **type)
|
|||
|
||||
/* Did we get some? */
|
||||
if (ps->toks != types) {
|
||||
name = string_of_toks(NULL, types, tok_peek(&ps->toks));
|
||||
const struct token *until = tok_peek(&ps->toks);
|
||||
if (!until) {
|
||||
complain(ps, "EOF after type");
|
||||
return false;
|
||||
}
|
||||
name = string_of_toks(NULL, types, until);
|
||||
kind = CDUMP_UNKNOWN;
|
||||
} else {
|
||||
/* Try normal types (or simple typedefs, etc). */
|
||||
|
|
@ -654,6 +676,7 @@ struct cdump_definitions *cdump_extract(const tal_t *ctx, const char *code,
|
|||
ps.defs = tal(ctx, struct cdump_definitions);
|
||||
ps.complaints = tal_strdup(ctx, "");
|
||||
ps.code = code;
|
||||
ps.depth = 0;
|
||||
|
||||
strmap_init(&ps.defs->enums);
|
||||
strmap_init(&ps.defs->structs);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,15 @@
|
|||
* ((encl_type *) \
|
||||
* ((char *)(mbr_ptr) - offsetof(encl_type, mbr))))
|
||||
*/
|
||||
#if HAVE_TYPEOF
|
||||
#if HAVE_TYPEOF && HAVE_BUILTIN_TYPES_COMPATIBLE_P
|
||||
#include <ccan/build_assert/build_assert.h>
|
||||
#define check_type(expr, type) \
|
||||
BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(typeof(expr), type))
|
||||
|
||||
#define check_types_match(expr1, expr2) \
|
||||
BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(typeof(expr1), \
|
||||
typeof(expr2)))
|
||||
#elif HAVE_TYPEOF
|
||||
#define check_type(expr, type) \
|
||||
((typeof(expr) *)0 != (type *)0)
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
#else
|
||||
#define CONST_FUNCTION
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PURE_FUNCTION
|
||||
#if HAVE_ATTRIBUTE_PURE
|
||||
|
|
@ -89,7 +90,6 @@
|
|||
#define PURE_FUNCTION
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAVE_ATTRIBUTE_UNUSED
|
||||
#ifndef UNNEEDED
|
||||
|
|
@ -199,7 +199,7 @@
|
|||
* // Use inline if compiler knows answer. Otherwise call function
|
||||
* // to avoid copies of the same code everywhere.
|
||||
* #define greek_name(g) \
|
||||
* (IS_COMPILE_CONSTANT(greek) ? _greek_name(g) : greek_name(g))
|
||||
* (IS_COMPILE_CONSTANT(g) ? _greek_name(g) : greek_name(g))
|
||||
*/
|
||||
#define IS_COMPILE_CONSTANT(expr) __builtin_constant_p(expr)
|
||||
#else
|
||||
|
|
@ -230,6 +230,7 @@
|
|||
#endif
|
||||
|
||||
|
||||
#ifndef WARN_DEPRECATED
|
||||
#if HAVE_ATTRIBUTE_DEPRECATED
|
||||
/**
|
||||
* WARN_DEPRECATED - warn that a function/type/variable is deprecated when used.
|
||||
|
|
@ -243,8 +244,9 @@
|
|||
#else
|
||||
#define WARN_DEPRECATED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef NO_NULL_ARGS
|
||||
#if HAVE_ATTRIBUTE_NONNULL
|
||||
/**
|
||||
* NO_NULL_ARGS - specify that no arguments to this function can be NULL.
|
||||
|
|
@ -255,7 +257,13 @@
|
|||
* NO_NULL_ARGS char *my_copy(char *buf);
|
||||
*/
|
||||
#define NO_NULL_ARGS __attribute__((__nonnull__))
|
||||
#else
|
||||
#define NO_NULL_ARGS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NON_NULL_ARGS
|
||||
#if HAVE_ATTRIBUTE_NONNULL
|
||||
/**
|
||||
* NON_NULL_ARGS - specify that some arguments to this function can't be NULL.
|
||||
* @...: 1-based argument numbers for which args can't be NULL.
|
||||
|
|
@ -267,10 +275,11 @@
|
|||
*/
|
||||
#define NON_NULL_ARGS(...) __attribute__((__nonnull__(__VA_ARGS__)))
|
||||
#else
|
||||
#define NO_NULL_ARGS
|
||||
#define NON_NULL_ARGS(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef RETURNS_NONNULL
|
||||
#if HAVE_ATTRIBUTE_RETURNS_NONNULL
|
||||
/**
|
||||
* RETURNS_NONNULL - specify that this function cannot return NULL.
|
||||
|
|
@ -284,7 +293,9 @@
|
|||
#else
|
||||
#define RETURNS_NONNULL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LAST_ARG_NULL
|
||||
#if HAVE_ATTRIBUTE_SENTINEL
|
||||
/**
|
||||
* LAST_ARG_NULL - specify the last argument of a variadic function must be NULL.
|
||||
|
|
@ -298,7 +309,9 @@
|
|||
#else
|
||||
#define LAST_ARG_NULL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef cpu_supports
|
||||
#if HAVE_BUILTIN_CPU_SUPPORTS
|
||||
/**
|
||||
* cpu_supports - test if current CPU supports the named feature.
|
||||
|
|
@ -313,5 +326,6 @@
|
|||
#else
|
||||
#define cpu_supports(x) 0
|
||||
#endif /* HAVE_BUILTIN_CPU_SUPPORTS */
|
||||
#endif
|
||||
|
||||
#endif /* CCAN_COMPILER_H */
|
||||
|
|
|
|||
|
|
@ -68,9 +68,10 @@ static inline char *container_of_or_null_(void *member_ptr, size_t offset)
|
|||
}
|
||||
#define container_of_or_null(member_ptr, containing_type, member) \
|
||||
((containing_type *) \
|
||||
container_of_or_null_(member_ptr, \
|
||||
container_off(containing_type, member)) \
|
||||
+ check_types_match(*(member_ptr), ((containing_type *)0)->member))
|
||||
((void)check_types_match(*(member_ptr), \
|
||||
((containing_type *)0)->member), \
|
||||
container_of_or_null_(member_ptr, \
|
||||
container_off(containing_type, member))))
|
||||
|
||||
/**
|
||||
* container_off - get offset to enclosing structure
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
* expands to '1' if @a is '0', otherwise expands to '0'.
|
||||
*/
|
||||
#define _CPPMAGIC_ISPROBE(...) CPPMAGIC_2ND(__VA_ARGS__, 0)
|
||||
#define _CPPMAGIC_PROBE() $, 1
|
||||
#define _CPPMAGIC_PROBE() _cppmagic_probe, 1
|
||||
#define _CPPMAGIC_ISZERO_0 _CPPMAGIC_PROBE()
|
||||
#define CPPMAGIC_ISZERO(a_) \
|
||||
_CPPMAGIC_ISPROBE(CPPMAGIC_GLUE2(_CPPMAGIC_ISZERO_, a_))
|
||||
|
|
@ -139,6 +139,11 @@
|
|||
*
|
||||
* CPPMAGIC_MAP(@m, @a1, @a2, ... @an)
|
||||
* expands to the expansion of @m(@a1) , @m(@a2) , ... , @m(@an)
|
||||
*
|
||||
* Note: an argument which expands to no tokens is not supported: it
|
||||
* silently truncates the argument list at that point (before C23
|
||||
* __VA_OPT__ there is no way to distinguish "expands to nothing"
|
||||
* from "absent").
|
||||
*/
|
||||
#define _CPPMAGIC_MAP_() _CPPMAGIC_MAP
|
||||
#define _CPPMAGIC_MAP(m_, a_, ...) \
|
||||
|
|
@ -158,6 +163,9 @@
|
|||
* CPPMAGIC_2MAP(@m, @a1, @b1, @a2, @b2, ..., @an, @bn)
|
||||
* expands to the expansion of
|
||||
* @m(@a1, @b1) , @m(@a2, @b2) , ... , @m(@an, @bn)
|
||||
*
|
||||
* Note: an argument which expands to no tokens is not supported
|
||||
* (see CPPMAGIC_MAP).
|
||||
*/
|
||||
#define _CPPMAGIC_2MAP_() _CPPMAGIC_2MAP
|
||||
#define _CPPMAGIC_2MAP(m_, a_, b_, ...) \
|
||||
|
|
@ -176,6 +184,9 @@
|
|||
*
|
||||
* CPPMAGIC_JOIN(@d, @a1, @a2, ..., @an)
|
||||
* expands to the expansion of @a1 @d @a2 @d ... @d @an
|
||||
*
|
||||
* Note: an argument which expands to no tokens is not supported
|
||||
* (see CPPMAGIC_MAP).
|
||||
*/
|
||||
#define _CPPMAGIC_JOIN_() _CPPMAGIC_JOIN
|
||||
#define _CPPMAGIC_JOIN(d_, a_, ...) \
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ void hkdf_sha256(void *okm, size_t okm_size,
|
|||
struct hmac_sha256_ctx ctx;
|
||||
unsigned char c;
|
||||
|
||||
assert(okm_size < 255 * sizeof(t));
|
||||
assert(okm_size <= 255 * sizeof(t));
|
||||
|
||||
if (okm_size == 0)
|
||||
return;
|
||||
|
||||
/* RFC 5869:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
/**
|
||||
* hkdf_sha256 - generate a derived key
|
||||
* @okm: where to output the key
|
||||
* @okm_size: the number of bytes pointed to by @okm (must be less than 255*32)
|
||||
* @okm_size: the number of bytes pointed to by @okm (must be at most 255*32)
|
||||
* @s: salt
|
||||
* @ssize: the number of bytes pointed to by @s
|
||||
* @k: pointer to input key
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ struct sha256_ctx {
|
|||
uint32_t u32[16];
|
||||
unsigned char u8[64];
|
||||
} buf;
|
||||
size_t bytes;
|
||||
/* uint64_t: hashing 4GB+ must not wrap the length on 32-bit. */
|
||||
uint64_t bytes;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -86,8 +86,17 @@ bool shachain_add_hash(struct shachain *chain,
|
|||
/* You have to insert them in order! */
|
||||
assert(index == shachain_next_index(chain));
|
||||
|
||||
/* Reject out-of-domain indices (SHACHAIN_BITS < 64 builds, and
|
||||
* the post-exhaustion wrap to UINT64_MAX). */
|
||||
if (index > (UINT64_MAX >> (64 - SHACHAIN_BITS)))
|
||||
return false;
|
||||
|
||||
pos = count_trailing_zeroes(index);
|
||||
|
||||
/* Beyond the chain domain (past exhaustion): no such slot. */
|
||||
if (pos > SHACHAIN_BITS)
|
||||
return false;
|
||||
|
||||
/* All derivable answers must be valid. */
|
||||
/* FIXME: Is it sufficient to check just the next answer? */
|
||||
for (i = 0; i < pos; i++) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ int main(int argc, char *argv[])
|
|||
/* Test err() in child */
|
||||
if (pipe(pfd))
|
||||
abort();
|
||||
fflush(stdout);
|
||||
if (fork()) {
|
||||
char buffer[BUFFER_MAX+1];
|
||||
unsigned int i;
|
||||
|
|
@ -63,7 +62,6 @@ int main(int argc, char *argv[])
|
|||
/* Test errx() in child */
|
||||
if (pipe(pfd))
|
||||
abort();
|
||||
fflush(stdout);
|
||||
if (fork()) {
|
||||
char buffer[BUFFER_MAX+1];
|
||||
unsigned int i;
|
||||
|
|
@ -94,7 +92,6 @@ int main(int argc, char *argv[])
|
|||
/* Test warn() in child */
|
||||
if (pipe(pfd))
|
||||
abort();
|
||||
fflush(stdout);
|
||||
if (fork()) {
|
||||
char buffer[BUFFER_MAX+1];
|
||||
unsigned int i;
|
||||
|
|
@ -127,7 +124,6 @@ int main(int argc, char *argv[])
|
|||
/* Test warnx() in child */
|
||||
if (pipe(pfd))
|
||||
abort();
|
||||
fflush(stdout);
|
||||
if (fork()) {
|
||||
char buffer[BUFFER_MAX+1];
|
||||
unsigned int i;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool fdpass_send(int sockout, int fd)
|
||||
{
|
||||
|
|
@ -71,11 +72,24 @@ int fdpass_recv(int sockin)
|
|||
return -1;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (!cmsg
|
||||
|| cmsg->cmsg_len != CMSG_LEN(sizeof(fd))
|
||||
if (!cmsg
|
||||
|| cmsg->cmsg_level != SOL_SOCKET
|
||||
|| cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
errno = -EINVAL;
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) {
|
||||
/* The kernel already installed any fds the message
|
||||
* carried; don't leak them. */
|
||||
if (cmsg->cmsg_len >= CMSG_LEN(0)
|
||||
&& cmsg->cmsg_len <= msg.msg_controllen) {
|
||||
int *fds = (int *)CMSG_DATA(cmsg);
|
||||
size_t i, nfds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
|
||||
for (i = 0; i < nfds; i++)
|
||||
close(fds[i]);
|
||||
}
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,12 @@ void htable_init(struct htable *ht,
|
|||
ht->table = &ht->common_bits;
|
||||
}
|
||||
|
||||
/* Number of buckets in the table. */
|
||||
static inline size_t ht_size(const struct htable *ht)
|
||||
{
|
||||
return (size_t)1 << ht->bits;
|
||||
}
|
||||
|
||||
/* Fill to 87.5% */
|
||||
static inline size_t ht_max(const struct htable *ht)
|
||||
{
|
||||
|
|
@ -95,7 +101,7 @@ static inline size_t ht_max(const struct htable *ht)
|
|||
/* Clean deleted if we're full, and more than 12.5% deleted */
|
||||
static inline size_t ht_max_deleted(const struct htable *ht)
|
||||
{
|
||||
return ((size_t)1 << ht->bits) / 8;
|
||||
return ht_size(ht) / 8;
|
||||
}
|
||||
|
||||
bool htable_init_sized(struct htable *ht,
|
||||
|
|
@ -108,11 +114,15 @@ bool htable_init_sized(struct htable *ht,
|
|||
for (ht->bits = 1; ht_max(ht) < expect; ht->bits++) {
|
||||
if (ht->bits == 30)
|
||||
break;
|
||||
/* Stop before the allocation size wraps (eg. 32-bit). */
|
||||
if ((sizeof(size_t) << (ht->bits + 1)) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
ht->table = htable_alloc(ht, sizeof(size_t) << ht->bits);
|
||||
if (!ht->table) {
|
||||
ht->table = &ht->common_bits;
|
||||
ht->bits = 0;
|
||||
return false;
|
||||
}
|
||||
(void)htable_debug(ht, HTABLE_LOC);
|
||||
|
|
@ -153,7 +163,7 @@ void htable_unlock(struct htable *ht)
|
|||
|
||||
static size_t hash_bucket(const struct htable *ht, size_t h)
|
||||
{
|
||||
return h & ((1 << ht->bits)-1);
|
||||
return h & (ht_size(ht)-1);
|
||||
}
|
||||
|
||||
static void *htable_val(const struct htable *ht,
|
||||
|
|
@ -166,7 +176,7 @@ static void *htable_val(const struct htable *ht,
|
|||
if (get_extra_ptr_bits(ht, ht->table[i->off]) == h2)
|
||||
return get_raw_ptr(ht, ht->table[i->off]);
|
||||
}
|
||||
i->off = (i->off + 1) & ((1 << ht->bits)-1);
|
||||
i->off = (i->off + 1) & (ht_size(ht)-1);
|
||||
h2 &= ~perfect;
|
||||
}
|
||||
return NULL;
|
||||
|
|
@ -182,13 +192,13 @@ void *htable_firstval_(const struct htable *ht,
|
|||
void *htable_nextval_(const struct htable *ht,
|
||||
struct htable_iter *i, size_t hash)
|
||||
{
|
||||
i->off = (i->off + 1) & ((1 << ht->bits)-1);
|
||||
i->off = (i->off + 1) & (ht_size(ht)-1);
|
||||
return htable_val(ht, i, hash, 0);
|
||||
}
|
||||
|
||||
void *htable_first_(const struct htable *ht, struct htable_iter *i)
|
||||
{
|
||||
for (i->off = 0; i->off < (size_t)1 << ht->bits; i->off++) {
|
||||
for (i->off = 0; i->off < ht_size(ht); i->off++) {
|
||||
if (entry_is_valid(ht->table[i->off]))
|
||||
return get_raw_ptr(ht, ht->table[i->off]);
|
||||
}
|
||||
|
|
@ -197,7 +207,7 @@ void *htable_first_(const struct htable *ht, struct htable_iter *i)
|
|||
|
||||
void *htable_next_(const struct htable *ht, struct htable_iter *i)
|
||||
{
|
||||
for (i->off++; i->off < (size_t)1 << ht->bits; i->off++) {
|
||||
for (i->off++; i->off < ht_size(ht); i->off++) {
|
||||
if (entry_is_valid(ht->table[i->off]))
|
||||
return get_raw_ptr(ht, ht->table[i->off]);
|
||||
}
|
||||
|
|
@ -243,7 +253,7 @@ static COLD void fixup_table_common(struct htable *ht, uintptr_t maskdiff)
|
|||
again:
|
||||
bitsdiff = ht->common_bits & maskdiff;
|
||||
|
||||
for (i = 0; i < (size_t)1 << ht->bits; i++) {
|
||||
for (i = 0; i < ht_size(ht); i++) {
|
||||
uintptr_t e;
|
||||
if (!entry_is_valid(e = ht->table[i]))
|
||||
continue;
|
||||
|
|
@ -296,7 +306,7 @@ static void ht_add(struct htable *ht, const void *new, size_t h)
|
|||
|
||||
while (entry_is_valid(ht->table[i])) {
|
||||
perfect = 0;
|
||||
i = (i + 1) & ((1 << ht->bits)-1);
|
||||
i = (i + 1) & (ht_size(ht)-1);
|
||||
}
|
||||
ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect);
|
||||
if (!entry_is_valid(ht->table[i]))
|
||||
|
|
@ -306,11 +316,16 @@ static void ht_add(struct htable *ht, const void *new, size_t h)
|
|||
static COLD bool double_table(struct htable *ht)
|
||||
{
|
||||
unsigned int i;
|
||||
size_t oldnum = (size_t)1 << ht->bits;
|
||||
size_t oldnum = ht_size(ht);
|
||||
size_t newsize = sizeof(size_t) << (ht->bits+1);
|
||||
uintptr_t *oldtable, e;
|
||||
|
||||
/* 32-bit: doubling can wrap the allocation size to 0. */
|
||||
if (newsize == 0)
|
||||
return false;
|
||||
|
||||
oldtable = ht->table;
|
||||
ht->table = htable_alloc(ht, sizeof(size_t) << (ht->bits+1));
|
||||
ht->table = htable_alloc(ht, newsize);
|
||||
if (!ht->table) {
|
||||
ht->table = oldtable;
|
||||
return false;
|
||||
|
|
@ -350,8 +365,8 @@ static COLD void rehash_table(struct htable *ht)
|
|||
/* Beware wrap cases: we need to start from first empty bucket. */
|
||||
for (start = 0; ht->table[start]; start++);
|
||||
|
||||
for (i = 0; i < (size_t)1 << ht->bits; i++) {
|
||||
size_t h = (i + start) & ((1 << ht->bits)-1);
|
||||
for (i = 0; i < ht_size(ht); i++) {
|
||||
size_t h = (i + start) & (ht_size(ht)-1);
|
||||
e = ht->table[h];
|
||||
if (!e)
|
||||
continue;
|
||||
|
|
@ -427,7 +442,7 @@ bool htable_del_(struct htable *ht, size_t h, const void *p)
|
|||
|
||||
void htable_delval_(struct htable *ht, struct htable_iter *i)
|
||||
{
|
||||
assert(i->off < (size_t)1 << ht->bits);
|
||||
assert(i->off < ht_size(ht));
|
||||
assert(entry_is_valid(ht->table[i->off]));
|
||||
|
||||
ht->elems--;
|
||||
|
|
@ -446,7 +461,7 @@ void *htable_pick_(const struct htable *ht, size_t seed, struct htable_iter *i)
|
|||
|
||||
if (!i)
|
||||
i = &unwanted;
|
||||
i->off = seed % ((size_t)1 << ht->bits);
|
||||
i->off = seed % ht_size(ht);
|
||||
e = htable_next(ht, i);
|
||||
if (!e)
|
||||
e = htable_first(ht, i);
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ int ilog64_nz(uint64_t _v) CONST_FUNCTION;
|
|||
#endif /* builtin_ilog32_nz */
|
||||
|
||||
#ifdef builtin_ilog64_nz
|
||||
#define ilog32(_v) ((_v) ? builtin_ilog32_nz(_v) : 0)
|
||||
#define ilog64(_v) ((_v) ? builtin_ilog64_nz(_v) : 0)
|
||||
#define ilog64_nz(_v) builtin_ilog64_nz(_v)
|
||||
#else
|
||||
#define ilog64_nz(_v) ilog64(_v)
|
||||
|
|
|
|||
|
|
@ -467,7 +467,8 @@ static inline void *sintmap_first_(const struct intmap *map,
|
|||
{
|
||||
intmap_index_t i;
|
||||
void *ret = intmap_first_(map, &i);
|
||||
*indexp = SINTMAP_UNOFF(i);
|
||||
if (ret)
|
||||
*indexp = SINTMAP_UNOFF(i);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
|
@ -495,7 +496,8 @@ static inline void *sintmap_last_(const struct intmap *map,
|
|||
{
|
||||
intmap_index_t i;
|
||||
void *ret = intmap_last_(map, &i);
|
||||
*indexp = SINTMAP_UNOFF(i);
|
||||
if (ret)
|
||||
*indexp = SINTMAP_UNOFF(i);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ struct io_listener *io_new_listener_(const tal_t *ctx, int fd,
|
|||
l->ctx = ctx;
|
||||
if (!add_listener(l))
|
||||
return tal_free(l);
|
||||
|
||||
/* Keep accept() async: a connection which vanishes between
|
||||
* poll() and accept() (eg. peer RST) must not block the loop. */
|
||||
io_fd_block(fd, false);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -310,6 +310,18 @@ static bool handle_always(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Is there an always plan we can actually run right now? */
|
||||
static bool always_runnable(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < num_always; i++) {
|
||||
if (!num_exclusive || *exclusive(always[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool backend_set_exclusive(struct io_plan *plan, bool excl)
|
||||
{
|
||||
bool *excl_ptr = exclusive(plan);
|
||||
|
|
@ -374,7 +386,7 @@ void *io_loop(struct timers *timers, struct timer **expired)
|
|||
{
|
||||
void *ret;
|
||||
/* This ensures we don't always service lower fds first */
|
||||
static int fairness_counter;
|
||||
static size_t fairness_counter;
|
||||
|
||||
/* if timers is NULL, expired must be. If not, not. */
|
||||
assert(!timers == !expired);
|
||||
|
|
@ -415,7 +427,7 @@ void *io_loop(struct timers *timers, struct timer **expired)
|
|||
}
|
||||
|
||||
/* Don't wait if we have always requests pending! */
|
||||
if (num_always != 0)
|
||||
if (always_runnable())
|
||||
ms_timeout = 0;
|
||||
|
||||
/* We do this temporarily, assuming exclusive is unusual */
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ static struct json_escape *escape(const tal_t *ctx,
|
|||
|
||||
/* Worst case: all \uXXXX */
|
||||
esc = (struct json_escape *)tal_arr(ctx, char, len * 6 + 1);
|
||||
if (!esc)
|
||||
return NULL;
|
||||
|
||||
for (i = n = 0; i < len; i++, n++) {
|
||||
char escape = 0;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,8 @@ static void unindent(struct json_out *jout, char type)
|
|||
jout->empty = false;
|
||||
}
|
||||
|
||||
/* Make sure jout->outbuf has room for len: return pointer */
|
||||
/* Make sure jout->outbuf has room for len: return pointer, or NULL
|
||||
* if the allocation failed. */
|
||||
static char *mkroom(struct json_out *jout, size_t len)
|
||||
{
|
||||
ptrdiff_t delta = membuf_prepare_space(&jout->outbuf, len);
|
||||
|
|
@ -123,6 +124,11 @@ static char *mkroom(struct json_out *jout, size_t len)
|
|||
if (delta && jout->move_cb)
|
||||
jout->move_cb(jout, delta, jout->cb_arg);
|
||||
|
||||
/* membuf_prepare_space() documents checking membuf_num_space()
|
||||
* to detect allocation failure. */
|
||||
if (membuf_num_space(&jout->outbuf) < len)
|
||||
return NULL;
|
||||
|
||||
return membuf_space(&jout->outbuf);
|
||||
}
|
||||
|
||||
|
|
@ -218,6 +224,7 @@ bool json_out_addv(struct json_out *jout,
|
|||
size_t fmtlen, avail;
|
||||
va_list ap2;
|
||||
char *dst;
|
||||
int vsnprintf_ret;
|
||||
|
||||
if (!json_out_member_direct(jout, fieldname, 0))
|
||||
return false;
|
||||
|
|
@ -236,7 +243,12 @@ bool json_out_addv(struct json_out *jout,
|
|||
|
||||
/* Try printing in place first. */
|
||||
dst = membuf_space(&jout->outbuf);
|
||||
fmtlen = vsnprintf(dst + quote, avail, fmt, ap);
|
||||
vsnprintf_ret = vsnprintf(dst + quote, avail, fmt, ap);
|
||||
if (vsnprintf_ret < 0) {
|
||||
dst = NULL;
|
||||
goto out;
|
||||
}
|
||||
fmtlen = vsnprintf_ret;
|
||||
|
||||
/* Horrible subtlety: vsnprintf *will* NUL terminate, even if it means
|
||||
* chopping off the last character. So if fmtlen ==
|
||||
|
|
@ -259,6 +271,10 @@ bool json_out_addv(struct json_out *jout,
|
|||
if (json_escape_needed(dst + quote, fmtlen)) {
|
||||
struct json_escape *e;
|
||||
e = json_escape_len(NULL, dst + quote, fmtlen);
|
||||
if (!e) {
|
||||
dst = NULL;
|
||||
goto out;
|
||||
}
|
||||
fmtlen = strlen(e->s);
|
||||
dst = mkroom(jout, fmtlen + (int)quote*2);
|
||||
if (!dst)
|
||||
|
|
@ -307,6 +323,8 @@ bool json_out_addstrn(struct json_out *jout,
|
|||
|
||||
if (json_escape_needed(str, len)) {
|
||||
e = json_escape_len(NULL, str, len);
|
||||
if (!e)
|
||||
return false;
|
||||
str = e->s;
|
||||
len = strlen(str);
|
||||
} else
|
||||
|
|
@ -328,11 +346,15 @@ bool json_out_add_splice(struct json_out *jout,
|
|||
{
|
||||
const char *p;
|
||||
size_t len;
|
||||
char *dst;
|
||||
|
||||
p = json_out_contents(src, &len);
|
||||
if (!p)
|
||||
return false;
|
||||
memcpy(json_out_member_direct(jout, fieldname, len), p, len);
|
||||
dst = json_out_member_direct(jout, fieldname, len);
|
||||
if (!dst)
|
||||
return false;
|
||||
memcpy(dst, p, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ char *likely_stats(unsigned int min_hits, unsigned int percent)
|
|||
}
|
||||
}
|
||||
|
||||
if (worst_ratio * 100 > percent)
|
||||
if (!worst || worst_ratio * 100 > percent)
|
||||
return NULL;
|
||||
|
||||
maxlen = strlen(worst->condstr) +
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ void *memrchr(const void *s, int c, size_t n)
|
|||
unsigned char *p = (unsigned char *)s;
|
||||
|
||||
while (n) {
|
||||
if (p[n-1] == c)
|
||||
if (p[n-1] == (unsigned char)c)
|
||||
return p + n - 1;
|
||||
n--;
|
||||
}
|
||||
|
|
@ -56,11 +56,11 @@ void *mempbrkm(const void *data_, size_t len, const void *accept_, size_t accept
|
|||
|
||||
void *memcchr(void const *data, int c, size_t data_len)
|
||||
{
|
||||
char const *p = data;
|
||||
unsigned char const *p = data;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < data_len; i++)
|
||||
if (p[i] != c)
|
||||
if (p[i] != (unsigned char)c)
|
||||
return (void *)&p[i];
|
||||
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -219,6 +219,9 @@ static inline bool memends_str(const void *a, size_t al, const char *s)
|
|||
* @al: length of first memory range
|
||||
* @b: pointer to second memory range
|
||||
* @al: length of second memory range
|
||||
*
|
||||
* Note that a zero-length range counts as overlapping any range that
|
||||
* straddles its address.
|
||||
*/
|
||||
CONST_FUNCTION
|
||||
static inline bool memoverlaps(const void *a_, size_t al,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <ccan/mem/mem.h>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
|
@ -96,8 +97,11 @@ int main(void)
|
|||
haystack1 + sizeof(haystack1), 1));
|
||||
ok1(!memoverlaps(haystack1 + sizeof(haystack1), 1,
|
||||
haystack1, sizeof(haystack1)));
|
||||
ok1(!memoverlaps(haystack1, sizeof(haystack1), haystack1 - 1, 1));
|
||||
ok1(!memoverlaps(haystack1 - 1, 1, haystack1, sizeof(haystack1)));
|
||||
/* Forming haystack1 - 1 directly is UB; round-trip via uintptr_t. */
|
||||
ok1(!memoverlaps(haystack1, sizeof(haystack1),
|
||||
(void *)((uintptr_t)haystack1 - 1), 1));
|
||||
ok1(!memoverlaps((void *)((uintptr_t)haystack1 - 1), 1,
|
||||
haystack1, sizeof(haystack1)));
|
||||
ok1(memoverlaps(haystack1, 5, haystack1 + 4, 7));
|
||||
ok1(!memoverlaps(haystack1, 5, haystack1 + 5, 6));
|
||||
ok1(memoverlaps(haystack1 + 4, 7, haystack1, 5));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void membuf_init_(struct membuf *mb,
|
||||
void *elems, size_t num_elems, size_t elemsize,
|
||||
|
|
@ -42,6 +43,12 @@ size_t membuf_prepare_space_(struct membuf *mb,
|
|||
if (num_extra < mb->max_elems)
|
||||
num_extra = mb->max_elems;
|
||||
|
||||
/* Don't let the allocation size wrap. */
|
||||
if (num_extra > SIZE_MAX / elemsize - mb->max_elems) {
|
||||
errno = ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
expand = mb->expandfn(mb, mb->elems,
|
||||
(mb->max_elems + num_extra) * elemsize);
|
||||
if (!expand) {
|
||||
|
|
@ -51,6 +58,9 @@ size_t membuf_prepare_space_(struct membuf *mb,
|
|||
mb->elems = expand;
|
||||
}
|
||||
}
|
||||
/* Nothing moved if there was no old buffer. */
|
||||
if (!oldstart)
|
||||
return 0;
|
||||
return (char *)membuf_elems_(mb, elemsize) - oldstart;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ static inline size_t membuf_num_elems_(const struct membuf *mb)
|
|||
|
||||
static inline void *membuf_elems_(const struct membuf *mb, size_t elemsize)
|
||||
{
|
||||
if (!mb->elems)
|
||||
return NULL;
|
||||
return mb->elems + mb->start * elemsize;
|
||||
}
|
||||
|
||||
|
|
@ -130,6 +132,8 @@ static inline size_t membuf_num_space_(const struct membuf *mb)
|
|||
|
||||
static inline void *membuf_space_(struct membuf *mb, size_t elemsize)
|
||||
{
|
||||
if (!mb->elems)
|
||||
return NULL;
|
||||
return mb->elems + mb->end * elemsize;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,8 +60,7 @@ char *opt_set_charp(const char *arg, char **p)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Set an integer value, various forms.
|
||||
FIXME: set to 1 on arg == NULL ? */
|
||||
/* Set an integer value, various forms. */
|
||||
char *opt_set_intval(const char *arg, int *i)
|
||||
{
|
||||
long l;
|
||||
|
|
|
|||
|
|
@ -447,14 +447,14 @@ char *opt_set_charp(const char *arg, char **p);
|
|||
/* If *p is NULL, this returns false (i.e. doesn't show a default) */
|
||||
bool opt_show_charp(char *buf, size_t len, char *const *p);
|
||||
|
||||
/* Set an integer value, various forms. Sets to 1 on arg == NULL. */
|
||||
char *opt_set_intval(const char *arg, int *i);
|
||||
/* Set an integer value, various forms. */
|
||||
char *opt_set_intval(const char *arg, int *i) NO_NULL_ARGS;
|
||||
bool opt_show_intval(char *buf, size_t len, const int *i);
|
||||
char *opt_set_uintval(const char *arg, unsigned int *ui);
|
||||
char *opt_set_uintval(const char *arg, unsigned int *ui) NO_NULL_ARGS;
|
||||
bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui);
|
||||
char *opt_set_longval(const char *arg, long *l);
|
||||
char *opt_set_longval(const char *arg, long *l) NO_NULL_ARGS;
|
||||
bool opt_show_longval(char *buf, size_t len, const long *l);
|
||||
char *opt_set_ulongval(const char *arg, unsigned long *ul);
|
||||
char *opt_set_ulongval(const char *arg, unsigned long *ul) NO_NULL_ARGS;
|
||||
bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul);
|
||||
|
||||
/* Set an floating point value, various forms. */
|
||||
|
|
|
|||
|
|
@ -92,12 +92,13 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
|
|||
arg = 1;
|
||||
} else {
|
||||
for (arg = 1; argv[arg]; arg++) {
|
||||
if (argv[arg][0] == '-')
|
||||
if (argv[arg][0] == '-' && argv[arg][1])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!argv[arg] || argv[arg][0] != '-')
|
||||
/* A bare '-' is an operand, not an option. */
|
||||
if (!argv[arg] || argv[arg][0] != '-' || argv[arg][1] == '\0')
|
||||
return 0;
|
||||
|
||||
/* Special arg terminator option. */
|
||||
|
|
|
|||
|
|
@ -56,13 +56,13 @@ int main(void)
|
|||
ok1(total_order_cmp(order2, &item2, &item2) == 0);
|
||||
ok1(total_order_cmp(order2, &item3, &item3) == 0);
|
||||
|
||||
ok1(total_order_cmp(order2, &item1, &item2) == 1);
|
||||
ok1(total_order_cmp(order2, &item1, &item2) == -1);
|
||||
ok1(total_order_cmp(order2, &item2, &item3) == 1);
|
||||
ok1(total_order_cmp(order2, &item1, &item3) == 1);
|
||||
|
||||
ok1(total_order_cmp(order2, &item2, &item1) == -1);
|
||||
ok1(total_order_cmp(order2, &item2, &item1) == 1);
|
||||
ok1(total_order_cmp(order2, &item3, &item2) == -1);
|
||||
ok1(total_order_cmp(order2, &item3, &item1) == -1);
|
||||
|
||||
exit(0);
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
|
|||
int child_close[4], num_child_close = 0;
|
||||
pid_t childpid;
|
||||
int err;
|
||||
ssize_t r;
|
||||
|
||||
if (fd_tochild) {
|
||||
if (fd_tochild == &pipecmd_preserve) {
|
||||
|
|
@ -164,7 +165,9 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
|
|||
if (write(execfail[1], &err, sizeof(err))) {
|
||||
;
|
||||
}
|
||||
exit(127);
|
||||
/* _exit: don't flush the parent's stdio buffers again,
|
||||
* nor run its atexit handlers. */
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
int i;
|
||||
|
|
@ -172,9 +175,22 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
|
|||
close(par_close[i]);
|
||||
|
||||
/* Child will close this without writing on successful exec. */
|
||||
if (read(execfail[0], &err, sizeof(err)) == sizeof(err)) {
|
||||
do {
|
||||
r = read(execfail[0], &err, sizeof(err));
|
||||
} while (r < 0 && errno == EINTR);
|
||||
|
||||
if (r == sizeof(err)) {
|
||||
close(execfail[0]);
|
||||
waitpid(childpid, NULL, 0);
|
||||
/* Useless now: close the parent-side pipe ends too. */
|
||||
if (fd_tochild && fd_tochild != &pipecmd_preserve)
|
||||
close(tochild[1]);
|
||||
if (fd_fromchild && fd_fromchild != &pipecmd_preserve)
|
||||
close(fromchild[0]);
|
||||
if (fd_errfromchild && fd_errfromchild != &pipecmd_preserve
|
||||
&& fd_errfromchild != fd_fromchild)
|
||||
close(errfromchild[0]);
|
||||
while (waitpid(childpid, NULL, 0) < 0 && errno == EINTR)
|
||||
;
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -190,6 +206,8 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
|
|||
fail:
|
||||
for (i = 0; i < num_par_close; i++)
|
||||
close_noerr(par_close[i]);
|
||||
for (i = 0; i < num_child_close; i++)
|
||||
close_noerr(child_close[i]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,10 @@ static char *grab(const char *filename)
|
|||
while ((ret = read(fd, buffer + s, max - s)) > 0) {
|
||||
s += ret;
|
||||
if (s == max) {
|
||||
buffer = realloc(buffer, max*2+1);
|
||||
if (!buffer)
|
||||
goto close;
|
||||
char *nb = realloc(buffer, max*2+1);
|
||||
if (!nb)
|
||||
goto free;
|
||||
buffer = nb;
|
||||
max *= 2;
|
||||
}
|
||||
}
|
||||
|
|
@ -62,10 +63,14 @@ static struct ptr_valid_map *add_map(struct ptr_valid_map *map,
|
|||
unsigned long start, unsigned long end, bool is_write)
|
||||
{
|
||||
if (*num == *max) {
|
||||
struct ptr_valid_map *newmap;
|
||||
*max *= 2;
|
||||
map = realloc(map, sizeof(*map) * *max);
|
||||
if (!map)
|
||||
newmap = realloc(map, sizeof(*newmap) * *max);
|
||||
if (!newmap) {
|
||||
free(map);
|
||||
return NULL;
|
||||
}
|
||||
map = newmap;
|
||||
}
|
||||
map[*num].start = (void *)start;
|
||||
map[*num].end = (void *)end;
|
||||
|
|
@ -182,9 +187,9 @@ static void run_child(int infd, int outfd)
|
|||
|
||||
/* This is weird. */
|
||||
if (read(infd, &size, sizeof(size)) != sizeof(size))
|
||||
exit(1);
|
||||
_exit(1);
|
||||
if (read(infd, &is_write, sizeof(is_write)) != sizeof(is_write))
|
||||
exit(2);
|
||||
_exit(2);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
ret = p[i];
|
||||
|
|
@ -194,9 +199,9 @@ static void run_child(int infd, int outfd)
|
|||
|
||||
/* If we're still here, the answer is "yes". */
|
||||
if (write(outfd, &ret, 1) != 1)
|
||||
exit(3);
|
||||
_exit(3);
|
||||
}
|
||||
exit(0);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
static bool create_child(struct ptr_valid_batch *batch)
|
||||
|
|
@ -271,16 +276,22 @@ bool ptr_valid_batch(struct ptr_valid_batch *batch,
|
|||
char *start, *end;
|
||||
bool ret;
|
||||
|
||||
if ((intptr_t)p & (alignment - 1))
|
||||
if ((intptr_t)p & (alignment - 1)) {
|
||||
errno = EFAULT;
|
||||
return false;
|
||||
}
|
||||
|
||||
start = (void *)((intptr_t)p & ~(getpagesize() - 1));
|
||||
end = (void *)(((intptr_t)p + size - 1) & ~(getpagesize() - 1));
|
||||
|
||||
/* We cache single page hits. */
|
||||
if (start == end) {
|
||||
if (batch->last && batch->last == start)
|
||||
if (batch->last && batch->last == start
|
||||
&& batch->last_write == write) {
|
||||
if (!batch->last_ok)
|
||||
errno = EFAULT;
|
||||
return batch->last_ok;
|
||||
}
|
||||
}
|
||||
|
||||
if (batch->num_maps)
|
||||
|
|
@ -291,8 +302,11 @@ bool ptr_valid_batch(struct ptr_valid_batch *batch,
|
|||
if (start == end) {
|
||||
batch->last = start;
|
||||
batch->last_ok = ret;
|
||||
batch->last_write = write;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
errno = EFAULT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ struct ptr_valid_batch {
|
|||
int to_child, from_child;
|
||||
void *last;
|
||||
bool last_ok;
|
||||
bool last_write;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -62,8 +62,14 @@ void *rbuf_fill_all(struct rbuf *rbuf)
|
|||
void *rbuf_fill(struct rbuf *rbuf)
|
||||
{
|
||||
if (!rbuf_len(rbuf)) {
|
||||
if (get_more(rbuf) < 0)
|
||||
ssize_t r = get_more(rbuf);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
/* EOF: documented NULL with errno 0. */
|
||||
if (r == 0) {
|
||||
errno = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return rbuf_start(rbuf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,7 +206,8 @@ bool rune_condition_is_valid(enum rune_condition cond)
|
|||
size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen)
|
||||
{
|
||||
for (size_t i = 0; i < alternstrlen; i++) {
|
||||
if (cispunct(alternstr[i]) && alternstr[i] != '_')
|
||||
if (cispunct(alternstr[i]) && alternstr[i] != '_'
|
||||
&& alternstr[i] != '-' && alternstr[i] != '.')
|
||||
return i;
|
||||
}
|
||||
return alternstrlen;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES)
|
|||
return tal_steal(ctx, (struct rune *)rune);
|
||||
|
||||
dup = tal_dup(ctx, struct rune, rune);
|
||||
if (rune->unique_id)
|
||||
dup->unique_id = tal_strdup(dup, rune->unique_id);
|
||||
if (rune->version)
|
||||
dup->version = tal_strdup(dup, rune->version);
|
||||
dup->restrs = tal_arr(dup, struct rune_restr *, tal_count(rune->restrs));
|
||||
for (size_t i = 0; i < tal_count(rune->restrs); i++) {
|
||||
dup->restrs[i] = rune_restr_dup(dup->restrs,
|
||||
|
|
@ -287,11 +291,16 @@ static int lexo_order(const char *fieldval_str,
|
|||
size_t fieldval_strlen,
|
||||
const char *alt)
|
||||
{
|
||||
int ret = strncmp(fieldval_str, alt, fieldval_strlen);
|
||||
size_t altlen = strlen(alt);
|
||||
size_t minlen = fieldval_strlen < altlen ? fieldval_strlen : altlen;
|
||||
int ret = memcmp(fieldval_str, alt, minlen);
|
||||
|
||||
/* If alt is same but longer, fieldval is < */
|
||||
if (ret == 0 && strlen(alt) > fieldval_strlen)
|
||||
ret = -1;
|
||||
if (ret == 0) {
|
||||
if (fieldval_strlen < altlen)
|
||||
ret = -1;
|
||||
else if (fieldval_strlen > altlen)
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
* *size_total += size;
|
||||
* }
|
||||
*
|
||||
* #define EVALUATE(psx, short, pt, st, t) \
|
||||
* #define EVALUATE(psx, sht, pt, st, t) \
|
||||
* evaluate(sizeof(psx), stringify(psx), stringify(sht), pt, st, t)
|
||||
*
|
||||
* int main(void)
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ bool base32_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
|
|||
destsize -= 8;
|
||||
dest += 8;
|
||||
}
|
||||
if (destsize != 1)
|
||||
if (destsize < 1)
|
||||
return false;
|
||||
*dest = '\0';
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
* for (i = 1; i < argc; i++) {
|
||||
* char str[hex_str_size(strlen(argv[i]))];
|
||||
*
|
||||
* hex_encode(str, sizeof(str), argv[i], strlen(argv[i]));
|
||||
* hex_encode(argv[i], strlen(argv[i]), str, sizeof(str));
|
||||
* printf("%s ", str);
|
||||
* }
|
||||
* printf("\n");
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ size_t strcount(const char *haystack, const char *needle)
|
|||
{
|
||||
size_t i = 0, nlen = strlen(needle);
|
||||
|
||||
if (nlen == 0)
|
||||
return 0;
|
||||
|
||||
while ((haystack = strstr(haystack, needle)) != NULL) {
|
||||
i++;
|
||||
haystack += nlen;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (strcmp(argv[1], "depends") == 0) {
|
||||
printf("ccan/ilog\n"
|
||||
"ccan/mem\n"
|
||||
"ccan/short_types\n"
|
||||
"ccan/str\n"
|
||||
"ccan/tcon\n"
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
#include <ccan/strmap/strmap.h>
|
||||
#include <ccan/short_types/short_types.h>
|
||||
#include <ccan/str/str.h>
|
||||
#include <ccan/mem/mem.h>
|
||||
#include <ccan/ilog/ilog.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
struct node {
|
||||
|
|
@ -42,7 +44,7 @@ void *strmap_getn_(const struct strmap *map,
|
|||
/* Not empty map? */
|
||||
if (map->u.n) {
|
||||
n = closest((struct strmap *)map, member, memberlen);
|
||||
if (!strncmp(member, n->u.s, memberlen) && !n->u.s[memberlen])
|
||||
if (memeqstr(member, memberlen, n->u.s))
|
||||
return n->v;
|
||||
}
|
||||
errno = ENOENT;
|
||||
|
|
@ -178,26 +180,119 @@ char *strmap_del_(struct strmap *map, const char *member, void **valuep)
|
|||
return (char *)ret;
|
||||
}
|
||||
|
||||
static bool iterate(struct strmap n,
|
||||
bool (*handle)(const char *, void *, void *),
|
||||
const void *data)
|
||||
/* Defer child[1] of a node we're descending past. */
|
||||
static void iter_push(struct strmap_iter *it, struct strmap *slot)
|
||||
{
|
||||
if (n.v)
|
||||
return handle(n.u.s, n.v, (void *)data);
|
||||
if (STRMAP_NUM_ITER_PARENTS == 0) {
|
||||
it->dropped = true;
|
||||
return;
|
||||
}
|
||||
if (it->num_parents == STRMAP_NUM_ITER_PARENTS) {
|
||||
/* Full: drop the *shallowest* deferral (kept in-order by
|
||||
* the slow path once the stack runs out). */
|
||||
memmove(&it->parents[0], &it->parents[1],
|
||||
sizeof(it->parents[0]) * (STRMAP_NUM_ITER_PARENTS - 1));
|
||||
it->num_parents--;
|
||||
it->dropped = true;
|
||||
}
|
||||
it->parents[it->num_parents++] = slot;
|
||||
}
|
||||
|
||||
return iterate(n.u.n->child[0], handle, data)
|
||||
&& iterate(n.u.n->child[1], handle, data);
|
||||
/* Descend leftmost from *slot, deferring child[1]s, and yield the leaf. */
|
||||
static const char *iter_descend(struct strmap_iter *it, struct strmap *slot,
|
||||
void **valuep)
|
||||
{
|
||||
while (!slot->v) {
|
||||
iter_push(it, &slot->u.n->child[1]);
|
||||
slot = &slot->u.n->child[0];
|
||||
}
|
||||
*valuep = slot->v;
|
||||
return slot->u.s;
|
||||
}
|
||||
|
||||
/* Successor of cur by value: O(depth), no stack. */
|
||||
static const char *iter_successor(const struct strmap *map,
|
||||
const char *cur, void **valuep)
|
||||
{
|
||||
size_t len = strlen(cur);
|
||||
const u8 *bytes = (const u8 *)cur;
|
||||
struct strmap n, cand;
|
||||
bool have_cand = false;
|
||||
|
||||
n = *(struct strmap *)map;
|
||||
while (!n.v) {
|
||||
u8 c = 0, direction;
|
||||
|
||||
if (n.u.n->byte_num < len)
|
||||
c = bytes[n.u.n->byte_num];
|
||||
direction = (c >> n.u.n->bit_num) & 1;
|
||||
if (direction == 0) {
|
||||
/* Everything in child[1] sorts after child[0]. */
|
||||
cand = n.u.n->child[1];
|
||||
have_cand = true;
|
||||
}
|
||||
n = n.u.n->child[direction];
|
||||
}
|
||||
|
||||
if (!have_cand)
|
||||
return NULL;
|
||||
|
||||
/* Leftmost member of the deepest candidate subtree. */
|
||||
while (!cand.v)
|
||||
cand = cand.u.n->child[0];
|
||||
*valuep = cand.v;
|
||||
return cand.u.s;
|
||||
}
|
||||
|
||||
const char *strmap_iter_first_(struct strmap_iter *it,
|
||||
const struct strmap *map, void **valuep)
|
||||
{
|
||||
it->num_parents = 0;
|
||||
it->dropped = false;
|
||||
it->slow_mode = false;
|
||||
|
||||
if (!map->u.n)
|
||||
return NULL;
|
||||
|
||||
return iter_descend(it, (struct strmap *)map, valuep);
|
||||
}
|
||||
|
||||
const char *strmap_iter_next_(struct strmap_iter *it,
|
||||
const struct strmap *map, const char *cur,
|
||||
void **valuep)
|
||||
{
|
||||
struct strmap *slot;
|
||||
|
||||
if (!it->slow_mode) {
|
||||
if (it->num_parents != 0) {
|
||||
slot = it->parents[--it->num_parents];
|
||||
return iter_descend(it, slot, valuep);
|
||||
}
|
||||
if (!it->dropped)
|
||||
return NULL;
|
||||
/* We dropped deferrals past STRMAP_NUM_ITER_PARENTS;
|
||||
* from here on, find successors by re-descent. */
|
||||
it->dropped = false;
|
||||
it->slow_mode = true;
|
||||
}
|
||||
|
||||
return iter_successor(map, cur, valuep);
|
||||
}
|
||||
|
||||
void strmap_iterate_(const struct strmap *map,
|
||||
bool (*handle)(const char *, void *, void *),
|
||||
const void *data)
|
||||
{
|
||||
/* Empty map? */
|
||||
if (!map->u.n)
|
||||
return;
|
||||
struct strmap_iter it;
|
||||
const char *m;
|
||||
void *v;
|
||||
|
||||
iterate(*map, handle, data);
|
||||
for (m = strmap_iter_first_(&it, map, &v);
|
||||
m;
|
||||
m = strmap_iter_next_(&it, map, m, &v)) {
|
||||
if (!handle(m, v, (void *)data))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const struct strmap *strmap_prefix_(const struct strmap *map,
|
||||
|
|
@ -235,6 +330,7 @@ const struct strmap *strmap_prefix_(const struct strmap *map,
|
|||
return top;
|
||||
}
|
||||
|
||||
/* Recursive fallback for strmap_clear_'s OOM path. */
|
||||
static void clear(struct strmap n)
|
||||
{
|
||||
if (!n.v) {
|
||||
|
|
@ -246,7 +342,75 @@ static void clear(struct strmap n)
|
|||
|
||||
void strmap_clear_(struct strmap *map)
|
||||
{
|
||||
if (map->u.n)
|
||||
clear(*map);
|
||||
uintptr_t inline_stack[STRMAP_NUM_ITER_PARENTS];
|
||||
uintptr_t *stack = inline_stack;
|
||||
size_t num = 0, max = STRMAP_NUM_ITER_PARENTS;
|
||||
uintptr_t cur;
|
||||
bool have_cur;
|
||||
|
||||
if (!map->u.n)
|
||||
return;
|
||||
|
||||
/* Post-order without recursion: slot pointers tagged in their
|
||||
* low bits (0 = visit child[0], 1 = visit child[1], 2 = free). */
|
||||
cur = (uintptr_t)map;
|
||||
have_cur = true;
|
||||
|
||||
while (have_cur || num) {
|
||||
struct strmap *slot;
|
||||
unsigned int tag;
|
||||
|
||||
if (!have_cur)
|
||||
cur = stack[--num];
|
||||
have_cur = false;
|
||||
slot = (struct strmap *)(cur & ~(uintptr_t)3);
|
||||
tag = cur & 3;
|
||||
|
||||
if (slot->v) {
|
||||
/* Leaf: caller-owned, keep. */
|
||||
continue;
|
||||
}
|
||||
if (tag == 2) {
|
||||
free(slot->u.n);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* tag 0 or 1: requeue for the next phase, then descend. */
|
||||
{
|
||||
struct strmap *child = &slot->u.n->child[tag];
|
||||
|
||||
if (num == max) {
|
||||
uintptr_t *ns;
|
||||
size_t nmax = max ? max * 2 : 64;
|
||||
|
||||
if (stack == inline_stack) {
|
||||
ns = malloc(nmax * sizeof(*ns));
|
||||
if (ns)
|
||||
memcpy(ns, stack,
|
||||
num * sizeof(*ns));
|
||||
} else {
|
||||
ns = realloc(stack, nmax * sizeof(*ns));
|
||||
}
|
||||
if (ns) {
|
||||
stack = ns;
|
||||
max = nmax;
|
||||
} else {
|
||||
/* OOM: finish this subtree
|
||||
* recursively. */
|
||||
clear(*child);
|
||||
if (tag == 0)
|
||||
clear(slot->u.n->child[1]);
|
||||
free(slot->u.n);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
stack[num++] = (uintptr_t)slot | (tag + 1);
|
||||
cur = (uintptr_t)child;
|
||||
have_cur = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (stack != inline_stack)
|
||||
free(stack);
|
||||
map->u.n = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <ccan/typesafe_cb/typesafe_cb.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* struct strmap - representation of a string map
|
||||
|
|
@ -204,6 +205,75 @@ void strmap_iterate_(const struct strmap *map,
|
|||
bool (*handle)(const char *, void *, void *),
|
||||
const void *data);
|
||||
|
||||
#ifndef STRMAP_NUM_ITER_PARENTS
|
||||
#define STRMAP_NUM_ITER_PARENTS 16
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct strmap_iter - state for strmap_iter_first/strmap_iter_next.
|
||||
*
|
||||
* This is exposed so you can declare it on the stack. It holds up to
|
||||
* STRMAP_NUM_ITER_PARENTS deferred subtrees; in trees deeper than
|
||||
* that, iteration falls back to re-descending from the root (an
|
||||
* O(depth) successor search per step), so memory use stays bounded
|
||||
* for arbitrarily deep trees.
|
||||
*/
|
||||
struct strmap_iter {
|
||||
struct strmap *parents[STRMAP_NUM_ITER_PARENTS];
|
||||
uint16_t num_parents;
|
||||
bool dropped;
|
||||
bool slow_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* strmap_iter_first - begin an ordered iteration over a map.
|
||||
* @it: the iterator to initialize.
|
||||
* @map: the typed strmap to iterate.
|
||||
* @valuep: a pointer to a value to fill in.
|
||||
*
|
||||
* Returns the first member, and sets *@valuep, or returns NULL if the
|
||||
* map is empty. You should not alter the map during iteration!
|
||||
*
|
||||
* Example:
|
||||
* static void dump_map_iter(const STRMAP(int *) *map)
|
||||
* {
|
||||
* struct strmap_iter it;
|
||||
* const char *m;
|
||||
* int *v;
|
||||
*
|
||||
* for (m = strmap_iter_first(&it, map, &v);
|
||||
* m;
|
||||
* m = strmap_iter_next(&it, map, m, &v))
|
||||
* printf("%s=>%i\n", m, *v);
|
||||
* }
|
||||
*/
|
||||
#define strmap_iter_first(it, map, valuep) \
|
||||
strmap_iter_first_((it), \
|
||||
tcon_unwrap(tcon_check_ptr((map), canary, \
|
||||
(valuep))), \
|
||||
(void *)(valuep))
|
||||
const char *strmap_iter_first_(struct strmap_iter *it,
|
||||
const struct strmap *map, void **valuep);
|
||||
|
||||
/**
|
||||
* strmap_iter_next - continue an ordered iteration.
|
||||
* @it: the iterator, initialized by strmap_iter_first().
|
||||
* @map: the typed strmap.
|
||||
* @cur: the member the iteration is currently on.
|
||||
* @valuep: a pointer to a value to fill in.
|
||||
*
|
||||
* Returns the next member, and sets *@valuep, or NULL at the end of
|
||||
* the map.
|
||||
*/
|
||||
#define strmap_iter_next(it, map, cur, valuep) \
|
||||
strmap_iter_next_((it), \
|
||||
tcon_unwrap(tcon_check_ptr((map), canary, \
|
||||
(valuep))), \
|
||||
(cur), (void *)(valuep))
|
||||
const char *strmap_iter_next_(struct strmap_iter *it,
|
||||
const struct strmap *map, const char *cur,
|
||||
void **valuep);
|
||||
|
||||
/**
|
||||
* strmap_prefix - return a submap matching a prefix
|
||||
* @map: the map.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <ccan/ilog/ilog.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
struct node {
|
||||
|
|
@ -228,26 +229,127 @@ char *strset_del(struct strset *set, const char *member)
|
|||
return (char *)ret;
|
||||
}
|
||||
|
||||
static bool iterate(struct strset n,
|
||||
bool (*handle)(const char *, void *), const void *data)
|
||||
{
|
||||
if (n.u.s[0])
|
||||
return handle(n.u.s, (void *)data);
|
||||
if (unlikely(n.u.n->byte_num == (size_t)-1))
|
||||
return handle(n.u.n->child[0].u.s, (void *)data);
|
||||
|
||||
return iterate(n.u.n->child[0], handle, data)
|
||||
&& iterate(n.u.n->child[1], handle, data);
|
||||
}
|
||||
|
||||
void strset_iterate_(const struct strset *set,
|
||||
bool (*handle)(const char *, void *), const void *data)
|
||||
{
|
||||
/* Empty set? */
|
||||
if (!set->u.n)
|
||||
return;
|
||||
struct strset_iter it;
|
||||
const char *m;
|
||||
|
||||
iterate(*set, handle, data);
|
||||
for (m = strset_iter_first(&it, set);
|
||||
m;
|
||||
m = strset_iter_next(&it, set, m)) {
|
||||
if (!handle(m, (void *)data))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Defer child[1] of a node we're descending past. */
|
||||
static void iter_push(struct strset_iter *it, struct strset *slot)
|
||||
{
|
||||
if (STRSET_NUM_ITER_PARENTS == 0) {
|
||||
it->dropped = true;
|
||||
return;
|
||||
}
|
||||
if (it->num_parents == STRSET_NUM_ITER_PARENTS) {
|
||||
/* Full: drop the *shallowest* deferral (kept in-order by
|
||||
* the slow_mode path once the stack runs out). */
|
||||
memmove(&it->parents[0], &it->parents[1],
|
||||
sizeof(it->parents[0]) * (STRSET_NUM_ITER_PARENTS - 1));
|
||||
it->num_parents--;
|
||||
it->dropped = true;
|
||||
}
|
||||
it->parents[it->num_parents++] = slot;
|
||||
}
|
||||
|
||||
/* Descend leftmost from *slot, deferring child[1]s, and yield the leaf. */
|
||||
static const char *iter_descend(struct strset_iter *it, struct strset *slot)
|
||||
{
|
||||
while (!slot->u.s[0]) {
|
||||
/* Empty-string node: the string is child[0]. */
|
||||
if (unlikely(slot->u.n->byte_num == (size_t)-1)) {
|
||||
slot = &slot->u.n->child[0];
|
||||
break;
|
||||
}
|
||||
iter_push(it, &slot->u.n->child[1]);
|
||||
slot = &slot->u.n->child[0];
|
||||
}
|
||||
return slot->u.s;
|
||||
}
|
||||
|
||||
/* Successor of cur by value: O(depth), no stack. */
|
||||
static const char *iter_successor(const struct strset *set,
|
||||
const char *cur)
|
||||
{
|
||||
size_t len = strlen(cur);
|
||||
const u8 *bytes = (const u8 *)cur;
|
||||
struct strset n, cand;
|
||||
bool have_cand = false;
|
||||
|
||||
n = *(struct strset *)set;
|
||||
while (!n.u.s[0]) {
|
||||
u8 c = 0, direction;
|
||||
|
||||
/* Empty-string node: only holds "" in child[0]. */
|
||||
if (unlikely(n.u.n->byte_num == (size_t)-1))
|
||||
break;
|
||||
if (n.u.n->byte_num < len)
|
||||
c = bytes[n.u.n->byte_num];
|
||||
direction = (c >> n.u.n->bit_num) & 1;
|
||||
if (direction == 0) {
|
||||
/* Everything in child[1] sorts after child[0]. */
|
||||
cand = n.u.n->child[1];
|
||||
have_cand = true;
|
||||
}
|
||||
n = n.u.n->child[direction];
|
||||
}
|
||||
|
||||
if (!have_cand)
|
||||
return NULL;
|
||||
|
||||
/* Leftmost member of the deepest candidate subtree. */
|
||||
while (!cand.u.s[0]) {
|
||||
if (unlikely(cand.u.n->byte_num == (size_t)-1)) {
|
||||
cand = cand.u.n->child[0];
|
||||
break;
|
||||
}
|
||||
cand = cand.u.n->child[0];
|
||||
}
|
||||
return cand.u.s;
|
||||
}
|
||||
|
||||
const char *strset_iter_first(struct strset_iter *it,
|
||||
const struct strset *set)
|
||||
{
|
||||
it->num_parents = 0;
|
||||
it->dropped = false;
|
||||
it->slow_mode = false;
|
||||
|
||||
if (!set->u.n)
|
||||
return NULL;
|
||||
|
||||
return iter_descend(it, (struct strset *)set);
|
||||
}
|
||||
|
||||
const char *strset_iter_next(struct strset_iter *it,
|
||||
const struct strset *set,
|
||||
const char *cur)
|
||||
{
|
||||
struct strset *slot;
|
||||
|
||||
if (likely(!it->slow_mode)) {
|
||||
if (it->num_parents != 0) {
|
||||
slot = it->parents[--it->num_parents];
|
||||
return iter_descend(it, slot);
|
||||
}
|
||||
if (!it->dropped)
|
||||
return NULL;
|
||||
/* We dropped deferrals past STRSET_NUM_ITER_PARENTS;
|
||||
* from here on, find successors by re-descent. */
|
||||
it->dropped = false;
|
||||
it->slow_mode = true;
|
||||
}
|
||||
|
||||
return iter_successor(set, cur);
|
||||
}
|
||||
|
||||
const struct strset *strset_prefix(const struct strset *set, const char *prefix)
|
||||
|
|
@ -290,6 +392,7 @@ const struct strset *strset_prefix(const struct strset *set, const char *prefix)
|
|||
return top;
|
||||
}
|
||||
|
||||
/* Recursive fallback for strset_clear's OOM path. */
|
||||
static void clear(struct strset n)
|
||||
{
|
||||
if (!n.u.s[0]) {
|
||||
|
|
@ -303,7 +406,81 @@ static void clear(struct strset n)
|
|||
|
||||
void strset_clear(struct strset *set)
|
||||
{
|
||||
if (set->u.n)
|
||||
clear(*set);
|
||||
uintptr_t inline_stack[STRSET_NUM_ITER_PARENTS];
|
||||
uintptr_t *stack = inline_stack;
|
||||
size_t num = 0, max = STRSET_NUM_ITER_PARENTS;
|
||||
uintptr_t cur;
|
||||
bool have_cur;
|
||||
|
||||
if (!set->u.n)
|
||||
return;
|
||||
|
||||
/* Post-order without recursion: slot pointers tagged in their
|
||||
* low bits (0 = visit child[0], 1 = visit child[1], 2 = free). */
|
||||
cur = (uintptr_t)set;
|
||||
have_cur = true;
|
||||
|
||||
while (have_cur || num) {
|
||||
struct strset *slot;
|
||||
unsigned int tag;
|
||||
|
||||
if (!have_cur)
|
||||
cur = stack[--num];
|
||||
have_cur = false;
|
||||
slot = (struct strset *)(cur & ~(uintptr_t)3);
|
||||
tag = cur & 3;
|
||||
|
||||
if (slot->u.s[0]) {
|
||||
/* Leaf: caller-owned, keep. */
|
||||
continue;
|
||||
}
|
||||
if (unlikely(slot->u.n->byte_num == (size_t)-1)) {
|
||||
/* Empty-string node: child[0] is the caller-owned
|
||||
* string itself; child[1] is unused. */
|
||||
free(slot->u.n);
|
||||
continue;
|
||||
}
|
||||
if (tag == 2) {
|
||||
free(slot->u.n);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* tag 0 or 1: requeue for the next phase, then descend. */
|
||||
{
|
||||
struct strset *child = &slot->u.n->child[tag];
|
||||
|
||||
if (num == max) {
|
||||
uintptr_t *ns;
|
||||
size_t nmax = max ? max * 2 : 64;
|
||||
|
||||
if (stack == inline_stack) {
|
||||
ns = malloc(nmax * sizeof(*ns));
|
||||
if (ns)
|
||||
memcpy(ns, stack,
|
||||
num * sizeof(*ns));
|
||||
} else {
|
||||
ns = realloc(stack, nmax * sizeof(*ns));
|
||||
}
|
||||
if (ns) {
|
||||
stack = ns;
|
||||
max = nmax;
|
||||
} else {
|
||||
/* OOM: finish this subtree
|
||||
* recursively. */
|
||||
clear(*child);
|
||||
if (tag == 0)
|
||||
clear(slot->u.n->child[1]);
|
||||
free(slot->u.n);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
stack[num++] = (uintptr_t)slot | (tag + 1);
|
||||
cur = (uintptr_t)child;
|
||||
have_cur = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (stack != inline_stack)
|
||||
free(stack);
|
||||
set->u.n = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <ccan/typesafe_cb/typesafe_cb.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* struct strset - representation of a string set
|
||||
|
|
@ -141,6 +142,61 @@ void strset_clear(struct strset *set);
|
|||
void strset_iterate_(const struct strset *set,
|
||||
bool (*handle)(const char *, void *), const void *data);
|
||||
|
||||
#ifndef STRSET_NUM_ITER_PARENTS
|
||||
#define STRSET_NUM_ITER_PARENTS 16
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct strset_iter - state for strset_iter_first/strset_iter_next.
|
||||
*
|
||||
* This is exposed so you can declare it on the stack. It holds up to
|
||||
* STRSET_NUM_ITER_PARENTS deferred subtrees; in trees deeper than
|
||||
* that, iteration falls back to re-descending from the root (an
|
||||
* O(depth) successor search per step), so memory use stays bounded
|
||||
* for arbitrarily deep trees.
|
||||
*/
|
||||
struct strset_iter {
|
||||
struct strset *parents[STRSET_NUM_ITER_PARENTS];
|
||||
uint16_t num_parents;
|
||||
bool dropped;
|
||||
bool slow_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* strset_iter_first - begin an ordered iteration over a set.
|
||||
* @it: the iterator to initialize.
|
||||
* @set: the set.
|
||||
*
|
||||
* Returns the first member, or NULL if the set is empty.
|
||||
* You should not alter the set during iteration!
|
||||
*
|
||||
* Example:
|
||||
* static void dump_set_iter(const struct strset *set)
|
||||
* {
|
||||
* struct strset_iter it;
|
||||
* const char *m;
|
||||
*
|
||||
* for (m = strset_iter_first(&it, set);
|
||||
* m;
|
||||
* m = strset_iter_next(&it, set, m))
|
||||
* printf("%s\n", m);
|
||||
* }
|
||||
*/
|
||||
const char *strset_iter_first(struct strset_iter *it,
|
||||
const struct strset *set);
|
||||
|
||||
/**
|
||||
* strset_iter_next - continue an ordered iteration.
|
||||
* @it: the iterator, initialized by strset_iter_first().
|
||||
* @set: the set.
|
||||
* @cur: the member the iteration is currently on.
|
||||
*
|
||||
* Returns the next member, or NULL at the end of the set.
|
||||
*/
|
||||
const char *strset_iter_next(struct strset_iter *it,
|
||||
const struct strset *set,
|
||||
const char *cur);
|
||||
|
||||
|
||||
/**
|
||||
* strset_prefix - return a subset matching a prefix
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@
|
|||
* there isn't any, or how many we expect. A negative number means
|
||||
* "up to or equal to that amount of padding", as padding can be
|
||||
* platform dependent.
|
||||
*
|
||||
* Note that members which are themselves structures or unions are
|
||||
* compared with memcmp(), so any *internal* padding they contain can
|
||||
* cause false negatives, just like top-level padding would.
|
||||
*/
|
||||
#define STRUCTEQ_DEF(sname, padbytes, ...) \
|
||||
static inline bool CPPMAGIC_GLUE2(sname, _eq)(const struct sname *_a, \
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ bool taken(const void *p)
|
|||
|
||||
memmove(&takenarr[i-1], &takenarr[i],
|
||||
(--num_taken - (i - 1))*sizeof(takenarr[0]));
|
||||
if (labelarr) {
|
||||
memmove(&labelarr[i-1], &labelarr[i],
|
||||
(num_taken - (i - 1))*sizeof(labelarr[0]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +118,7 @@ const char *taken_any(void)
|
|||
void take_cleanup(void)
|
||||
{
|
||||
max_taken = num_taken = 0;
|
||||
allocfail = 0;
|
||||
free(takenarr);
|
||||
takenarr = NULL;
|
||||
free(labelarr);
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@ static void *grab_fd_internal(const void *ctx, int fd, bool add_nul_term)
|
|||
|
||||
size = 0;
|
||||
|
||||
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode))
|
||||
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size != 0)
|
||||
max = st.st_size;
|
||||
else
|
||||
/* Non-regular file, or one reporting a zero size despite
|
||||
* having content (eg. /proc, /sys): guess and grow. */
|
||||
max = 16384;
|
||||
|
||||
buffer = tal_arr(ctx, char, max+add_nul_term);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
* The object will be freed when @newobj is freed or the last tal_link()
|
||||
* is tal_delink'ed.
|
||||
*
|
||||
* Note: @newobj must not be tal_free()'d or tal_steal()'d while it has
|
||||
* links: that aborts. Remove all links first.
|
||||
*
|
||||
* Returns @newobj or NULL (if an allocation fails).
|
||||
*
|
||||
* Example:
|
||||
|
|
|
|||
|
|
@ -321,7 +321,8 @@ fail_take_to:
|
|||
break;
|
||||
}
|
||||
|
||||
if (!tal_resize(&ret, maxlen *= 2 + 1))
|
||||
maxlen = maxlen * 2 + 1;
|
||||
if (!tal_resize(&ret, maxlen))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
@ -393,8 +394,12 @@ char *path_simplify(const tal_t *ctx, const char *path)
|
|||
j = sep - ret + 1;
|
||||
else
|
||||
j = 0;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
/* Symlink or nonexistent: can't safely step
|
||||
* back, so keep the ".." literally. */
|
||||
ret[j-1] = PATH_SEP;
|
||||
goto copy;
|
||||
} else if (start) {
|
||||
/* /.. => / */
|
||||
j = 1;
|
||||
|
|
@ -436,20 +441,27 @@ char *path_basename(const tal_t *ctx, const char *path)
|
|||
|
||||
/* Trailing slashes need to be trimmed. */
|
||||
if (!sep[1]) {
|
||||
const char *end;
|
||||
size_t end, sep_off;
|
||||
|
||||
for (end = sep; end != path; end--)
|
||||
if (*end != PATH_SEP)
|
||||
for (end = sep - path; end != 0; end--)
|
||||
if (path[end] != PATH_SEP)
|
||||
break;
|
||||
|
||||
/* Find *previous* / */
|
||||
for (sep = end; sep >= path && *sep != PATH_SEP; sep--);
|
||||
/* Find *previous* / ((size_t)-1 if there is none) */
|
||||
for (sep_off = end; ; sep_off--) {
|
||||
if (path[sep_off] == PATH_SEP)
|
||||
break;
|
||||
if (sep_off == 0) {
|
||||
sep_off = (size_t)-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* All /? Just return / */
|
||||
if (end == sep)
|
||||
if (end == sep_off)
|
||||
ret = tal_strdup(ctx, PATH_SEP_STR);
|
||||
else
|
||||
ret = tal_strndup(ctx, sep+1, end - sep);
|
||||
ret = tal_strndup(ctx, path + (sep_off + 1), end - sep_off);
|
||||
} else
|
||||
ret = tal_strdup(ctx, sep + 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ char *path_simplify(const tal_t *ctx, const char *a TAKES);
|
|||
*
|
||||
* If @a is an absolute path, return a copy of it. Otherwise, attach
|
||||
* @a to @base.
|
||||
*
|
||||
* @base and @a must not be NULL, except as the taken result of a
|
||||
* failed take() chain (e.g. path_join(ctx, take(tal_fmt(...)), "x")).
|
||||
*/
|
||||
char *path_join(const tal_t *ctx, const char *base TAKES, const char *a TAKES);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ int main(void)
|
|||
{
|
||||
char cwd[1024], *path, *ctx = tal_strdup(NULL, "ctx");
|
||||
|
||||
plan_tests(85);
|
||||
plan_tests(95);
|
||||
|
||||
if (!getcwd(cwd, sizeof(cwd)))
|
||||
abort();
|
||||
|
|
@ -227,6 +227,33 @@ int main(void)
|
|||
ok1(tal_parent(path) == ctx);
|
||||
tal_free(path);
|
||||
|
||||
/* Don't trace back over a symlink: keep ".." literally. */
|
||||
path = path_simplify(ctx, "run-simplify-link/..");
|
||||
ok1(streq(path, "run-simplify-link/.."));
|
||||
ok1(tal_parent(path) == ctx);
|
||||
tal_free(path);
|
||||
|
||||
path = path_simplify(ctx, "run-simplify-link/../");
|
||||
ok1(streq(path, "run-simplify-link/.."));
|
||||
ok1(tal_parent(path) == ctx);
|
||||
tal_free(path);
|
||||
|
||||
path = path_simplify(ctx, "run-simplify-link/../x");
|
||||
ok1(streq(path, "run-simplify-link/../x"));
|
||||
ok1(tal_parent(path) == ctx);
|
||||
tal_free(path);
|
||||
|
||||
path = path_simplify(ctx, "run-simplify-link/../../foo");
|
||||
ok1(streq(path, "run-simplify-link/../../foo"));
|
||||
ok1(tal_parent(path) == ctx);
|
||||
tal_free(path);
|
||||
|
||||
/* Nonexistent path: can't prove it's a real dir, keep "..". */
|
||||
path = path_simplify(ctx, "run-simplify-nosuch/..");
|
||||
ok1(streq(path, "run-simplify-nosuch/.."));
|
||||
ok1(tal_parent(path) == ctx);
|
||||
tal_free(path);
|
||||
|
||||
/* take tests */
|
||||
path = path_simplify(ctx, take(tal_strdup(ctx, "/tmp/../tmp/.")));
|
||||
ok1(streq(path, "/tmp"));
|
||||
|
|
|
|||
|
|
@ -150,8 +150,11 @@ char **tal_strsplit_(const tal_t *ctx,
|
|||
if (flags == STR_EMPTY_OK && dlen)
|
||||
dlen = 1;
|
||||
str += len + dlen;
|
||||
if (++num == max && !tal_resize(&parts, max*=2 + 1))
|
||||
goto fail;
|
||||
if (++num == max) {
|
||||
max = max * 2 + 1;
|
||||
if (!tal_resize(&parts, max))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
parts[num] = NULL;
|
||||
|
||||
|
|
@ -214,33 +217,9 @@ fail:
|
|||
goto out;
|
||||
}
|
||||
|
||||
static size_t count_open_braces(const char *string)
|
||||
{
|
||||
#if 1
|
||||
size_t num = 0, esc = 0;
|
||||
|
||||
while (*string) {
|
||||
if (*string == '\\')
|
||||
esc++;
|
||||
else {
|
||||
/* An odd number of \ means it's escaped. */
|
||||
if (*string == '(' && (esc & 1) == 0)
|
||||
num++;
|
||||
esc = 0;
|
||||
}
|
||||
string++;
|
||||
}
|
||||
return num;
|
||||
#else
|
||||
return strcount(string, "(");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool tal_strreg_(const tal_t *ctx, const char *string, const char *label,
|
||||
const char *regex, ...)
|
||||
{
|
||||
size_t nmatch = 1 + count_open_braces(regex);
|
||||
regmatch_t matches[nmatch];
|
||||
regex_t r;
|
||||
bool ret = false;
|
||||
unsigned int i;
|
||||
|
|
@ -249,33 +228,39 @@ bool tal_strreg_(const tal_t *ctx, const char *string, const char *label,
|
|||
if (regcomp(&r, regex, REG_EXTENDED) != 0)
|
||||
goto fail_no_re;
|
||||
|
||||
if (regexec(&r, string, nmatch, matches, 0) != 0)
|
||||
goto fail;
|
||||
{
|
||||
/* re_nsub counts real capture groups: unlike scanning the
|
||||
* regex text, it is not fooled by '(' inside bracket
|
||||
* expressions. */
|
||||
size_t nmatch = 1 + r.re_nsub;
|
||||
regmatch_t matches[nmatch];
|
||||
|
||||
ret = true;
|
||||
va_start(ap, regex);
|
||||
for (i = 1; i < nmatch; i++) {
|
||||
char **arg = va_arg(ap, char **);
|
||||
if (arg) {
|
||||
/* eg. ([a-z])? can give "no match". */
|
||||
if (matches[i].rm_so == -1)
|
||||
*arg = NULL;
|
||||
else {
|
||||
*arg = tal_strndup_(ctx,
|
||||
string + matches[i].rm_so,
|
||||
matches[i].rm_eo
|
||||
- matches[i].rm_so,
|
||||
label);
|
||||
/* FIXME: If we fail, we set some and leak! */
|
||||
if (!*arg) {
|
||||
ret = false;
|
||||
break;
|
||||
if (regexec(&r, string, nmatch, matches, 0) == 0) {
|
||||
ret = true;
|
||||
va_start(ap, regex);
|
||||
for (i = 1; i < nmatch; i++) {
|
||||
char **arg = va_arg(ap, char **);
|
||||
if (arg) {
|
||||
/* eg. ([a-z])? can give "no match". */
|
||||
if (matches[i].rm_so == -1)
|
||||
*arg = NULL;
|
||||
else {
|
||||
*arg = tal_strndup_(ctx,
|
||||
string + matches[i].rm_so,
|
||||
matches[i].rm_eo
|
||||
- matches[i].rm_so,
|
||||
label);
|
||||
/* FIXME: If we fail, we set some and leak! */
|
||||
if (!*arg) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
fail:
|
||||
regfree(&r);
|
||||
fail_no_re:
|
||||
if (taken(regex))
|
||||
|
|
|
|||
|
|
@ -244,6 +244,9 @@ static void notify(const struct tal_hdr *ctx,
|
|||
EXTRA_ARG(n));
|
||||
else
|
||||
cb.destroy(from_tal_hdr(ctx));
|
||||
/* Restore: object may have been rescued by a
|
||||
* tal_steal() from inside the destructor. */
|
||||
n->u = cb;
|
||||
} else
|
||||
n->u.notifyfn(from_tal_hdr_or_null(ctx), type,
|
||||
(void *)info);
|
||||
|
|
@ -420,22 +423,26 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *child)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
|
||||
static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno);
|
||||
|
||||
/* Free t's children, properties and t itself. The destroying bit is
|
||||
* already set (by del_tree() or tal_free()). */
|
||||
static void del_tree_inner(struct tal_hdr *t, const tal_t *orig,
|
||||
int saved_errno)
|
||||
{
|
||||
struct prop_hdr *prop;
|
||||
char *ptr, *next;
|
||||
|
||||
assert(!taken(from_tal_hdr(t)));
|
||||
|
||||
/* Already being destroyed? Don't loop. */
|
||||
if (unlikely(get_destroying_bit(t->parent_child)))
|
||||
return;
|
||||
|
||||
set_destroying_bit(&t->parent_child);
|
||||
|
||||
/* Call free notifiers. */
|
||||
notify(t, TAL_NOTIFY_FREE, (tal_t *)orig, saved_errno);
|
||||
|
||||
/* A destructor/notifier can rescue the object by tal_steal()ing
|
||||
* it elsewhere: add_child() clears the destroying bit. */
|
||||
if (!get_destroying_bit(t->parent_child))
|
||||
return;
|
||||
|
||||
/* Now free children and groups. */
|
||||
prop = find_property(t, CHILDREN);
|
||||
if (prop) {
|
||||
|
|
@ -456,6 +463,16 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
|
|||
freefn(t);
|
||||
}
|
||||
|
||||
static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
|
||||
{
|
||||
/* Already being destroyed? Don't loop. */
|
||||
if (unlikely(get_destroying_bit(t->parent_child)))
|
||||
return;
|
||||
|
||||
set_destroying_bit(&t->parent_child);
|
||||
del_tree_inner(t, orig, saved_errno);
|
||||
}
|
||||
|
||||
/* Don't have compiler complain we're returning NULL if we promised not to! */
|
||||
static void *null_alloc_failed(void)
|
||||
{
|
||||
|
|
@ -525,11 +542,21 @@ void *tal_free(const tal_t *ctx)
|
|||
t = debug_tal(to_tal_hdr(ctx));
|
||||
if (unlikely(get_destroying_bit(t->parent_child)))
|
||||
return NULL;
|
||||
/* Unlink and mark destroying before notifying the parent:
|
||||
* a notifier which (recursively) calls tal_free() on ctx
|
||||
* is then a no-op rather than unbounded recursion. */
|
||||
list_del(&t->list);
|
||||
set_destroying_bit(&t->parent_child);
|
||||
if (notifiers)
|
||||
notify(ignore_destroying_bit(t->parent_child)->parent,
|
||||
TAL_NOTIFY_DEL_CHILD, ctx, saved_errno);
|
||||
list_del(&t->list);
|
||||
del_tree(t, ctx, saved_errno);
|
||||
/* A notifier can rescue ctx by tal_steal()ing it elsewhere:
|
||||
* add_child() clears the destroying bit. */
|
||||
if (!get_destroying_bit(t->parent_child)) {
|
||||
errno = saved_errno;
|
||||
return NULL;
|
||||
}
|
||||
del_tree_inner(t, ctx, saved_errno);
|
||||
errno = saved_errno;
|
||||
}
|
||||
return NULL;
|
||||
|
|
@ -543,11 +570,28 @@ void *tal_steal_(const tal_t *new_parent, const tal_t *ctx)
|
|||
newpar = debug_tal(to_tal_hdr_or_null(new_parent));
|
||||
t = debug_tal(to_tal_hdr(ctx));
|
||||
|
||||
/* Unlink it from old parent. */
|
||||
list_del(&t->list);
|
||||
/* Can't steal into a parent which is being destroyed:
|
||||
* we'd be linked into the list del_tree() is draining,
|
||||
* and freed (or looped on) anyway. */
|
||||
if (unlikely(get_destroying_bit(newpar->parent_child)))
|
||||
return NULL;
|
||||
|
||||
/* Unlink it from old parent (an object being destroyed
|
||||
* has already been unlinked: it can only be stolen as a
|
||||
* rescue from its destructor/notifier). */
|
||||
if (!get_destroying_bit(t->parent_child))
|
||||
list_del(&t->list);
|
||||
old_parent = ignore_destroying_bit(t->parent_child)->parent;
|
||||
|
||||
if (unlikely(!add_child(newpar, t))) {
|
||||
/* No fallback for a rescue from a destructor:
|
||||
* re-linking into the old parent would silently
|
||||
* keep the object (its destructor saw NULL), and if
|
||||
* the old parent is mid-del_tree it would re-enter
|
||||
* the list being drained. Leave it unlinked and
|
||||
* destroying; the free proceeds. */
|
||||
if (get_destroying_bit(t->parent_child))
|
||||
return NULL;
|
||||
/* We can always add to old parent, because it has a
|
||||
* children property already. */
|
||||
if (!add_child(old_parent, t))
|
||||
|
|
@ -800,6 +844,12 @@ bool tal_expand_(tal_t **ctxp, const void *src, size_t size, size_t count)
|
|||
|
||||
old_len = debug_tal(to_tal_hdr(*ctxp))->bytelen;
|
||||
|
||||
/* Check for multiplicative overflow */
|
||||
if (size && unlikely(count * size / size != count)) {
|
||||
call_error("dup size overflow");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check for additive overflow */
|
||||
if (old_len + count * size < old_len) {
|
||||
call_error("dup size overflow");
|
||||
|
|
@ -810,7 +860,9 @@ bool tal_expand_(tal_t **ctxp, const void *src, size_t size, size_t count)
|
|||
assert(src < *ctxp
|
||||
|| (char *)src >= (char *)(*ctxp) + old_len);
|
||||
|
||||
if (!tal_resize_(ctxp, size, old_len/size + count, false))
|
||||
/* Resize by raw length, so excess bytes are preserved and
|
||||
* tal_count() grows by exactly count. */
|
||||
if (!tal_resize_(ctxp, 1, old_len + count * size, false))
|
||||
goto out;
|
||||
|
||||
memcpy((char *)*ctxp + old_len, src, count * size);
|
||||
|
|
|
|||
|
|
@ -143,7 +143,16 @@ void *tal_free(const tal_t *p);
|
|||
*
|
||||
* This may need to perform an allocation, in which case it may fail; thus
|
||||
* it can return NULL, otherwise returns @ptr. If @ptr is NULL, this function does
|
||||
* nothing.
|
||||
* nothing. It also fails (returning NULL) if @ctx is currently being
|
||||
* destroyed.
|
||||
*
|
||||
* A destructor or TAL_NOTIFY_FREE notifier may use this to rescue @ptr
|
||||
* from destruction by moving it to a new parent; the free is then
|
||||
* aborted. The rescue is only noticed once every notifier and
|
||||
* destructor registered for this pass has run, so later ones still
|
||||
* fire (and all of them fire again when the object is finally
|
||||
* destroyed). If the rescue fails (allocation failure), the free
|
||||
* proceeds.
|
||||
*/
|
||||
#if HAVE_STATEMENT_EXPR
|
||||
/* Weird macro avoids gcc's 'warning: value computed is not used'. */
|
||||
|
|
@ -174,7 +183,8 @@ void *tal_free(const tal_t *p);
|
|||
*
|
||||
* If @function has not been successfully added as a destructor, this returns
|
||||
* false. Note that if we're inside the destructor call itself, this will
|
||||
* return false.
|
||||
* return false, and the destructor remains registered: it is restored when
|
||||
* the call returns, in case the object was rescued from destruction.
|
||||
*/
|
||||
#define tal_del_destructor(ptr, function) \
|
||||
tal_del_destructor_((ptr), typesafe_cb(void, void *, (function), (ptr)))
|
||||
|
|
@ -206,7 +216,8 @@ void *tal_free(const tal_t *p);
|
|||
*
|
||||
* If @function has not been successfully added as a destructor, this returns
|
||||
* false. Note that if we're inside the destructor call itself, this will
|
||||
* return false.
|
||||
* return false, and the destructor remains registered: it is restored when
|
||||
* the call returns, in case the object was rescued from destruction.
|
||||
*/
|
||||
#define tal_del_destructor(ptr, function) \
|
||||
tal_del_destructor_((ptr), typesafe_cb(void, void *, (function), (ptr)))
|
||||
|
|
@ -272,6 +283,17 @@ enum tal_notify_type {
|
|||
* not called when this context is tal_free()d: TAL_NOTIFY_FREE is
|
||||
* considered sufficient for that case.
|
||||
*
|
||||
* For TAL_NOTIFY_ADD_CHILD, the callback must not tal_free() or
|
||||
* tal_steal() the child: the allocating call will still return it to
|
||||
* the caller, so this will crash or corrupt.
|
||||
*
|
||||
* For TAL_NOTIFY_DEL_CHILD, the child is already unlinked and marked
|
||||
* destroying: calling tal_free() on it from the callback is a no-op,
|
||||
* and tal_steal()ing it rescues it from destruction (aborting the
|
||||
* free).
|
||||
*
|
||||
* In all cases, the callback must not tal_free() @ptr itself.
|
||||
*
|
||||
* TAL_NOTIFY_ADD_NOTIFIER/TAL_NOTIFIER_DEL_NOTIFIER are called when a
|
||||
* notifier is added or removed (not for this notifier): @info is the
|
||||
* callback. This is also called for tal_add_destructor and
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@
|
|||
*
|
||||
* It evaluates to @x so you can chain it.
|
||||
*
|
||||
* Note that a @expr of type void * silently passes against any
|
||||
* canary (the comparison is legal C); the same applies to
|
||||
* tcon_check_ptr().
|
||||
*
|
||||
* Example:
|
||||
* #define tlist_add(h, n, member) \
|
||||
* list_add(&tcon_check((h), canary, (n))->raw, &(n)->member)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,17 @@
|
|||
#include <ccan/time/time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* Largest representable tv_sec (time_t is signed). */
|
||||
#define TIMEREL_SEC_MAX \
|
||||
((time_t)((~(uint64_t)0) >> (64 - sizeof(time_t)*CHAR_BIT + 1)))
|
||||
|
||||
static struct timerel timerel_max(void)
|
||||
{
|
||||
struct timerel max = { { TIMEREL_SEC_MAX, 999999999 } };
|
||||
return max;
|
||||
}
|
||||
|
||||
#if !HAVE_CLOCK_GETTIME
|
||||
#include <sys/time.h>
|
||||
|
|
@ -58,6 +69,11 @@ struct timerel time_divide(struct timerel t, unsigned long div)
|
|||
/* FIXME: fp is cheating! */
|
||||
double nsec = rem * 1000000000.0 + t.ts.tv_nsec;
|
||||
res.ts.tv_nsec = nsec / div;
|
||||
/* Rounding can give exactly 1e9; renormalize. */
|
||||
if (res.ts.tv_nsec >= 1000000000) {
|
||||
res.ts.tv_nsec -= 1000000000;
|
||||
res.ts.tv_sec++;
|
||||
}
|
||||
} else {
|
||||
ns = rem * 1000000000 + t.ts.tv_nsec;
|
||||
res.ts.tv_nsec = ns / div;
|
||||
|
|
@ -69,11 +85,16 @@ struct timerel time_multiply(struct timerel t, unsigned long mult)
|
|||
{
|
||||
struct timerel res;
|
||||
|
||||
(void)TIMEREL_CHECK(t);
|
||||
|
||||
/* Are we going to overflow if we multiply nsec? */
|
||||
if (mult & ~((1UL << 30) - 1)) {
|
||||
/* FIXME: fp is cheating! */
|
||||
double nsec = (double)t.ts.tv_nsec * mult;
|
||||
|
||||
/* Saturate rather than overflow time_t. */
|
||||
if (nsec >= (double)TIMEREL_SEC_MAX * 1000000000.0)
|
||||
return timerel_max();
|
||||
res.ts.tv_sec = nsec / 1000000000.0;
|
||||
res.ts.tv_nsec = nsec - (res.ts.tv_sec * 1000000000.0);
|
||||
} else {
|
||||
|
|
@ -82,7 +103,12 @@ struct timerel time_multiply(struct timerel t, unsigned long mult)
|
|||
res.ts.tv_nsec = nsec % 1000000000;
|
||||
res.ts.tv_sec = nsec / 1000000000;
|
||||
}
|
||||
res.ts.tv_sec += TIMEREL_CHECK(t).ts.tv_sec * mult;
|
||||
|
||||
/* The seconds multiply can overflow too: saturate. */
|
||||
if (mult != 0
|
||||
&& t.ts.tv_sec > (time_t)((TIMEREL_SEC_MAX - res.ts.tv_sec) / mult))
|
||||
return timerel_max();
|
||||
res.ts.tv_sec += t.ts.tv_sec * mult;
|
||||
return TIMEREL_CHECK(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -569,6 +569,9 @@ struct timerel time_divide(struct timerel t, unsigned long div);
|
|||
* @t: a relative time.
|
||||
* @mult: number to multiply it by.
|
||||
*
|
||||
* If the result would not fit in a timerel, the maximum representable
|
||||
* time is returned.
|
||||
*
|
||||
* Example:
|
||||
* ...
|
||||
* printf("Time to do 100000 forks would be %u sec\n",
|
||||
|
|
|
|||
|
|
@ -153,6 +153,15 @@ static void timers_far_get(struct timers *timers,
|
|||
}
|
||||
}
|
||||
|
||||
/* Maximum time covered by levels 0..@level (from base); saturates
|
||||
* once the range exceeds the representable 64 bits. */
|
||||
static uint64_t level_max_time(const struct timers *timers, unsigned int level)
|
||||
{
|
||||
if ((level + 1) * TIMER_LEVEL_BITS >= 64)
|
||||
return -1ULL;
|
||||
return timers->base + (1ULL << ((level+1)*TIMER_LEVEL_BITS)) - 1;
|
||||
}
|
||||
|
||||
static void add_level(struct timers *timers, unsigned int level)
|
||||
{
|
||||
struct timer_level *l;
|
||||
|
|
@ -169,8 +178,7 @@ static void add_level(struct timers *timers, unsigned int level)
|
|||
timers->level[level] = l;
|
||||
|
||||
list_head_init(&from_far);
|
||||
timers_far_get(timers, &from_far,
|
||||
timers->base + (1ULL << ((level+1)*TIMER_LEVEL_BITS)) - 1);
|
||||
timers_far_get(timers, &from_far, level_max_time(timers, level));
|
||||
|
||||
while ((t = list_pop(&from_far, struct timer, list)) != NULL)
|
||||
timer_add_raw(timers, t);
|
||||
|
|
@ -313,8 +321,7 @@ static void timer_fast_forward(struct timers *timers, uint64_t time)
|
|||
if (!timers->level[level]) {
|
||||
/* We need any which belong on this level. */
|
||||
timers_far_get(timers, &list,
|
||||
timers->base
|
||||
+ (1ULL << ((level+1)*TIMER_LEVEL_BITS))-1);
|
||||
level_max_time(timers, level));
|
||||
need_level = level;
|
||||
} else {
|
||||
unsigned src;
|
||||
|
|
@ -353,6 +360,9 @@ struct timer *timers_expire(struct timers *timers, struct timemono expire)
|
|||
if (list_empty(&timers->far))
|
||||
return NULL;
|
||||
add_level(timers, 0);
|
||||
/* Allocation failure: timers wait safely on the far list. */
|
||||
if (!timers->level[0])
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do {
|
||||
|
|
@ -448,8 +458,13 @@ struct timers *timers_check(const struct timers *timers, const char *abortstr)
|
|||
}
|
||||
|
||||
past_levels:
|
||||
base = (timers->base & ~((1ULL << (TIMER_LEVEL_BITS * l)) - 1))
|
||||
+ (1ULL << (TIMER_LEVEL_BITS * l)) - 1;
|
||||
if (TIMER_LEVEL_BITS * l < 64) {
|
||||
base = (timers->base & ~((1ULL << (TIMER_LEVEL_BITS * l)) - 1))
|
||||
+ (1ULL << (TIMER_LEVEL_BITS * l)) - 1;
|
||||
} else {
|
||||
/* Levels cover all representable times: far must be empty. */
|
||||
base = -1ULL;
|
||||
}
|
||||
if (!timer_list_check(&timers->far, base, -1ULL,
|
||||
timers->firsts[ARRAY_SIZE(timers->level)],
|
||||
abortstr))
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ void _callback(void (*fn)(void *arg), void *arg)
|
|||
/* Callback is set up to warn if arg isn't a pointer (since it won't
|
||||
* pass cleanly to _callback's second arg. */
|
||||
#define callback(fn, arg) \
|
||||
_callback(typesafe_cb(void, (fn), (arg)), (arg))
|
||||
_callback(typesafe_cb(void, void *, (fn), (arg)), (arg))
|
||||
|
||||
void my_callback(int something);
|
||||
void my_callback(int something)
|
||||
|
|
@ -23,6 +23,9 @@ int main(void)
|
|||
#ifdef FAIL
|
||||
/* This fails due to arg, not due to cast. */
|
||||
callback(my_callback, 100);
|
||||
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
|
||||
#error "Unfortunately we don't fail if typesafe_cb is a noop."
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,11 @@
|
|||
* It is assumed that @arg is of pointer type: usually @arg is passed
|
||||
* or assigned to a void * elsewhere anyway.
|
||||
*
|
||||
* Note that the callback must have a prototype: an unprototyped
|
||||
* function (ie. "void fn()") defeats this check entirely, and is
|
||||
* silently accepted with any @arg type (gcc and clang give no
|
||||
* diagnostic at -Wall).
|
||||
*
|
||||
* Example:
|
||||
* void _register_callback(void (*fn)(void *arg), void *arg);
|
||||
* #define register_callback(fn, arg) \
|
||||
|
|
|
|||
|
|
@ -396,6 +396,9 @@ static const struct test base_tests[] = {
|
|||
{ "HAVE_STATEMENT_EXPR", "statement expression support",
|
||||
"INSIDE_MAIN", NULL, NULL,
|
||||
"return ({ int x = argc; x == argc ? 0 : 1; });" },
|
||||
{ "HAVE_STATIC_ASSERT", "_Static_assert support",
|
||||
"INSIDE_MAIN", NULL, NULL,
|
||||
"_Static_assert(1, \"OK\"); return 0;" },
|
||||
{ "HAVE_SYS_FILIO_H", "<sys/filio.h>",
|
||||
"OUTSIDE_MAIN", NULL, NULL, /* Solaris needs this for FIONREAD */
|
||||
"#include <sys/filio.h>\n" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue