mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-10 05:02:06 +00:00
Fold LIBC_ALG into LIBC_MEM
This commit is contained in:
parent
7cf66bc161
commit
17aea99bb3
162 changed files with 265 additions and 430 deletions
50
libc/mem/alg.h
Normal file
50
libc/mem/alg.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ALG_ALG_H_
|
||||
#define COSMOPOLITAN_LIBC_ALG_ALG_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § algorithms ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
void *bsearch(const void *, const void *, size_t, size_t,
|
||||
int cmp(const void *, const void *))
|
||||
paramsnonnull() dontthrow nosideeffect;
|
||||
void *bsearch_r(const void *, const void *, size_t, size_t,
|
||||
int cmp(const void *, const void *, void *), void *)
|
||||
paramsnonnull((1, 2, 5)) dontthrow nosideeffect;
|
||||
void djbsort(int32_t *, size_t);
|
||||
void qsort(void *, size_t, size_t, int (*)(const void *, const void *))
|
||||
paramsnonnull();
|
||||
void qsort_r(void *, size_t, size_t,
|
||||
int cmp(const void *, const void *, void *), void *arg)
|
||||
paramsnonnull((1, 4));
|
||||
int tarjan(int, const int (*)[2], int, int[], int[], int *)
|
||||
paramsnonnull((2, 4)) nocallback dontthrow;
|
||||
|
||||
#define __algalloc returnspointerwithnoaliases dontthrow nocallback dontdiscard
|
||||
|
||||
char *replacestr(const char *, const char *, const char *)
|
||||
paramsnonnull() __algalloc;
|
||||
char16_t *replacestr16(const char16_t *, const char16_t *, const char16_t *)
|
||||
paramsnonnull() __algalloc;
|
||||
wchar_t *replacewcs(const wchar_t *, const wchar_t *, const wchar_t *)
|
||||
paramsnonnull() __algalloc;
|
||||
|
||||
char *concatstr(const char *, ...) nullterminated() paramsnonnull() __algalloc;
|
||||
char16_t *concatstr16(const char16_t *, ...) nullterminated()
|
||||
paramsnonnull() __algalloc;
|
||||
wchar_t *concatwcs(const wchar_t *, ...) nullterminated()
|
||||
paramsnonnull() __algalloc;
|
||||
|
||||
int cmpsb(const void *, const void *);
|
||||
int cmpub(const void *, const void *);
|
||||
int cmpsw(const void *, const void *);
|
||||
int cmpuw(const void *, const void *);
|
||||
int cmpsl(const void *, const void *);
|
||||
int cmpul(const void *, const void *);
|
||||
int cmpsq(const void *, const void *);
|
||||
int cmpuq(const void *, const void *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ALG_ALG_H_ */
|
31
libc/mem/arraylist.internal.h
Normal file
31
libc/mem/arraylist.internal.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ALG_ARRAYLIST_H_
|
||||
#define COSMOPOLITAN_LIBC_ALG_ARRAYLIST_H_
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
/* TODO(jart): DELETE */
|
||||
|
||||
#define append(ARRAYLIST, ITEM) concat((ARRAYLIST), (ITEM), 1)
|
||||
|
||||
#ifndef concat
|
||||
#define concat(ARRAYLIST, ITEM, COUNT) \
|
||||
({ \
|
||||
autotype(ARRAYLIST) List = (ARRAYLIST); \
|
||||
autotype(&List->p[0]) Item = (ITEM); \
|
||||
size_t SizE = sizeof(*Item); \
|
||||
size_t Count = (COUNT); \
|
||||
size_t Idx = List->i; \
|
||||
if (Idx + Count < List->n || __grow(&List->p, &List->n, SizE, Count)) { \
|
||||
memcpy(&List->p[Idx], Item, SizE *Count); \
|
||||
List->i = Idx + Count; \
|
||||
} else { \
|
||||
Idx = -1UL; \
|
||||
} \
|
||||
(ssize_t)(Idx); \
|
||||
})
|
||||
#endif /* concat */
|
||||
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ALG_ARRAYLIST_H_ */
|
39
libc/mem/arraylist2.internal.h
Normal file
39
libc/mem/arraylist2.internal.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ALG_ARRAYLIST2_H_
|
||||
#define COSMOPOLITAN_LIBC_ALG_ARRAYLIST2_H_
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/* TODO(jart): Fully develop these better macros. */
|
||||
|
||||
#define APPEND(LIST_P, LIST_I, LIST_N, ITEM) \
|
||||
CONCAT(LIST_P, LIST_I, LIST_N, ITEM, 1)
|
||||
|
||||
#ifndef CONCAT
|
||||
#define CONCAT(LIST_P, LIST_I, LIST_N, ITEM, COUNT) \
|
||||
({ \
|
||||
autotype(LIST_P) ListP = (LIST_P); \
|
||||
autotype(LIST_I) ListI = (LIST_I); \
|
||||
autotype(LIST_N) ListN = (LIST_N); \
|
||||
typeof(&(*ListP)[0]) Item = (ITEM); \
|
||||
size_t SizE = sizeof(*Item); \
|
||||
size_t Count = (COUNT); \
|
||||
ssize_t Entry = -1; \
|
||||
/* NOTE: We use `<` to guarantee one additional slot */ \
|
||||
/* grow() will memset(0) extended memory, thus */ \
|
||||
/* you get a nul-terminator for free sometimes */ \
|
||||
/* the exception is if you list.i=0 and re-use */ \
|
||||
/* so you need concat(...); list.p[list.i++]=0 */ \
|
||||
if (*ListI + Count < *ListN || __grow(ListP, ListN, SizE, Count)) { \
|
||||
memcpy(&(*ListP)[*ListI], Item, (SizE) * (Count)); \
|
||||
Entry = *ListI; \
|
||||
*ListI += Count; /* happens after copy in case signal */ \
|
||||
} \
|
||||
Entry; \
|
||||
})
|
||||
#endif /* CONCAT */
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ALG_ARRAYLIST2_H_ */
|
33
libc/mem/bisect.internal.h
Normal file
33
libc/mem/bisect.internal.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ALG_BISECT_H_
|
||||
#define COSMOPOLITAN_LIBC_ALG_BISECT_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
forceinline void *bisect(const void *k, const void *data, size_t n, size_t size,
|
||||
int cmp(const void *a, const void *b, void *arg),
|
||||
void *arg) {
|
||||
int c;
|
||||
const char *p;
|
||||
ssize_t m, l, r;
|
||||
if (n) {
|
||||
l = 0;
|
||||
r = n - 1;
|
||||
p = data;
|
||||
while (l <= r) {
|
||||
m = (l & r) + ((l ^ r) >> 1);
|
||||
c = cmp(k, p + m * size, arg);
|
||||
if (c > 0) {
|
||||
l = m + 1;
|
||||
} else if (c < 0) {
|
||||
r = m - 1;
|
||||
} else {
|
||||
return (char *)p + m * size;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ALG_BISECT_H_ */
|
26
libc/mem/bisectcarleft.internal.h
Normal file
26
libc/mem/bisectcarleft.internal.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ALG_BISECTCARLEFT_H_
|
||||
#define COSMOPOLITAN_LIBC_ALG_BISECTCARLEFT_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/* TODO: DELETE */
|
||||
|
||||
forceinline int32_t bisectcarleft(const int32_t (*cons)[2], size_t count,
|
||||
const int32_t key) {
|
||||
size_t left = 0;
|
||||
size_t right = count;
|
||||
while (left < right) {
|
||||
size_t m = (left + right) >> 1;
|
||||
if (cons[m][0] < key) {
|
||||
left = m + 1;
|
||||
} else {
|
||||
right = m;
|
||||
}
|
||||
}
|
||||
if (left && (left == count || cons[left][0] > key)) left--;
|
||||
return left;
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ALG_BISECTCARLEFT_H_ */
|
29
libc/mem/bsearch.c
Normal file
29
libc/mem/bsearch.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/mem/bisect.internal.h"
|
||||
|
||||
/**
|
||||
* Searches sorted array for exact item in logarithmic time.
|
||||
* @see bsearch_r(), bisectcarleft()
|
||||
*/
|
||||
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int cmp(const void *a, const void *b)) {
|
||||
return bisect(key, base, nmemb, size, (void *)cmp, NULL);
|
||||
}
|
29
libc/mem/bsearch_r.c
Normal file
29
libc/mem/bsearch_r.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/mem/bisect.internal.h"
|
||||
|
||||
/**
|
||||
* Searches sorted array for exact item in logarithmic time.
|
||||
* @see bsearch(), bisectcarleft()
|
||||
*/
|
||||
void *bsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int cmp(const void *a, const void *b, void *arg), void *arg) {
|
||||
return bisect(key, base, nmemb, size, cmp, arg);
|
||||
}
|
27
libc/mem/critbit0.h
Normal file
27
libc/mem/critbit0.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ALG_CRITBIT0_H_
|
||||
#define COSMOPOLITAN_LIBC_ALG_CRITBIT0_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § data structures » critical bit tree (for c strings) ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
struct critbit0 {
|
||||
void *root;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
bool critbit0_contains(struct critbit0 *, const char *) dontthrow nosideeffect
|
||||
paramsnonnull();
|
||||
int critbit0_insert(struct critbit0 *, const char *) paramsnonnull();
|
||||
bool critbit0_delete(struct critbit0 *, const char *) dontthrow paramsnonnull();
|
||||
void critbit0_clear(struct critbit0 *) dontthrow paramsnonnull();
|
||||
char *critbit0_get(struct critbit0 *, const char *);
|
||||
intptr_t critbit0_allprefixed(struct critbit0 *, const char *,
|
||||
intptr_t (*)(const char *, void *), void *)
|
||||
paramsnonnull((1, 2, 3)) dontthrow;
|
||||
int critbit0_emplace(struct critbit0 *, char *, size_t) paramsnonnull();
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ALG_CRITBIT0_H_ */
|
66
libc/mem/critbit0_allprefixed.c
Normal file
66
libc/mem/critbit0_allprefixed.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/critbit0.h"
|
||||
#include "libc/mem/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static intptr_t allprefixed_traverse(unsigned char *top,
|
||||
intptr_t (*callback)(const char *, void *),
|
||||
void *arg) {
|
||||
if (1 & (intptr_t)top) {
|
||||
struct CritbitNode *q = (void *)(top - 1);
|
||||
for (int direction = 0; direction < 2; ++direction) {
|
||||
intptr_t rc = allprefixed_traverse(q->child[direction], callback, arg);
|
||||
if (rc) return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return callback((const char *)top, arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes callback for all items with prefix.
|
||||
*
|
||||
* @return 0 unless iteration was halted by CALLBACK returning
|
||||
* nonzero, in which case that value is returned
|
||||
* @note h/t djb and agl
|
||||
*/
|
||||
intptr_t critbit0_allprefixed(struct critbit0 *t, const char *prefix,
|
||||
intptr_t (*callback)(const char *elem, void *arg),
|
||||
void *arg) {
|
||||
const unsigned char *ubytes = (void *)prefix;
|
||||
const size_t ulen = strlen(prefix);
|
||||
unsigned char *p = t->root;
|
||||
unsigned char *top = p;
|
||||
if (!p) return 0;
|
||||
while (1 & (intptr_t)p) {
|
||||
struct CritbitNode *q = (void *)(p - 1);
|
||||
unsigned char c = 0;
|
||||
if (q->byte < ulen) c = ubytes[q->byte];
|
||||
const int direction = (1 + (q->otherbits | c)) >> 8;
|
||||
p = q->child[direction];
|
||||
if (q->byte < ulen) top = p;
|
||||
}
|
||||
for (size_t i = 0; i < ulen; ++i) {
|
||||
if (p[i] != ubytes[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return allprefixed_traverse(top, callback, arg);
|
||||
}
|
46
libc/mem/critbit0_clear.c
Normal file
46
libc/mem/critbit0_clear.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/critbit0.h"
|
||||
#include "libc/mem/internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
static void critbit0_clear_traverse(void *top) {
|
||||
unsigned char *p = top;
|
||||
if (1 & (intptr_t)p) {
|
||||
struct CritbitNode *q = (void *)(p - 1);
|
||||
critbit0_clear_traverse(q->child[0]);
|
||||
critbit0_clear_traverse(q->child[1]);
|
||||
free(q), q = NULL;
|
||||
} else {
|
||||
free(p), p = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all items from 𝑡.
|
||||
* @param t tree
|
||||
* @note h/t djb and agl
|
||||
*/
|
||||
void critbit0_clear(struct critbit0 *t) {
|
||||
if (t->root) {
|
||||
critbit0_clear_traverse(t->root);
|
||||
t->root = NULL;
|
||||
}
|
||||
t->count = 0;
|
||||
}
|
42
libc/mem/critbit0_contains.c
Normal file
42
libc/mem/critbit0_contains.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/critbit0.h"
|
||||
#include "libc/mem/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns non-zero iff 𝑢 ∈ 𝑡.
|
||||
* @param t tree
|
||||
* @param u NUL-terminated string
|
||||
* @note h/t djb and agl
|
||||
*/
|
||||
bool critbit0_contains(struct critbit0 *t, const char *u) {
|
||||
const unsigned char *ubytes = (void *)u;
|
||||
const size_t ulen = strlen(u);
|
||||
unsigned char *p = t->root;
|
||||
if (!p) return 0;
|
||||
while (1 & (intptr_t)p) {
|
||||
struct CritbitNode *q = (void *)(p - 1);
|
||||
unsigned char c = 0;
|
||||
if (q->byte < ulen) c = ubytes[q->byte];
|
||||
const int direction = (1 + (q->otherbits | c)) >> 8;
|
||||
p = q->child[direction];
|
||||
}
|
||||
return 0 == strcmp(u, (const char *)p);
|
||||
}
|
60
libc/mem/critbit0_delete.c
Normal file
60
libc/mem/critbit0_delete.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/critbit0.h"
|
||||
#include "libc/mem/internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Removes 𝑢 from 𝑡.
|
||||
* @param t tree
|
||||
* @param u NUL-terminated string
|
||||
* @return true if 𝑡 was mutated
|
||||
* @note h/t djb and agl
|
||||
*/
|
||||
bool critbit0_delete(struct critbit0 *t, const char *u) {
|
||||
const unsigned char *ubytes = (void *)u;
|
||||
const size_t ulen = strlen(u);
|
||||
unsigned char *p = t->root;
|
||||
void **wherep = &t->root;
|
||||
void **whereq = 0;
|
||||
struct CritbitNode *q = 0;
|
||||
int direction = 0;
|
||||
if (!p) return false;
|
||||
while (1 & (intptr_t)p) {
|
||||
whereq = wherep;
|
||||
q = (void *)(p - 1);
|
||||
unsigned char c = 0;
|
||||
if (q->byte < ulen) c = ubytes[q->byte];
|
||||
direction = (1 + (q->otherbits | c)) >> 8;
|
||||
wherep = q->child + direction;
|
||||
p = *wherep;
|
||||
}
|
||||
if (0 != strcmp(u, (const char *)p)) return false;
|
||||
free(p), p = NULL;
|
||||
if (!whereq) {
|
||||
t->root = NULL;
|
||||
t->count = 0;
|
||||
return true;
|
||||
}
|
||||
*whereq = q->child[1 - direction];
|
||||
free(q), q = NULL;
|
||||
t->count--;
|
||||
return true;
|
||||
}
|
92
libc/mem/critbit0_emplace.c
Normal file
92
libc/mem/critbit0_emplace.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/critbit0.h"
|
||||
#include "libc/mem/internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Inserts 𝑢 into 𝑡 without copying.
|
||||
*
|
||||
* @param t is critical bit tree
|
||||
* @param u is nul-terminated string which must be 8+ byte aligned
|
||||
* and becomes owned by the tree afterwards
|
||||
* @return true if 𝑡 was mutated, or -1 w/ errno
|
||||
* @note h/t djb and agl
|
||||
*/
|
||||
int critbit0_emplace(struct critbit0 *t, char *u, size_t ulen) {
|
||||
unsigned char *p = t->root;
|
||||
if (!p) {
|
||||
t->root = u;
|
||||
t->count = 1;
|
||||
return 1;
|
||||
}
|
||||
const unsigned char *const ubytes = (void *)u;
|
||||
while (1 & (intptr_t)p) {
|
||||
struct CritbitNode *q = (void *)(p - 1);
|
||||
unsigned char c = 0;
|
||||
if (q->byte < ulen) c = ubytes[q->byte];
|
||||
const int direction = (1 + (q->otherbits | c)) >> 8;
|
||||
p = q->child[direction];
|
||||
}
|
||||
uint32_t newbyte;
|
||||
uint32_t newotherbits;
|
||||
for (newbyte = 0; newbyte < ulen; ++newbyte) {
|
||||
if (p[newbyte] != ubytes[newbyte]) {
|
||||
newotherbits = p[newbyte] ^ ubytes[newbyte];
|
||||
goto DifferentByteFound;
|
||||
}
|
||||
}
|
||||
if (p[newbyte] != 0) {
|
||||
newotherbits = p[newbyte];
|
||||
goto DifferentByteFound;
|
||||
}
|
||||
return 0;
|
||||
DifferentByteFound:
|
||||
newotherbits |= newotherbits >> 1;
|
||||
newotherbits |= newotherbits >> 2;
|
||||
newotherbits |= newotherbits >> 4;
|
||||
newotherbits = (newotherbits & ~(newotherbits >> 1)) ^ 255;
|
||||
unsigned char c = p[newbyte];
|
||||
int newdirection = (1 + (newotherbits | c)) >> 8;
|
||||
struct CritbitNode *newnode;
|
||||
if ((newnode = malloc(sizeof(struct CritbitNode)))) {
|
||||
newnode->byte = newbyte;
|
||||
newnode->otherbits = newotherbits;
|
||||
newnode->child[1 - newdirection] = (void *)ubytes;
|
||||
void **wherep = &t->root;
|
||||
for (;;) {
|
||||
unsigned char *wp = *wherep;
|
||||
if (!(1 & (intptr_t)wp)) break;
|
||||
struct CritbitNode *q = (void *)(wp - 1);
|
||||
if (q->byte > newbyte) break;
|
||||
if (q->byte == newbyte && q->otherbits > newotherbits) break;
|
||||
unsigned char c2 = 0;
|
||||
if (q->byte < ulen) c2 = ubytes[q->byte];
|
||||
const int direction = (1 + (q->otherbits | c2)) >> 8;
|
||||
wherep = q->child + direction;
|
||||
}
|
||||
newnode->child[newdirection] = *wherep;
|
||||
*wherep = (void *)(1 + (char *)newnode);
|
||||
t->count++;
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
43
libc/mem/critbit0_get.c
Normal file
43
libc/mem/critbit0_get.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/critbit0.h"
|
||||
#include "libc/mem/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns first item in 𝑡 with prefix 𝑢.
|
||||
* @param t tree
|
||||
* @param u NUL-terminated string
|
||||
* @return item or NULL if not found
|
||||
* @note h/t djb and agl
|
||||
*/
|
||||
char *critbit0_get(struct critbit0 *t, const char *u) {
|
||||
const unsigned char *ubytes = (void *)u;
|
||||
const size_t ulen = strlen(u);
|
||||
unsigned char *p = t->root;
|
||||
if (!p) return 0;
|
||||
while (1 & (intptr_t)p) {
|
||||
struct CritbitNode *q = (void *)(p - 1);
|
||||
unsigned char c = 0;
|
||||
if (q->byte < ulen) c = ubytes[q->byte];
|
||||
const int direction = (1 + (q->otherbits | c)) >> 8;
|
||||
p = q->child[direction];
|
||||
}
|
||||
return strncmp(u, (char *)p, ulen) == 0 ? (char *)p : NULL;
|
||||
}
|
39
libc/mem/critbit0_insert.c
Normal file
39
libc/mem/critbit0_insert.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/critbit0.h"
|
||||
#include "libc/mem/internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Inserts 𝑢 into 𝑡.
|
||||
* @param t tree
|
||||
* @param u NUL-terminated string
|
||||
* @return true if 𝑡 was mutated, or -1 w/ errno
|
||||
* @note h/t djb and agl
|
||||
*/
|
||||
int critbit0_insert(struct critbit0 *t, const char *u) {
|
||||
char *p;
|
||||
size_t n;
|
||||
if ((p = malloc((n = strlen(u)) + 1))) {
|
||||
return critbit0_emplace(t, memcpy(p, u, n + 1), n);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,12 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct CritbitNode {
|
||||
void *child[2];
|
||||
uint32_t byte;
|
||||
unsigned otherbits;
|
||||
};
|
||||
|
||||
int PutEnvImpl(char *, bool) hidden;
|
||||
void __freeenv(void *) hidden;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/alg/alg.h"
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
|
64
libc/mem/replacestr.c
Normal file
64
libc/mem/replacestr.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/mem/arraylist2.internal.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Replaces all instances of NEEDLE in S with REPLACEMENT.
|
||||
*
|
||||
* @param needle can't be empty
|
||||
* @return newly allocated memory that must be free()'d or NULL w/ errno
|
||||
* @error ENOMEM, EINVAL
|
||||
*/
|
||||
char *replacestr(const char *s, const char *needle, const char *replacement) {
|
||||
char *p1, *p2, *res_p;
|
||||
size_t left, nlen, rlen, res_i, res_n;
|
||||
if (*needle) {
|
||||
p1 = s;
|
||||
left = strlen(s);
|
||||
nlen = strlen(needle);
|
||||
rlen = strlen(replacement);
|
||||
res_i = 0;
|
||||
res_n = MAX(left, 32);
|
||||
if ((res_p = malloc(res_n * sizeof(char)))) {
|
||||
do {
|
||||
if (!(p2 = memmem(p1, left, needle, nlen))) break;
|
||||
if (CONCAT(&res_p, &res_i, &res_n, p1, p2 - p1) == -1 ||
|
||||
CONCAT(&res_p, &res_i, &res_n, replacement, rlen) == -1) {
|
||||
goto oom;
|
||||
}
|
||||
p2 += nlen;
|
||||
left -= p2 - p1;
|
||||
p1 = p2;
|
||||
} while (left);
|
||||
if (CONCAT(&res_p, &res_i, &res_n, p1, left + 1) != -1) {
|
||||
return res_p;
|
||||
}
|
||||
}
|
||||
oom:
|
||||
free(res_p);
|
||||
} else {
|
||||
einval();
|
||||
}
|
||||
return NULL;
|
||||
}
|
31
libc/mem/reverse.internal.h
Normal file
31
libc/mem/reverse.internal.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ALG_REVERSE_H_
|
||||
#define COSMOPOLITAN_LIBC_ALG_REVERSE_H_
|
||||
#include "libc/intrin/xchg.internal.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
/**
|
||||
* Reverses array.
|
||||
*
|
||||
* @param ARRAY is a typed array or a pointer to one
|
||||
* @param COUNT is the number of items
|
||||
* @return pointer to start of array
|
||||
* @see ARRAYLEN()
|
||||
*/
|
||||
#define reverse(ARRAY, COUNT) \
|
||||
({ \
|
||||
autotype(&(ARRAY)[0]) Array = (ARRAY); \
|
||||
size_t Count = (COUNT); \
|
||||
if (Count) { \
|
||||
size_t Start = 0; \
|
||||
size_t End = Count - 1; \
|
||||
while (Start < End) { \
|
||||
xchg(&Array[Start], &Array[End]); \
|
||||
++Start; \
|
||||
--End; \
|
||||
} \
|
||||
} \
|
||||
Array; \
|
||||
})
|
||||
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ALG_REVERSE_H_ */
|
23
libc/mem/shuffle.internal.h
Normal file
23
libc/mem/shuffle.internal.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_RAND_SHUFFLE_H_
|
||||
#define COSMOPOLITAN_LIBC_RAND_SHUFFLE_H_
|
||||
#include "libc/intrin/xchg.internal.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
/**
|
||||
* Fisher-Yates shuffle.
|
||||
*
|
||||
* @param R is a function like rand() → ≥0
|
||||
* @param A is a typed array
|
||||
* @param n is the number of items in A
|
||||
* @see ARRAYLEN()
|
||||
*/
|
||||
#define shuffle(R, A, n) \
|
||||
do { \
|
||||
autotype(A) Array = (A); \
|
||||
for (size_t i = (n)-1; i >= 1; --i) { \
|
||||
xchg(&Array[i], &Array[R() % (i + 1)]); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_RAND_SHUFFLE_H_ */
|
182
libc/mem/tarjan.c
Normal file
182
libc/mem/tarjan.c
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/alg.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Tarjan's Strongly Connected Components Algorithm.
|
||||
*
|
||||
* “The data structures that [Tarjan] devised for this problem fit
|
||||
* together in an amazingly beautiful way, so that the quantities
|
||||
* you need to look at while exploring a directed graph are always
|
||||
* magically at your fingertips. And his algorithm also does
|
||||
* topological sorting as a byproduct.” ──D.E. Knuth
|
||||
*/
|
||||
|
||||
struct Tarjan {
|
||||
int Vn, En, Ci, Ri, *R, *C, index;
|
||||
const int (*E)[2];
|
||||
struct Vertex {
|
||||
int Vi;
|
||||
int Ei;
|
||||
int index;
|
||||
int lowlink;
|
||||
bool onstack;
|
||||
bool selfreferential;
|
||||
} * V;
|
||||
struct TarjanStack {
|
||||
int i;
|
||||
int n;
|
||||
int *p;
|
||||
} S;
|
||||
};
|
||||
|
||||
static bool TarjanPush(struct Tarjan *t, int v) {
|
||||
int *q;
|
||||
assert(t->S.i >= 0);
|
||||
assert(t->S.n >= 0);
|
||||
assert(0 <= v && v < t->Vn);
|
||||
if (t->S.i == t->S.n) {
|
||||
if ((q = realloc(t->S.p, (t->S.n + (t->S.n >> 1) + 8) * sizeof(*t->S.p)))) {
|
||||
t->S.p = q;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
t->S.p[t->S.i++] = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int TarjanPop(struct Tarjan *t) {
|
||||
assert(t->S.i > 0);
|
||||
return t->S.p[--t->S.i];
|
||||
}
|
||||
|
||||
static bool TarjanConnect(struct Tarjan *t, int v) {
|
||||
int fs, w, e;
|
||||
assert(0 <= v && v < t->Vn);
|
||||
t->V[v].index = t->index;
|
||||
t->V[v].lowlink = t->index;
|
||||
t->V[v].onstack = true;
|
||||
t->index++;
|
||||
if (!TarjanPush(t, v)) return false;
|
||||
fs = t->V[v].Ei;
|
||||
if (fs != -1) {
|
||||
for (e = fs; e < t->En && v == t->E[e][0]; ++e) {
|
||||
w = t->E[e][1];
|
||||
if (!t->V[w].index) {
|
||||
if (!TarjanConnect(t, t->V[w].Vi)) return false;
|
||||
t->V[v].lowlink = MIN(t->V[v].lowlink, t->V[w].lowlink);
|
||||
} else if (t->V[w].onstack) {
|
||||
t->V[v].lowlink = MIN(t->V[v].lowlink, t->V[w].index);
|
||||
}
|
||||
if (w == v) {
|
||||
t->V[w].selfreferential = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t->V[v].lowlink == t->V[v].index) {
|
||||
do {
|
||||
w = TarjanPop(t);
|
||||
t->V[w].onstack = false;
|
||||
t->R[t->Ri++] = t->V[w].Vi;
|
||||
} while (w != v);
|
||||
if (t->C) t->C[t->Ci++] = t->Ri;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines order of things in network and finds tangled clusters too.
|
||||
*
|
||||
* @param vertices is an array of vertex values, which isn't passed to
|
||||
* this function, since the algorithm only needs to consider indices
|
||||
* @param vertex_count is the number of items in the vertices array
|
||||
* @param edges are grouped directed links between indices of vertices,
|
||||
* which can be thought of as "edge[i][0] depends on edge[i][1]" or
|
||||
* "edge[i][1] must come before edge[i][0]" in topological order
|
||||
* @param edge_count is the number of items in edges, which may be 0 if
|
||||
* there aren't any connections between vertices in the graph
|
||||
* @param out_sorted receives indices into the vertices array in
|
||||
* topologically sorted order, and must be able to store
|
||||
* vertex_count items, and that's always how many are stored
|
||||
* @param out_opt_components receives indices into the out_sorted array,
|
||||
* indicating where each strongly-connected component ends; must be
|
||||
* able to store vertex_count items; and it may be NULL
|
||||
* @param out_opt_componentcount receives the number of cycle indices
|
||||
* written to out_opt_components, which will be vertex_count if
|
||||
* there aren't any cycles in the graph; and may be NULL if
|
||||
* out_opt_components is NULL
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @error ENOMEM
|
||||
* @note Tarjan's Algorithm is O(|V|+|E|)
|
||||
*/
|
||||
int tarjan(int vertex_count, const int (*edges)[2], int edge_count,
|
||||
int out_sorted[], int out_opt_components[],
|
||||
int *out_opt_componentcount) {
|
||||
int i, rc, v, e;
|
||||
struct Tarjan *t;
|
||||
assert(0 <= edge_count && edge_count <= INT_MAX);
|
||||
assert(0 <= vertex_count && vertex_count <= INT_MAX);
|
||||
for (i = 0; i < edge_count; ++i) {
|
||||
if (i) assert(edges[i - 1][0] <= edges[i][0]);
|
||||
assert(edges[i][0] < vertex_count);
|
||||
assert(edges[i][1] < vertex_count);
|
||||
}
|
||||
if (!(t = calloc(1, (sizeof(struct Tarjan) +
|
||||
sizeof(struct Vertex) * vertex_count)))) {
|
||||
return -1;
|
||||
}
|
||||
t->V = (struct Vertex *)((char *)t + sizeof(struct Tarjan));
|
||||
t->Vn = vertex_count;
|
||||
t->E = edges;
|
||||
t->En = edge_count;
|
||||
t->R = out_sorted;
|
||||
t->C = out_opt_components;
|
||||
t->index = 1;
|
||||
for (v = 0; v < t->Vn; ++v) {
|
||||
t->V[v].Vi = v;
|
||||
t->V[v].Ei = -1;
|
||||
}
|
||||
for (e = 0, v = -1; e < t->En; ++e) {
|
||||
if (t->E[e][0] == v) continue;
|
||||
v = t->E[e][0];
|
||||
t->V[v].Ei = e;
|
||||
}
|
||||
rc = 0;
|
||||
for (v = 0; v < t->Vn; ++v) {
|
||||
if (!t->V[v].index) {
|
||||
if (!TarjanConnect(t, v)) {
|
||||
free(t->S.p);
|
||||
free(t);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out_opt_components) {
|
||||
*out_opt_componentcount = t->Ci;
|
||||
}
|
||||
assert(t->Ri == vertex_count);
|
||||
free(t->S.p);
|
||||
free(t);
|
||||
return rc;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue