Compare commits

...

4 Commits

Author SHA1 Message Date
Michael Theall
dca3e9919d Additional debugging 2019-02-03 00:16:05 -06:00
Michael Theall
6298be6420 Make test program work with new debugging checks 2019-02-03 00:16:04 -06:00
Michael Theall
888569bba1 Additional debugging 2019-02-03 00:16:04 -06:00
Michael Theall
431fd9aa9c Add rbtree validation 2019-02-03 00:16:04 -06:00
22 changed files with 624 additions and 40 deletions

2
.gitignore vendored
View File

@ -9,3 +9,5 @@ docs/
examples/ examples/
internal_docs internal_docs
build.sh build.sh
libctru/source/util/rbtree/test/*.o
libctru/source/util/rbtree/test/rbtree_test

View File

@ -4,6 +4,12 @@
*/ */
#pragma once #pragma once
#ifdef _3DS
#include <3ds/synchronization.h>
#else
#include <pthread.h>
#endif
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
@ -21,16 +27,23 @@ typedef int (*rbtree_node_comparator_t)(const rbtree_node_t *lhs,
/// An rbtree node. /// An rbtree node.
struct rbtree_node struct rbtree_node
{ {
uintptr_t parent_color; ///< Parent color. uint32_t prefix[32]; ///< Guard prefix.
rbtree_node_t *child[2]; ///< Node children. char nodeTag[4]; ///< Node tag.
uintptr_t parent_color; ///< Parent color.
rbtree_node_t *child[2]; ///< Node children.
rbtree_t *tree; ///< Owning tree.
uint32_t *suffix[32]; ///< Guard suffix.
}; };
/// An rbtree. /// An rbtree.
struct rbtree struct rbtree
{ {
char treeTag[4]; ///< Tree tag.
rbtree_node_t *root; ///< Root node. rbtree_node_t *root; ///< Root node.
rbtree_node_comparator_t comparator; ///< Node comparator. rbtree_node_comparator_t comparator; ///< Node comparator.
size_t size; ///< Size. size_t size; ///< Size.
LightLock lock; ///< Tree mutex.
bool busy; ///< Busy flag.
}; };
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -7,11 +7,13 @@ extern "C"
#include "mem_pool.h" #include "mem_pool.h"
#include "addrmap.h" #include "addrmap.h"
#include "lock.h"
extern u32 __ctru_linear_heap; extern u32 __ctru_linear_heap;
extern u32 __ctru_linear_heap_size; extern u32 __ctru_linear_heap_size;
static MemPool sLinearPool; static MemPool sLinearPool;
static LightLock sLock = 1;
static bool linearInit() static bool linearInit()
{ {
@ -42,6 +44,7 @@ void* linearMemAlign(size_t size, size_t alignment)
return nullptr; return nullptr;
// Initialize the pool if it is not ready // Initialize the pool if it is not ready
LockGuard guard(sLock);
if (!sLinearPool.Ready() && !linearInit()) if (!sLinearPool.Ready() && !linearInit())
return nullptr; return nullptr;
@ -56,7 +59,9 @@ void* linearMemAlign(size_t size, size_t alignment)
sLinearPool.Deallocate(chunk); sLinearPool.Deallocate(chunk);
return nullptr; return nullptr;
} }
if (rbtree_insert(&sAddrMap, &node->node)); if (rbtree_insert(&sAddrMap, &node->node) != &node->node)
svcBreak(USERBREAK_PANIC);
return chunk.addr; return chunk.addr;
} }
@ -73,12 +78,14 @@ void* linearRealloc(void* mem, size_t size)
size_t linearGetSize(void* mem) size_t linearGetSize(void* mem)
{ {
LockGuard guard(sLock);
auto node = getNode(mem); auto node = getNode(mem);
return node ? node->chunk.size : 0; return node ? node->chunk.size : 0;
} }
void linearFree(void* mem) void linearFree(void* mem)
{ {
LockGuard guard(sLock);
auto node = getNode(mem); auto node = getNode(mem);
if (!node) return; if (!node) return;
@ -91,5 +98,6 @@ void linearFree(void* mem)
u32 linearSpaceFree() u32 linearSpaceFree()
{ {
LockGuard guard(sLock);
return sLinearPool.GetFreeSpace(); return sLinearPool.GetFreeSpace();
} }

View File

@ -0,0 +1,23 @@
#pragma once
extern "C"
{
#include <3ds/synchronization.h>
}
class LockGuard
{
public:
~LockGuard()
{
LightLock_Unlock(&lock);
}
LockGuard(LightLock &lock) : lock(lock)
{
LightLock_Lock(&lock);
}
private:
LightLock &lock;
};

View File

@ -7,8 +7,10 @@ extern "C"
#include "mem_pool.h" #include "mem_pool.h"
#include "addrmap.h" #include "addrmap.h"
#include "lock.h"
static MemPool sMappablePool; static MemPool sMappablePool;
static LightLock sLock = 1;
static bool mappableInit() static bool mappableInit()
{ {
@ -25,6 +27,7 @@ static bool mappableInit()
void* mappableAlloc(size_t size) void* mappableAlloc(size_t size)
{ {
// Initialize the pool if it is not ready // Initialize the pool if it is not ready
LockGuard guard(sLock);
if (!sMappablePool.Ready() && !mappableInit()) if (!sMappablePool.Ready() && !mappableInit())
return nullptr; return nullptr;
@ -45,12 +48,14 @@ void* mappableAlloc(size_t size)
size_t mappableGetSize(void* mem) size_t mappableGetSize(void* mem)
{ {
LockGuard guard(sLock);
auto node = getNode(mem); auto node = getNode(mem);
return node ? node->chunk.size : 0; return node ? node->chunk.size : 0;
} }
void mappableFree(void* mem) void mappableFree(void* mem)
{ {
LockGuard guard(sLock);
auto node = getNode(mem); auto node = getNode(mem);
if (!node) return; if (!node) return;
@ -63,5 +68,6 @@ void mappableFree(void* mem)
u32 mappableSpaceFree() u32 mappableSpaceFree()
{ {
LockGuard guard(sLock);
return sMappablePool.GetFreeSpace(); return sMappablePool.GetFreeSpace();
} }

View File

@ -7,8 +7,10 @@ extern "C"
#include "mem_pool.h" #include "mem_pool.h"
#include "addrmap.h" #include "addrmap.h"
#include "lock.h"
static MemPool sVramPool; static MemPool sVramPool;
static LightLock sLock = 1;
static bool vramInit() static bool vramInit()
{ {
@ -39,6 +41,7 @@ void* vramMemAlign(size_t size, size_t alignment)
return nullptr; return nullptr;
// Initialize the pool if it is not ready // Initialize the pool if it is not ready
LockGuard guard(sLock);
if (!sVramPool.Ready() && !vramInit()) if (!sVramPool.Ready() && !vramInit())
return nullptr; return nullptr;
@ -70,12 +73,14 @@ void* vramRealloc(void* mem, size_t size)
size_t vramGetSize(void* mem) size_t vramGetSize(void* mem)
{ {
LockGuard guard(sLock);
auto node = getNode(mem); auto node = getNode(mem);
return node ? node->chunk.size : 0; return node ? node->chunk.size : 0;
} }
void vramFree(void* mem) void vramFree(void* mem)
{ {
LockGuard guard(sLock);
auto node = getNode(mem); auto node = getNode(mem);
if (!node) return; if (!node) return;
@ -88,5 +93,6 @@ void vramFree(void* mem)
u32 vramSpaceFree() u32 vramSpaceFree()
{ {
LockGuard guard(sLock);
return sVramPool.GetFreeSpace(); return sVramPool.GetFreeSpace();
} }

81
libctru/source/crc32c.c Normal file
View File

@ -0,0 +1,81 @@
#include "crc32c.h"
static const uint32_t crc32cTable[] =
{
0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
};
uint32_t crc32c(const void *buffer, size_t size, uint32_t crc)
{
const uint8_t *ptr = (const uint8_t*)buffer;
crc ^= ~(uint32_t)0;
for(size_t i = 0; i < size; ++i)
crc = crc32cTable[(crc ^ ptr[i]) & 0xFF] ^ (crc >> 8);
return ~crc;
}

6
libctru/source/crc32c.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
uint32_t crc32c(const void *buffer, size_t size, uint32_t crc);

View File

@ -5,25 +5,28 @@ void
rbtree_clear(rbtree_t *tree, rbtree_clear(rbtree_t *tree,
rbtree_node_destructor_t destructor) rbtree_node_destructor_t destructor)
{ {
rbtree_set_busy(tree);
rbtree_validate(tree);
rbtree_node_t *node = tree->root; rbtree_node_t *node = tree->root;
while(tree->root != NULL) while(tree->root)
{ {
while(node->child[LEFT] != NULL) while(node->child[LEFT])
node = node->child[LEFT]; node = node->child[LEFT];
if(node->child[RIGHT] != NULL) if(node->child[RIGHT])
node = node->child[RIGHT]; node = node->child[RIGHT];
else else
{ {
rbtree_node_t *parent = get_parent(node); rbtree_node_t *parent = get_parent(node);
if(parent == NULL) if(!parent)
tree->root = NULL; tree->root = NULL;
else else
parent->child[node != parent->child[LEFT]] = NULL; parent->child[node != parent->child[LEFT]] = NULL;
if(destructor != NULL) if(destructor)
(*destructor)(node); (*destructor)(node);
node = parent; node = parent;
@ -31,4 +34,7 @@ rbtree_clear(rbtree_t *tree,
} }
tree->size = 0; tree->size = 0;
rbtree_validate(tree);
rbtree_clear_busy(tree);
} }

View File

@ -1,7 +1,14 @@
#include <3ds/util/rbtree.h> #include <3ds/util/rbtree.h>
#include "rbtree_internal.h"
int int
rbtree_empty(const rbtree_t *tree) rbtree_empty(const rbtree_t *tree)
{ {
return tree->root == NULL; rbtree_set_busy((rbtree_t*)tree);
rbtree_validate(tree);
bool empty = !tree->root;
rbtree_clear_busy((rbtree_t*)tree);
return empty;
} }

View File

@ -5,10 +5,13 @@ rbtree_node_t*
rbtree_find(const rbtree_t *tree, rbtree_find(const rbtree_t *tree,
const rbtree_node_t *node) const rbtree_node_t *node)
{ {
rbtree_set_busy((rbtree_t*)tree);
rbtree_validate(tree);
rbtree_node_t *tmp = tree->root; rbtree_node_t *tmp = tree->root;
rbtree_node_t *save = NULL; rbtree_node_t *save = NULL;
while(tmp != NULL) while(tmp)
{ {
int rc = (*tree->comparator)(node, tmp); int rc = (*tree->comparator)(node, tmp);
if(rc < 0) if(rc < 0)
@ -26,5 +29,8 @@ rbtree_find(const rbtree_t *tree,
} }
} }
rbtree_validate(tree);
rbtree_clear_busy((rbtree_t*)tree);
return save; return save;
} }

View File

@ -1,10 +1,19 @@
#include <3ds/util/rbtree.h> #include <3ds/util/rbtree.h>
#include "rbtree_internal.h"
void void
rbtree_init(rbtree_t *tree, rbtree_init(rbtree_t *tree,
rbtree_node_comparator_t comparator) rbtree_node_comparator_t comparator)
{ {
tree->treeTag[3] = 'T';
tree->treeTag[2] = 'R';
tree->treeTag[1] = 'E';
tree->treeTag[0] = 'E';
tree->root = NULL; tree->root = NULL;
tree->comparator = comparator; tree->comparator = comparator;
tree->size = 0; tree->size = 0;
tree->busy = false;
LightLock_Init(&tree->lock);
rbtree_validate(tree);
} }

View File

@ -6,12 +6,30 @@ do_insert(rbtree_t *tree,
rbtree_node_t *node, rbtree_node_t *node,
int multi) int multi)
{ {
rbtree_set_busy(tree);
rbtree_validate(tree);
for(size_t i = 0; i < 32; ++i)
{
node->prefix[i] = 0;
node->suffix[i] = 0;
}
node->nodeTag[3] = 'N';
node->nodeTag[2] = 'O';
node->nodeTag[1] = 'D';
node->nodeTag[0] = 'E';
node->parent_color = 0;
node->child[LEFT] = NULL;
node->child[RIGHT] = NULL;
node->tree = tree;
rbtree_node_t *original = node; rbtree_node_t *original = node;
rbtree_node_t **tmp = &tree->root; rbtree_node_t **tmp = &tree->root;
rbtree_node_t *parent = NULL; rbtree_node_t *parent = NULL;
rbtree_node_t *save = NULL; rbtree_node_t *save = NULL;
while(*tmp != NULL) while(*tmp)
{ {
int cmp = (*(tree->comparator))(node, *tmp); int cmp = (*(tree->comparator))(node, *tmp);
parent = *tmp; parent = *tmp;
@ -29,8 +47,20 @@ do_insert(rbtree_t *tree,
} }
} }
if(save != NULL) if(save)
{ {
node->nodeTag[3] = 'D';
node->nodeTag[2] = 'O';
node->nodeTag[1] = 'N';
node->nodeTag[0] = 'E';
node->parent_color = 0;
node->child[LEFT] = NULL;
node->child[RIGHT] = NULL;
node->tree = NULL;
rbtree_validate(tree);
rbtree_clear_busy(tree);
return save; return save;
} }
@ -78,6 +108,9 @@ do_insert(rbtree_t *tree,
tree->size += 1; tree->size += 1;
rbtree_validate(tree);
rbtree_clear_busy(tree);
return original; return original;
} }

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "3ds/util/rbtree.h"
#define LEFT 0 #define LEFT 0
#define RIGHT 1 #define RIGHT 1
@ -28,7 +29,7 @@ set_red(rbtree_node_t *node)
static inline rbtree_color_t static inline rbtree_color_t
get_color(const rbtree_node_t *node) get_color(const rbtree_node_t *node)
{ {
if(node == NULL) if(!node)
return BLACK; return BLACK;
return (rbtree_color_t)(node->parent_color & COLOR_MASK); return (rbtree_color_t)(node->parent_color & COLOR_MASK);
} }
@ -58,7 +59,19 @@ set_parent(rbtree_node_t *node,
node->parent_color = (get_color(node)) | ((uintptr_t)parent); node->parent_color = (get_color(node)) | ((uintptr_t)parent);
} }
void
rbtree_set_busy(rbtree_t *tree);
void
rbtree_clear_busy(rbtree_t *tree);
void void
rbtree_rotate(rbtree_t *tree, rbtree_rotate(rbtree_t *tree,
rbtree_node_t *node, rbtree_node_t *node,
int left); int left);
void
rbtree_validate(const rbtree_t *tree);
void
rbtree_node_validate(const rbtree_t *tree, const rbtree_node_t *node, size_t *all, size_t *black, size_t *depth);

View File

@ -7,16 +7,16 @@ do_iterate(const rbtree_node_t *node,
{ {
rbtree_node_t *it = (rbtree_node_t*)node; rbtree_node_t *it = (rbtree_node_t*)node;
if(it->child[next] != NULL) if(it->child[next])
{ {
it = it->child[next]; it = it->child[next];
while(it->child[!next] != NULL) while(it->child[!next])
it = it->child[!next]; it = it->child[!next];
} }
else else
{ {
rbtree_node_t *parent = get_parent(node); rbtree_node_t *parent = get_parent(node);
while(parent != NULL && it == parent->child[next]) while(parent && it == parent->child[next])
{ {
it = parent; it = parent;
parent = get_parent(it); parent = get_parent(it);

View File

@ -5,33 +5,32 @@ static inline rbtree_node_t*
do_minmax(const rbtree_t *tree, do_minmax(const rbtree_t *tree,
int max) int max)
{ {
rbtree_set_busy((rbtree_t*)tree);
rbtree_validate(tree);
rbtree_node_t *node = tree->root; rbtree_node_t *node = tree->root;
if(node == NULL) if(!node)
{
rbtree_clear_busy((rbtree_t*)tree);
return NULL; return NULL;
}
while(node->child[max] != NULL) while(node->child[max])
node = node->child[max]; node = node->child[max];
rbtree_clear_busy((rbtree_t*)tree);
return node; return node;
} }
rbtree_node_t* rbtree_node_t*
rbtree_min(const rbtree_t *tree) rbtree_min(const rbtree_t *tree)
{ {
rbtree_node_t *node; return do_minmax(tree, LEFT);
node = do_minmax(tree, LEFT);
return node;
} }
rbtree_node_t* rbtree_node_t*
rbtree_max(const rbtree_t *tree) rbtree_max(const rbtree_t *tree)
{ {
rbtree_node_t *node; return do_minmax(tree, RIGHT);
node = do_minmax(tree, RIGHT);
return node;
} }

View File

@ -1,4 +1,5 @@
#include <3ds/util/rbtree.h> #include <3ds/util/rbtree.h>
#include <3ds/svc.h>
#include "rbtree_internal.h" #include "rbtree_internal.h"
static void static void
@ -51,31 +52,55 @@ recolor(rbtree_t *tree,
} }
} }
if(node != NULL) if(node)
set_black(node); set_black(node);
} }
static bool
in_tree(const rbtree_t *tree,
const rbtree_node_t *node)
{
if(tree->root == node)
return true;
const rbtree_node_t *parent = get_parent(node);
while(parent)
{
if(tree->root == parent)
return true;
parent = get_parent(parent);
}
return false;
}
rbtree_node_t* rbtree_node_t*
rbtree_remove(rbtree_t *tree, rbtree_remove(rbtree_t *tree,
rbtree_node_t *node, rbtree_node_t *node,
rbtree_node_destructor_t destructor) rbtree_node_destructor_t destructor)
{ {
rbtree_set_busy(tree);
if(!in_tree(tree, node))
svcBreak(USERBREAK_PANIC);
rbtree_validate(tree);
rbtree_color_t color; rbtree_color_t color;
rbtree_node_t *child, *parent, *original = node; rbtree_node_t *child, *parent, *original = node;
rbtree_node_t *next; rbtree_node_t *next;
next = rbtree_node_next(node); next = rbtree_node_next(node);
if(node->child[LEFT] != NULL && node->child[RIGHT] != NULL) if(node->child[LEFT] && node->child[RIGHT])
{ {
rbtree_node_t *old = node; rbtree_node_t *old = node;
node = node->child[RIGHT]; node = node->child[RIGHT];
while(node->child[LEFT] != NULL) while(node->child[LEFT])
node = node->child[LEFT]; node = node->child[LEFT];
parent = get_parent(old); parent = get_parent(old);
if(parent != NULL) if(parent)
{ {
if(parent->child[LEFT] == old) if(parent->child[LEFT] == old)
parent->child[LEFT] = node; parent->child[LEFT] = node;
@ -93,7 +118,7 @@ rbtree_remove(rbtree_t *tree,
parent = node; parent = node;
else else
{ {
if(child != NULL) if(child)
set_parent(child, parent); set_parent(child, parent);
parent->child[LEFT] = child; parent->child[LEFT] = child;
@ -107,7 +132,7 @@ rbtree_remove(rbtree_t *tree,
} }
else else
{ {
if(node->child[LEFT] == NULL) if(!node->child[LEFT])
child = node->child[RIGHT]; child = node->child[RIGHT];
else else
child = node->child[LEFT]; child = node->child[LEFT];
@ -115,9 +140,9 @@ rbtree_remove(rbtree_t *tree,
parent = get_parent(node); parent = get_parent(node);
color = get_color(node); color = get_color(node);
if(child != NULL) if(child)
set_parent(child, parent); set_parent(child, parent);
if(parent != NULL) if(parent)
{ {
if(parent->child[LEFT] == node) if(parent->child[LEFT] == node)
parent->child[LEFT] = child; parent->child[LEFT] = child;
@ -131,10 +156,22 @@ rbtree_remove(rbtree_t *tree,
if(color == BLACK) if(color == BLACK)
recolor(tree, parent, child); recolor(tree, parent, child);
if(destructor != NULL) original->nodeTag[3] = 'D';
original->nodeTag[2] = 'O';
original->nodeTag[1] = 'N';
original->nodeTag[0] = 'E';
original->parent_color = 0;
original->child[LEFT] = NULL;
original->child[RIGHT] = NULL;
original->tree = NULL;
if(destructor)
(*destructor)(original); (*destructor)(original);
tree->size -= 1; tree->size -= 1;
rbtree_validate(tree);
rbtree_clear_busy(tree);
return next; return next;
} }

View File

@ -10,12 +10,12 @@ rbtree_rotate(rbtree_t *tree,
rbtree_node_t *parent = get_parent(node); rbtree_node_t *parent = get_parent(node);
node->child[left] = tmp->child[!left]; node->child[left] = tmp->child[!left];
if(tmp->child[!left] != NULL) if(tmp->child[!left])
set_parent(tmp->child[!left], node); set_parent(tmp->child[!left], node);
tmp->child[!left] = node; tmp->child[!left] = node;
set_parent(tmp, parent); set_parent(tmp, parent);
if(parent != NULL) if(parent)
{ {
if(node == parent->child[!left]) if(node == parent->child[!left])
parent->child[!left] = tmp; parent->child[!left] = tmp;

View File

@ -1,7 +1,14 @@
#include <3ds/util/rbtree.h> #include <3ds/util/rbtree.h>
#include "rbtree_internal.h"
size_t size_t
rbtree_size(const rbtree_t *tree) rbtree_size(const rbtree_t *tree)
{ {
return tree->size; rbtree_set_busy((rbtree_t*)tree);
rbtree_validate(tree);
size_t size = tree->size;
rbtree_clear_busy((rbtree_t*)tree);
return size;
} }

View File

@ -0,0 +1,164 @@
#include "rbtree_internal.h"
#include <3ds/svc.h>
#include <stdlib.h>
#define panic() do { \
svcBreak(USERBREAK_PANIC); \
abort(); \
} while(0)
void
rbtree_validate(const rbtree_t *tree)
{
if(!tree)
panic();
// root node must be black
if(!is_black(tree->root))
panic();
// root node's parent must be null
if(tree->root)
{
if(get_parent(tree->root))
panic();
}
// validate subtree starting at root node
size_t size = 0;
rbtree_node_validate(tree, tree->root, &size, NULL, NULL);
// make sure we are tracking the correct number of nodes
if(size != tree->size)
panic();
}
void
rbtree_node_validate(const rbtree_t *tree, const rbtree_node_t *node, size_t *size, size_t *black, size_t *depth)
{
if(!node) // implies is_black
{
if(black)
*black = 1;
if(depth)
*depth = 1;
return;
}
for(size_t i = 0; i < 32; ++i)
{
if(node->prefix[i])
panic();
if(node->suffix[i])
panic();
}
rbtree_node_t *parent = get_parent(node);
if(!parent)
{
// only the root node can have a null parent
if(node != tree->root)
panic();
}
else
{
// make sure this node is a child of its parent
if(parent->child[LEFT] != node && parent->child[RIGHT] != node)
panic();
// if the parent is red, this node must be black
if(is_red(parent) && !is_black(node))
panic();
}
if(is_red(node))
{
// if this node is red, both children must be black
if(!is_black(node->child[LEFT]))
panic();
if(!is_black(node->child[RIGHT]))
panic();
}
if(node->child[LEFT])
{
// this node must be >= left child
if((*(tree->comparator))(node, node->child[LEFT]) < 0)
panic();
}
if(node->child[RIGHT])
{
// this node must be <= right child
if((*(tree->comparator))(node, node->child[RIGHT]) > 0)
panic();
}
// validate subtree at left child
size_t left_size = 0;
size_t left_black = 0;
size_t left_depth = 0;
rbtree_node_validate(tree, node->child[LEFT], &left_size, &left_black, &left_depth);
// validate subtree at right child
size_t right_size = 0;
size_t right_black = 0;
size_t right_depth = 0;
rbtree_node_validate(tree, node->child[RIGHT], &right_size, &right_black, &right_depth);
// size is left+right subtrees plus self
if(size)
*size = left_size + right_size + 1;
// all possible paths to leaf nodes must have the same number of black nodes along the path
if(left_black != right_black)
panic();
if(black)
*black = left_black + (is_black(node) ? 1 : 0);
// depth of one subtree must not exceed 2x depth of the other subtree
if(left_depth < right_depth)
{
if(right_depth - left_depth > left_depth)
panic();
}
else if(left_depth - right_depth > right_depth)
panic();
if(depth)
{
if(left_depth > right_depth)
*depth = left_depth + 1;
else
*depth = right_depth + 1;
}
}
void
rbtree_set_busy(rbtree_t *tree)
{
LightLock_Lock(&tree->lock);
if(tree->busy)
panic();
tree->busy = true;
LightLock_Unlock(&tree->lock);
}
void
rbtree_clear_busy(rbtree_t *tree)
{
LightLock_Lock(&tree->lock);
if(!tree->busy)
panic();
tree->busy = false;
LightLock_Unlock(&tree->lock);
}

View File

@ -0,0 +1,28 @@
CFILES := $(wildcard ../*.c)
CXXFILES := $(wildcard *.cpp)
OFILES := $(patsubst ../%,%,$(CFILES:.c=.c.o))
OXXFILES := $(CXXFILES:.cpp=.cpp.o)
CPPFLAGS := -Wall -g -I../../../../include -O2 \
-D"LightLock=pthread_mutex_t" \
-D"LightLock_Init(x)=pthread_mutex_init(x, NULL)" \
-D"LightLock_Lock(x)=pthread_mutex_lock(x)" \
-D"LightLock_Unlock(x)=pthread_mutex_unlock(x)"
CFLAGS := $(CPPFLAGS)
CXXFLAGS := $(CPPFLAGS) -std=c++11
all: rbtree_test
rbtree_test: $(OFILES) $(OXXFILES)
$(CXX) -o $@ $^ $(LDFLAGS)
$(OFILES): %.c.o: ../%.c
$(CC) -o $@ -c $< $(CFLAGS)
$(OXXFILES): %.cpp.o: %.cpp
$(CXX) -o $@ -c $< $(CXXFLAGS)
clean:
$(RM) rbtree_test $(OFILES) $(OXXFILES)

View File

@ -0,0 +1,130 @@
extern "C"
{
#include <3ds/svc.h>
#include <3ds/util/rbtree.h>
#include "../rbtree_internal.h"
}
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <limits>
#include <random>
#include <vector>
namespace
{
struct IntNode
{
IntNode(int value) : value(value)
{}
const int value;
rbtree_node_t node;
};
IntNode *getIntNode(rbtree_node_t *node)
{
return rbtree_item(node, IntNode, node);
}
const IntNode *getIntNode(const rbtree_node_t *node)
{
return rbtree_item(node, IntNode, node);
}
void IntNodeDestructor(rbtree_node_t *node)
{
delete getIntNode(node);
}
int IntNodeComparator(const rbtree_node_t *lhs, const rbtree_node_t *rhs)
{
auto left = getIntNode(lhs)->value;
auto right = getIntNode(rhs)->value;
if(left < right)
return -1;
if(right < left)
return 1;
return 0;
}
#if 0
void printNode(const rbtree_node_t *node, int indent)
{
if(!node)
{
std::printf("%*s(nil) black\n", indent, "");
return;
}
std::printf("%*s%d %s\n", indent, "", getIntNode(node)->value, is_black(node) ? "black" : "red");
printNode(node->child[LEFT], indent + 2);
printNode(node->child[RIGHT], indent + 2);
}
void printTree(const rbtree_t *tree)
{
std::printf("==========\n");
printNode(tree->root, 0);
}
#endif
}
void svcBreak(UserBreakType breakReason)
{
std::abort();
}
int main(int argc, char *argv[])
{
std::default_random_engine eng;
{
std::random_device rand;
std::random_device::result_type seedBuffer[32];
for(auto &d : seedBuffer)
d = rand();
std::seed_seq seed(std::begin(seedBuffer), std::end(seedBuffer));
eng.seed(seed);
}
auto dist = std::bind(std::uniform_int_distribution<int>(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()), std::ref(eng));
auto remove = std::bind(std::uniform_int_distribution<std::size_t>(0, 100), std::ref(eng));
rbtree_t tree;
rbtree_init(&tree, IntNodeComparator);
for(std::size_t chance = 0; chance <= 100; chance += 25)
{
std::printf("Chance %zu\n", chance);
std::vector<IntNode*> nodes;
for(std::size_t i = 0; i < 10000; ++i)
{
auto node = new IntNode(dist());
if(rbtree_insert(&tree, &node->node) != &node->node)
delete node;
else
nodes.emplace_back(node);
// remove random node
if(remove() <= chance)
{
auto it = std::begin(nodes) + (eng() % nodes.size());
rbtree_remove(&tree, &(*it)->node, IntNodeDestructor);
nodes.erase(it);
}
// printTree(&tree);
}
}
std::printf("Ended with %zu nodes in tree\n", rbtree_size(&tree));
rbtree_clear(&tree, IntNodeDestructor);
}