From 8b40798fe4ee951c0e7513afa1fd042800a71e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 1 Oct 2003 01:51:01 +0000 Subject: [PATCH] Moved the "btree" test from befs/ to bfs/. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4899 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../add-ons/kernel/file_systems/befs/Jamfile | 1 - .../add-ons/kernel/file_systems/bfs/Jamfile | 1 + .../kernel/file_systems/bfs/btree/Inode.cpp | 53 ++ .../kernel/file_systems/bfs/btree/Inode.h | 53 ++ .../kernel/file_systems/bfs/btree/Jamfile | 25 + .../kernel/file_systems/bfs/btree/Journal.h | 42 + .../kernel/file_systems/bfs/btree/Volume.cpp | 18 + .../kernel/file_systems/bfs/btree/Volume.h | 36 + .../kernel/file_systems/bfs/btree/cache.cpp | 106 +++ .../kernel/file_systems/bfs/btree/cache.h | 22 + .../kernel/file_systems/bfs/btree/cpp.h | 5 + .../kernel/file_systems/bfs/btree/test.cpp | 759 ++++++++++++++++++ 12 files changed, 1120 insertions(+), 1 deletion(-) create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/Inode.cpp create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/Inode.h create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/Jamfile create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/Journal.h create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/Volume.cpp create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/Volume.h create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/cache.cpp create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/cache.h create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/cpp.h create mode 100644 src/tests/add-ons/kernel/file_systems/bfs/btree/test.cpp diff --git a/src/tests/add-ons/kernel/file_systems/befs/Jamfile b/src/tests/add-ons/kernel/file_systems/befs/Jamfile index 5b6f5d8f15..88ecbb10b1 100644 --- a/src/tests/add-ons/kernel/file_systems/befs/Jamfile +++ b/src/tests/add-ons/kernel/file_systems/befs/Jamfile @@ -1,7 +1,6 @@ SubDir OBOS_TOP src tests add-ons kernel file_systems befs ; SubInclude OBOS_TOP src tests add-ons kernel file_systems befs array ; -SubInclude OBOS_TOP src tests add-ons kernel file_systems befs btree ; SubInclude OBOS_TOP src tests add-ons kernel file_systems befs queries ; SubInclude OBOS_TOP src tests add-ons kernel file_systems befs randomread ; SubInclude OBOS_TOP src tests add-ons kernel file_systems befs rename ; diff --git a/src/tests/add-ons/kernel/file_systems/bfs/Jamfile b/src/tests/add-ons/kernel/file_systems/bfs/Jamfile index 47bfac38d3..ad0eec2ce0 100644 --- a/src/tests/add-ons/kernel/file_systems/bfs/Jamfile +++ b/src/tests/add-ons/kernel/file_systems/bfs/Jamfile @@ -3,3 +3,4 @@ SubDir OBOS_TOP src tests add-ons kernel file_systems bfs ; #SubInclude OBOS_TOP src tests add-ons kernel file_systems bfs blockAllocator ; SubInclude OBOS_TOP src tests add-ons kernel file_systems bfs bufferPool ; SubInclude OBOS_TOP src tests add-ons kernel file_systems bfs bfs_shell ; +SubInclude OBOS_TOP src tests add-ons kernel file_systems bfs btree ; diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/Inode.cpp b/src/tests/add-ons/kernel/file_systems/bfs/btree/Inode.cpp new file mode 100644 index 0000000000..2d38e76cee --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/Inode.cpp @@ -0,0 +1,53 @@ +/* Inode - emulation for the B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include "Inode.h" +#include "Volume.h" +#include "Journal.h" + + +Inode::Inode(const char *name,int32 mode) + : + fMode(mode) +{ + fFile.SetTo(name,B_CREATE_FILE | B_READ_WRITE | B_ERASE_FILE); + fSize = 0; + fVolume = new Volume(&fFile); +} + + +Inode::~Inode() +{ + delete fVolume; +} + + +status_t +Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset) +{ + // the whole file data is covered by this one block_run structure... + run.SetTo(0,0,1); + offset = 0; + return B_OK; +} + + +status_t +Inode::Append(Transaction *transaction, off_t bytes) +{ + return SetFileSize(transaction,Size() + bytes); +} + + +status_t +Inode::SetFileSize(Transaction *, off_t bytes) +{ + //printf("set size = %ld\n",bytes); + fSize = bytes; + return fFile.SetSize(bytes); +} + diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/Inode.h b/src/tests/add-ons/kernel/file_systems/bfs/btree/Inode.h new file mode 100644 index 0000000000..cebfa194bd --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/Inode.h @@ -0,0 +1,53 @@ +#ifndef INODE_H +#define INODE_H +/* Inode - emulation for the B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include +#include + +#include "Lock.h" +#include "bfs.h" + + +class Volume; +class Transaction; + + +class Inode { + public: + Inode(const char *name,int32 mode = S_STR_INDEX | S_ALLOW_DUPS); + ~Inode(); + + ReadWriteLock &Lock() { return fLock; } + + status_t FindBlockRun(off_t pos,block_run &run,off_t &offset); + status_t Append(Transaction *,off_t bytes); + status_t SetFileSize(Transaction *,off_t bytes); + + Volume *GetVolume() const { return fVolume; } + off_t ID() const { return 0; } + int32 Mode() const { return fMode; } + char *Name() const { return "whatever"; } + block_run BlockRun() const { return block_run::Run(0,0,0); } + block_run Parent() const { return block_run::Run(0,0,0); } + off_t BlockNumber() const { return 0; } + bfs_inode *Node() { return (bfs_inode *)1; } + + off_t Size() const { return fSize; } + bool IsContainer() const { return true; } + bool IsDirectory() const { return true; } + + private: + Volume *fVolume; + BFile fFile; + off_t fSize; + ReadWriteLock fLock; + int32 fMode; +}; + +#endif /* INODE_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/Jamfile b/src/tests/add-ons/kernel/file_systems/bfs/btree/Jamfile new file mode 100644 index 0000000000..5681a9797b --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/Jamfile @@ -0,0 +1,25 @@ +SubDir OBOS_TOP src tests add-ons kernel file_systems befs btree ; + +SubDirHdrs $(OBOS_TOP) src add-ons kernel file_systems bfs ; + +rule FPreIncludes { return -include\ $(1:D=$(SUBDIR)) ; } + +{ + local defines = [ FDefines USER DEBUG ] ; # _NO_INLINE_ASM + local preIncludes = [ FPreIncludes Journal.h Inode.h ] ; + SubDirC++Flags $(defines) $(preIncludes) -fno-exceptions -fno-rtti ; #-fcheck-memory-usage +} + +SimpleTest btreeTest + : test.cpp + Volume.cpp + Inode.cpp + cache.cpp + BPlusTree.cpp + Utility.cpp + Debug.cpp + : be ; + +# Tell Jam where to find these sources +SEARCH on [ FGristFiles BPlusTree.cpp Utility.cpp Debug.cpp ] + = [ FDirName $(OBOS_TOP) src add-ons kernel file_systems bfs ] ; diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/Journal.h b/src/tests/add-ons/kernel/file_systems/bfs/btree/Journal.h new file mode 100644 index 0000000000..db19c82ec1 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/Journal.h @@ -0,0 +1,42 @@ +#ifndef JOURNAL_H +#define JOURNAL_H +/* Journal - emulation for the B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include + +#include "Volume.h" +#include "Debug.h" +#include "cache.h" + + +class Transaction { + public: + Transaction(Volume *volume,off_t refBlock) + : + fVolume(volume) + { + } + + ~Transaction() + { + } + + status_t WriteBlocks(off_t blockNumber,const uint8 *buffer,size_t numBlocks = 1) + { + return cached_write(fVolume->Device(),blockNumber,buffer,numBlocks,fVolume->BlockSize()); + } + + void Done() + { + } + + protected: + Volume *fVolume; +}; + +#endif /* JOURNAL_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/Volume.cpp b/src/tests/add-ons/kernel/file_systems/bfs/btree/Volume.cpp new file mode 100644 index 0000000000..3529d7d119 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/Volume.cpp @@ -0,0 +1,18 @@ +/* Volume - emulation for the B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include "Volume.h" + +#include + + +void +Volume::Panic() +{ + printf("PANIC!\n"); +} + diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/Volume.h b/src/tests/add-ons/kernel/file_systems/bfs/btree/Volume.h new file mode 100644 index 0000000000..74aa5f32e5 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/Volume.h @@ -0,0 +1,36 @@ +#ifndef VOLUME_H +#define VOLUME_H +/* Volume - emulation for the B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include + +#include "Lock.h" +#include "bfs.h" + + +class BFile; + + +class Volume { + public: + Volume(BFile *file) : fFile(file) {} + + BFile *Device() { return fFile; } + + int32 BlockSize() const { return 1024; } + off_t ToBlock(block_run run) const { return run.start; } + block_run ToBlockRun(off_t block) const { return block_run::Run(0,0,block); } + + static void Panic(); + + private: + BFile *fFile; +}; + + +#endif /* VOLUME_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/cache.cpp b/src/tests/add-ons/kernel/file_systems/bfs/btree/cache.cpp new file mode 100644 index 0000000000..2b9275188a --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/cache.cpp @@ -0,0 +1,106 @@ +/* cache - emulation for the B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include "cache.h" + +#include +#include + +#include +#include + + +/* A note from the author: this cache implementation can only be used + * with the test program, it really suites no other needs. + * It's very simple and not that efficient, and simple holds the whole + * file in memory, all the time. + */ + + +BList gBlocks; + + +void +init_cache(BFile */*file*/,int32 /*blockSize*/) +{ +} + + +void +shutdown_cache(BFile *file,int32 blockSize) +{ + for (int32 i = 0;i < gBlocks.CountItems();i++) { + void *buffer = gBlocks.ItemAt(i); + if (buffer == NULL) { + printf("cache is corrupt!\n"); + exit(-1); + } + file->WriteAt(i * blockSize,buffer,blockSize); + free(buffer); + } +} + + +static status_t +readBlocks(BFile *file,uint32 num,uint32 size) +{ + for (int32 i = gBlocks.CountItems();i <= num;i++) { + void *buffer = malloc(size); + if (buffer == NULL) + return B_NO_MEMORY; + + gBlocks.AddItem(buffer); + if (file->ReadAt(i * size,buffer,size) < B_OK) + return B_IO_ERROR; + } + return B_OK; +} + + +int +cached_write(BFile *file, off_t num,const void *data,off_t numBlocks, int blockSize) +{ + //printf("cached_write(num = %Ld,data = %p,numBlocks = %Ld,blockSize = %ld)\n",num,data,numBlocks,blockSize); + if (file == NULL) + return B_BAD_VALUE; + + if (num >= gBlocks.CountItems()) + puts("Oh no!"); + + void *buffer = gBlocks.ItemAt(num); + if (buffer == NULL) + return B_BAD_VALUE; + + if (buffer != data && numBlocks == 1) + memcpy(buffer,data,blockSize); + + return B_OK; +} + + +void * +get_block(BFile *file, off_t num, int blockSize) +{ + //printf("get_block(num = %Ld,blockSize = %ld)\n",num,blockSize); + if (file == NULL) + return NULL; + + if (num >= gBlocks.CountItems()) + readBlocks(file,num,blockSize); + + return gBlocks.ItemAt(num); +} + + +int +release_block(BFile *file, off_t num) +{ + //printf("release_block(num = %Ld)\n",num); + return 0; +} + + diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/cache.h b/src/tests/add-ons/kernel/file_systems/bfs/btree/cache.h new file mode 100644 index 0000000000..0b260f792d --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/cache.h @@ -0,0 +1,22 @@ +#ifndef CACHE_H +#define CACHE_H +/* cache - emulation for the B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include + +class BFile; + + +extern void init_cache(BFile *file, int32 blockSize); +extern void shutdown_cache(BFile *file, int32 blockSize); + +extern int cached_write(BFile *file, off_t bnum, const void *data,off_t num_blocks, int bsize); +extern void *get_block(BFile *file, off_t bnum, int bsize); +extern int release_block(BFile *file, off_t bnum); + +#endif /* CACHE_H */ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/cpp.h b/src/tests/add-ons/kernel/file_systems/bfs/btree/cpp.h new file mode 100644 index 0000000000..0b8faf6264 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/cpp.h @@ -0,0 +1,5 @@ +/* cpp - emulation for the B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ diff --git a/src/tests/add-ons/kernel/file_systems/bfs/btree/test.cpp b/src/tests/add-ons/kernel/file_systems/bfs/btree/test.cpp new file mode 100644 index 0000000000..e521ffbf2e --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/bfs/btree/test.cpp @@ -0,0 +1,759 @@ +/* test - BFS B+Tree torture test +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include "Volume.h" +#include "Inode.h" +#include "BPlusTree.h" + +#include + +#include +#include +#include +#include +#include + + +#define DEFAULT_ITERATIONS 10 +#define DEFAULT_NUM_KEYS 100 +#define DEFAULT_KEY_TYPE S_STR_INDEX + +#define MIN_STRING 3 +#define MAX_STRING 256 + +struct key { + void *data; + uint32 length; + int32 in; + int32 count; + off_t value; +}; + +key *gKeys; +int32 gNum = DEFAULT_NUM_KEYS; +int32 gType = DEFAULT_KEY_TYPE; +int32 gTreeCount = 0; +bool gVerbose, gExcessive; +int32 gIterations = DEFAULT_ITERATIONS; +int32 gHard = 1; +Volume *gVolume; +int32 gSeed = 42; + +// from cache.cpp (yes, we are that mean) +extern BList gBlocks; + + +// prototypes +void bailOut(); +void bailOutWithKey(void *key, uint16 length); +void dumpTree(); +void dumpKey(void *key, int32 length); +void dumpKeys(); + + +void +dumpTree() +{ + puts("\n*** Tree-Dump:\n"); + + bplustree_header *header = (bplustree_header *)gBlocks.ItemAt(0); + dump_bplustree_header(header); + + for (int32 i = 1;i < gBlocks.CountItems();i++) { + bplustree_node *node = (bplustree_node *)gBlocks.ItemAt(i); + printf("\n--- %s node at %ld --------------------------------------\n", + node->overflow_link == BPLUSTREE_NULL ? "leaf" : "index", + i * BPLUSTREE_NODE_SIZE); + dump_bplustree_node(node,header,gVolume); + } +} + + +void +bailOut() +{ + if (gVerbose) { + // dump the tree + dumpTree(); + } + + // in any case, write the tree back to disk + shutdown_cache(gVolume->Device(),gVolume->BlockSize()); + exit(-1); +} + + +void +bailOutWithKey(void *key,uint16 length) +{ + dumpKey(key,length); + putchar('\n'); + bailOut(); +} + + +void +dumpKey(void *key,int32 length) +{ + switch (gType) { + case S_STR_INDEX: + printf("\"%s\" (%ld bytes)",key,length); + break; + case S_INT_INDEX: + printf("%ld",*(int32 *)key); + break; + case S_UINT_INDEX: + printf("%lu",*(uint32 *)key); + break; + case S_LONG_LONG_INDEX: + printf("%Ld",*(int64 *)key); + break; + case S_ULONG_LONG_INDEX: + printf("%Lu",*(uint64 *)key); + break; + case S_FLOAT_INDEX: + printf("%g",*(float *)key); + break; + case S_DOUBLE_INDEX: + printf("%g",*(double *)key); + break; + } + if ((gType == S_INT_INDEX || gType == S_UINT_INDEX || gType == S_FLOAT_INDEX) + && length != 4) + printf(" (wrong length %ld)",length); + else if ((gType == S_LONG_LONG_INDEX || gType == S_ULONG_LONG_INDEX || gType == S_DOUBLE_INDEX) + && length != 8) + printf(" (wrong length %ld)",length); +} + + +void +dumpKeys() +{ + char *type; + switch (gType) { + case S_STR_INDEX: + type = "string"; + break; + case S_INT_INDEX: + type = "int32"; + break; + case S_UINT_INDEX: + type = "uint32"; + break; + case S_LONG_LONG_INDEX: + type = "int64"; + break; + case S_ULONG_LONG_INDEX: + type = "uint64"; + break; + case S_FLOAT_INDEX: + type = "float"; + break; + case S_DOUBLE_INDEX: + type = "double"; + break; + } + printf("Dumping %ld keys of type %s\n",gNum,type); + + for (int32 i = 0;i < gNum;i++) { + printf("% 8ld. (%3ld) key = ",i,gKeys[i].in); + dumpKey(gKeys[i].data,gKeys[i].length); + putchar('\n'); + } +} + + +// #pragma mark - +// +// Functions to generate the keys in every available type +// + + +void +generateName(int32 i,char *name,int32 *_length) +{ + // We're using the index position as a hint for the length + // of the string - this way, it's much less expansive to + // test for string uniqueness. + // We don't want to sort the strings to have more realistic + // access patterns to the tree (only true for the strings test). + int32 length = i % (MAX_STRING - MIN_STRING) + MIN_STRING; + for (int32 i = 0;i < length;i++) { + int32 c = int32(52.0 * rand() / RAND_MAX); + if (c >= 26) + name[i] = 'A' + c - 26; + else + name[i] = 'a' + c; + } + name[length] = 0; + *_length = length; +} + + +void +fillBuffer(void *buffer,int32 start) +{ + for (int32 i = 0;i < gNum;i++) { + switch (gType) { + case S_INT_INDEX: + { + int32 *array = (int32 *)buffer; + array[i] = start + i; + break; + } + case S_UINT_INDEX: + { + uint32 *array = (uint32 *)buffer; + array[i] = start + i; + break; + } + case S_LONG_LONG_INDEX: + { + int64 *array = (int64 *)buffer; + array[i] = start + i; + break; + } + case S_ULONG_LONG_INDEX: + { + uint64 *array = (uint64 *)buffer; + array[i] = start + i; + break; + } + case S_FLOAT_INDEX: + { + float *array = (float *)buffer; + array[i] = start + i * 1.0001; + break; + } + case S_DOUBLE_INDEX: + { + double *array = (double *)buffer; + array[i] = start + i * 1.0001; + break; + } + } + gKeys[i].value = i; + } +} + + +bool +findKey(void *key,int32 length,int32 maxIndex) +{ + for (int32 i = length;i < maxIndex;i += MAX_STRING - MIN_STRING) { + if (length == gKeys[i].length + && !memcpy(key,gKeys[i].data,length)) + return true; + } + return false; +} + + +status_t +createKeys() +{ + gKeys = (key *)malloc(gNum * sizeof(key)); + if (gKeys == NULL) + return B_NO_MEMORY; + + if (gType == S_STR_INDEX) { + for (int32 i = 0;i < gNum;i++) { + char name[B_FILE_NAME_LENGTH]; + int32 length,tries = 0; + bool last; + + // create unique keys! + do { + generateName(i,name,&length); + } while ((last = findKey(name,length,i)) && tries++ < 100); + + if (last) { + printf("Couldn't create unique key list!\n"); + dumpKeys(); + bailOut(); + } + + gKeys[i].data = malloc(length + 1); + memcpy(gKeys[i].data,name,length + 1); + gKeys[i].length = length; + gKeys[i].in = 0; + gKeys[i].count = 0; + gKeys[i].value = i; + } + } else { + int32 length; + int32 start = 0; + switch (gType) { + case S_FLOAT_INDEX: + case S_INT_INDEX: + start = -gNum / 2; + case S_UINT_INDEX: + length = 4; + break; + case S_DOUBLE_INDEX: + case S_LONG_LONG_INDEX: + start = -gNum / 2; + case S_ULONG_LONG_INDEX: + length = 8; + break; + default: + return B_BAD_VALUE; + } + uint8 *buffer = (uint8 *)malloc(length * gNum); + if (buffer == NULL) + return B_NO_MEMORY; + + for (int32 i = 0;i < gNum;i++) { + gKeys[i].data = (void *)(buffer + i * length); + gKeys[i].length = length; + gKeys[i].in = 0; + gKeys[i].count = 0; + } + fillBuffer(buffer,start); + } + return B_OK; +} + + +// #pragma mark - +// +// Tree validity checker +// + + +void +checkTreeContents(BPlusTree *tree) +{ + // reset counter + for (int32 i = 0;i < gNum;i++) + gKeys[i].count = 0; + + TreeIterator iterator(tree); + char key[B_FILE_NAME_LENGTH]; + uint16 length,duplicate; + off_t value; + status_t status; + while ((status = iterator.GetNextEntry(key,&length,B_FILE_NAME_LENGTH,&value,&duplicate)) == B_OK) { + if (value < 0 || value >= gNum) { + iterator.Dump(); + printf("\ninvalid value %Ld in tree: ",value); + bailOutWithKey(key,length); + } + if (gKeys[value].value != value) { + iterator.Dump(); + printf("\nkey pointing to the wrong value %Ld (should be %Ld)\n",value,gKeys[value].value); + bailOutWithKey(key,length); + } + if (length != gKeys[value].length + || memcmp(key,gKeys[value].data,length)) { + iterator.Dump(); + printf("\nkeys don't match (key index = %Ld, %ld times in tree, %ld. occassion):\n\tfound: ",value,gKeys[value].in,gKeys[value].count + 1); + dumpKey(key,length); + printf("\n\texpected: "); + dumpKey(gKeys[value].data,gKeys[value].length); + putchar('\n'); + bailOut(); + } + + gKeys[value].count++; + } + if (status != B_ENTRY_NOT_FOUND) { + printf("TreeIterator::GetNext() returned: %s\n",strerror(status)); + iterator.Dump(); + bailOut(); + } + + for (int32 i = 0;i < gNum;i++) { + if (gKeys[i].in != gKeys[i].count) { + printf("Key "); + dumpKey(gKeys[i].data,gKeys[i].length); + printf(" found only %ld from %ld\n",gKeys[i].count,gKeys[i].in); + } + } +} + + +void +checkTreeIntegrity(BPlusTree *tree) +{ + // simple test, just seeks down to every key - if it couldn't + // be found, something must be wrong + + TreeIterator iterator(tree); + for (int32 i = 0;i < gNum;i++) { + if (gKeys[i].in == 0) + continue; + + status_t status = iterator.Find((uint8 *)gKeys[i].data,gKeys[i].length); + if (status != B_OK) { + printf("TreeIterator::Find() returned: %s\n",strerror(status)); + bailOutWithKey(gKeys[i].data,gKeys[i].length); + } + } +} + + +void +checkTree(BPlusTree *tree) +{ + if (!gExcessive) + printf("* Check tree...\n"); + + checkTreeContents(tree); + checkTreeIntegrity(tree); +} + + +// #pragma mark - +// +// The tree "torture" functions +// + + +void +addAllKeys(Transaction *transaction, BPlusTree *tree) +{ + printf("*** Adding all keys to the tree...\n"); + for (int32 i = 0;i < gNum;i++) { + status_t status = tree->Insert(transaction,(uint8 *)gKeys[i].data,gKeys[i].length,gKeys[i].value); + if (status < B_OK) { + printf("BPlusTree::Insert() returned: %s\n",strerror(status)); + printf("key: "); + dumpKey(gKeys[i].data,gKeys[i].length); + putchar('\n'); + } + else { + gKeys[i].in++; + gTreeCount++; + } + } + checkTree(tree); +} + + +void +removeAllKeys(Transaction *transaction, BPlusTree *tree) +{ + printf("*** Removing all keys from the tree...\n"); + for (int32 i = 0;i < gNum;i++) { + while (gKeys[i].in > 0) { + status_t status = tree->Remove(transaction, (uint8 *)gKeys[i].data, + gKeys[i].length, gKeys[i].value); + if (status < B_OK) { + printf("BPlusTree::Remove() returned: %s\n", strerror(status)); + printf("key: "); + dumpKey(gKeys[i].data, gKeys[i].length); + putchar('\n'); + } + else { + gKeys[i].in--; + gTreeCount--; + } + } + } + checkTree(tree); + +} + + +void +duplicateTest(Transaction *transaction,BPlusTree *tree) +{ + int32 index = int32(1.0 * gNum * rand() / RAND_MAX); + if (index == gNum) + index = gNum - 1; + + printf("*** Duplicate test with key "); + dumpKey(gKeys[index].data,gKeys[index].length); + puts("..."); + + status_t status; + + int32 insertTotal = 0; + for (int32 i = 0;i < 8;i++) { + int32 insertCount = int32(1000.0 * rand() / RAND_MAX); + if (gVerbose) + printf("* insert %ld to %ld old entries...\n",insertCount,insertTotal + gKeys[index].in); + + for (int32 j = 0;j < insertCount;j++) { + status = tree->Insert(transaction,(uint8 *)gKeys[index].data,gKeys[index].length,insertTotal); + if (status < B_OK) { + printf("BPlusTree::Insert() returned: %s\n",strerror(status)); + bailOutWithKey(gKeys[index].data,gKeys[index].length); + } + insertTotal++; + gTreeCount++; + + if (gExcessive) + checkTree(tree); + } + + int32 count; + if (i < 7) { + count = int32(1000.0 * rand() / RAND_MAX); + if (count > insertTotal) + count = insertTotal; + } else + count = insertTotal; + + if (gVerbose) + printf("* remove %ld from %ld entries...\n",count,insertTotal + gKeys[index].in); + + for (int32 j = 0;j < count;j++) { + status_t status = tree->Remove(transaction,(uint8 *)gKeys[index].data,gKeys[index].length,insertTotal - 1); + if (status < B_OK) { + printf("BPlusTree::Remove() returned: %s\n",strerror(status)); + bailOutWithKey(gKeys[index].data,gKeys[index].length); + } + insertTotal--; + gTreeCount--; + + if (gExcessive) + checkTree(tree); + } + } + + if (!gExcessive) + checkTree(tree); +} + + +void +addRandomSet(Transaction *transaction,BPlusTree *tree,int32 num) +{ + printf("*** Add random set to tree (%ld to %ld old entries)...\n",num,gTreeCount); + + for (int32 i = 0;i < num;i++) { + int32 index = int32(1.0 * gNum * rand() / RAND_MAX); + if (index == gNum) + index = gNum - 1; + + if (gVerbose) + printf("adding key %ld (%ld times in the tree)\n",index,gKeys[index].in); + + status_t status = tree->Insert(transaction,(uint8 *)gKeys[index].data,gKeys[index].length,gKeys[index].value); + if (status < B_OK) { + printf("BPlusTree::Insert() returned: %s\n",strerror(status)); + bailOutWithKey(gKeys[index].data,gKeys[index].length); + } + gKeys[index].in++; + gTreeCount++; + + if (gExcessive) + checkTree(tree); + } + if (!gExcessive) + checkTree(tree); +} + + +void +removeRandomSet(Transaction *transaction,BPlusTree *tree,int32 num) +{ + printf("*** Remove random set from tree (%ld from %ld entries)...\n",num,gTreeCount); + + int32 tries = 500; + + for (int32 i = 0;i < num;i++) { + if (gTreeCount < 1) + break; + + int32 index = int32(1.0 * gNum * rand() / RAND_MAX); + if (index == gNum) + index = gNum - 1; + + if (gKeys[index].in == 0) { + i--; + if (tries-- < 0) + break; + continue; + } + + if (gVerbose) + printf("removing key %ld (%ld times in the tree)\n",index,gKeys[index].in); + + status_t status = tree->Remove(transaction,(uint8 *)gKeys[index].data,gKeys[index].length,gKeys[index].value); + if (status < B_OK) { + printf("BPlusTree::Remove() returned: %s\n",strerror(status)); + bailOutWithKey(gKeys[index].data,gKeys[index].length); + } + gKeys[index].in--; + gTreeCount--; + + if (gExcessive) + checkTree(tree); + } + if (!gExcessive) + checkTree(tree); +} + + +// #pragma mark - + + +void +usage(char *program) +{ + if (strrchr(program,'/')) + program = strrchr(program,'/') + 1; + fprintf(stderr,"usage: %s [-veh] [-t type] [-n keys] [-i iterations] [-h times] [-r seed]\n" + "BFS B+Tree torture test\n" + "\t-t\ttype is one of string, int32, uint32, int64, uint64, float,\n" + "\t\tor double; defaults to string.\n" + "\t-n\tkeys is the number of keys to be used,\n" + "\t\tminimum is 1, defaults to %ld.\n" + "\t-i\titerations is the number of the test cycles, defaults to %ld.\n" + "\t-r\tthe seed for the random function, defaults to %ld.\n" + "\t-h\tremoves the keys and start over again for x times.\n" + "\t-e\texcessive validity tests: tree contents will be tested after every operation\n" + "\t-v\tfor verbose output.\n", + program, DEFAULT_NUM_KEYS, DEFAULT_ITERATIONS, gSeed); + exit(0); +} + + +int +main(int argc,char **argv) +{ + char *program = argv[0]; + + while (*++argv) + { + char *arg = *argv; + if (*arg == '-') + { + if (arg[1] == '-') + usage(program); + + while (*++arg && isalpha(*arg)) + { + switch (*arg) + { + case 'v': + gVerbose = true; + break; + case 'e': + gExcessive = true; + break; + case 't': + if (*++argv == NULL) + usage(program); + + if (!strcmp(*argv,"string")) + gType = S_STR_INDEX; + else if (!strcmp(*argv,"int32") + || !strcmp(*argv,"int")) + gType = S_INT_INDEX; + else if (!strcmp(*argv,"uint32") + || !strcmp(*argv,"uint")) + gType = S_UINT_INDEX; + else if (!strcmp(*argv,"int64") + || !strcmp(*argv,"llong")) + gType = S_LONG_LONG_INDEX; + else if (!strcmp(*argv,"uint64") + || !strcmp(*argv,"ullong")) + gType = S_ULONG_LONG_INDEX; + else if (!strcmp(*argv,"float")) + gType = S_FLOAT_INDEX; + else if (!strcmp(*argv,"double")) + gType = S_DOUBLE_INDEX; + else + usage(program); + break; + case 'n': + if (*++argv == NULL || !isdigit(**argv)) + usage(program); + + gNum = atoi(*argv); + if (gNum < 1) + gNum = 1; + break; + case 'h': + if (*++argv == NULL || !isdigit(**argv)) + usage(program); + + gHard = atoi(*argv); + if (gHard < 1) + gHard = 1; + break; + case 'i': + if (*++argv == NULL || !isdigit(**argv)) + usage(program); + + gIterations = atoi(*argv); + if (gIterations < 1) + gIterations = 1; + break; + case 'r': + if (*++argv == NULL || !isdigit(**argv)) + usage(program); + + gSeed = atoi(*argv); + break; + } + } + } + else + break; + } + + // we do want to have reproducible random keys + if (gVerbose) + printf("Set seed to %ld\n",gSeed); + srand(gSeed); + + Inode inode("tree.data",gType | S_ALLOW_DUPS); + gVolume = inode.GetVolume(); + Transaction transaction(gVolume,0); + + init_cache(gVolume->Device(),gVolume->BlockSize()); + + // + // Create the tree, the keys, and add all keys to the tree initially + // + + BPlusTree tree(&transaction,&inode); + status_t status; + if ((status = tree.InitCheck()) < B_OK) { + fprintf(stderr,"creating tree failed: %s\n",strerror(status)); + bailOut(); + } + printf("*** Creating %ld keys...\n",gNum); + if ((status = createKeys()) < B_OK) { + fprintf(stderr,"creating keys failed: %s\n",strerror(status)); + bailOut(); + } + + if (gVerbose) + dumpKeys(); + + for (int32 j = 0; j < gHard; j++ ) { + addAllKeys(&transaction, &tree); + + // + // Run the tests (they will exit the app, if an error occurs) + // + + for (int32 i = 0;i < gIterations;i++) { + printf("---------- Test iteration %ld ---------------------------------\n",i+1); + + addRandomSet(&transaction,&tree,int32(1.0 * gNum * rand() / RAND_MAX)); + removeRandomSet(&transaction,&tree,int32(1.0 * gNum * rand() / RAND_MAX)); + duplicateTest(&transaction,&tree); + } + + removeAllKeys(&transaction, &tree); + } + + // of course, we would have to free all our memory in a real application here... + + // write the cache back to the tree + shutdown_cache(gVolume->Device(),gVolume->BlockSize()); + return 0; +} +