- added some missing copyrights
- merged (bs-coreoptimize->trunk) generic athena style configuration parser (raconf) - merged (bs-coreoptimize->trunk) threadsafe memory pool (mempool) [i need it for the new 'socket' system] - set svn:eol-style property on newer files were it was missing git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@16263 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
parent
2cb2ee816b
commit
3471d3f35e
@ -79,6 +79,9 @@ set( COMMON_BASE_HEADERS
|
||||
"${COMMON_SOURCE_DIR}/atomic.h"
|
||||
"${COMMON_SOURCE_DIR}/spinlock.h"
|
||||
"${COMMON_SOURCE_DIR}/thread.h"
|
||||
"${COMMON_SOURCE_DIR}/mutex.h"
|
||||
"${COMMON_SOURCE_DIR}/raconf.h"
|
||||
"${COMMON_SOURCE_DIR}/mempool.h"
|
||||
${LIBCONFIG_HEADERS} # needed by conf.h/showmsg.h
|
||||
CACHE INTERNAL "common_base headers" )
|
||||
set( COMMON_BASE_SOURCES
|
||||
@ -100,6 +103,9 @@ set( COMMON_BASE_SOURCES
|
||||
"${COMMON_SOURCE_DIR}/timer.c"
|
||||
"${COMMON_SOURCE_DIR}/utils.c"
|
||||
"${COMMON_SOURCE_DIR}/thread.c"
|
||||
"${COMMON_SOURCE_DIR}/mutex.c"
|
||||
"${COMMON_SOURCE_DIR}/mempool.c"
|
||||
"${COMMON_SOURCE_DIR}/raconf.c"
|
||||
${LIBCONFIG_SOURCES} # needed by conf.c/showmsg.c
|
||||
CACHE INTERNAL "common_base sources" )
|
||||
set( COMMON_BASE_INCLUDE_DIRS
|
||||
|
@ -3,7 +3,7 @@ COMMON_OBJ = obj_all/core.o obj_all/socket.o obj_all/timer.o obj_all/db.o obj_al
|
||||
obj_all/nullpo.o obj_all/malloc.o obj_all/showmsg.o obj_all/strlib.o obj_all/utils.o \
|
||||
obj_all/grfio.o obj_all/mapindex.o obj_all/ers.o obj_all/md5calc.o \
|
||||
obj_all/minicore.o obj_all/minisocket.o obj_all/minimalloc.o obj_all/random.o obj_all/des.o \
|
||||
obj_all/conf.o obj_all/thread.o obj_all/mutex.o
|
||||
obj_all/conf.o obj_all/thread.o obj_all/mutex.o obj_all/raconf.o obj_all/mempool.o
|
||||
|
||||
COMMON_H = $(shell ls ../common/*.h)
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#ifndef _rA_ATOMIC_H_
|
||||
#define _rA_ATOMIC_H_
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "../common/socket.h"
|
||||
#include "../common/timer.h"
|
||||
#include "../common/thread.h"
|
||||
#include "../common/mempool.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@ -280,7 +281,7 @@ int main (int argc, char **argv)
|
||||
usercheck();
|
||||
|
||||
rathread_init();
|
||||
|
||||
mempool_init();
|
||||
db_init();
|
||||
signals_init();
|
||||
|
||||
@ -306,7 +307,7 @@ int main (int argc, char **argv)
|
||||
timer_final();
|
||||
socket_final();
|
||||
db_final();
|
||||
|
||||
mempool_final();
|
||||
rathread_final();
|
||||
#endif
|
||||
|
||||
|
562
src/common/mempool.c
Normal file
562
src/common/mempool.c
Normal file
@ -0,0 +1,562 @@
|
||||
|
||||
//
|
||||
// Memory Pool Implementation (Threadsafe)
|
||||
//
|
||||
//
|
||||
// Author: Florian Wilkemeyer <fw@f-ws.de>
|
||||
//
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
//
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include "../common/winapi.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
#include "../common/showmsg.h"
|
||||
#include "../common/mempool.h"
|
||||
#include "../common/atomic.h"
|
||||
#include "../common/spinlock.h"
|
||||
#include "../common/thread.h"
|
||||
#include "../common/malloc.h"
|
||||
#include "../common/mutex.h"
|
||||
|
||||
#define ALIGN16 ra_align(16)
|
||||
#define ALIGN_TO(x, a) (x + ( a - ( x % a) ) )
|
||||
#define ALIGN_TO_16(x) ALIGN_TO(x, 16)
|
||||
|
||||
#undef MEMPOOL_DEBUG
|
||||
#define MEMPOOLASSERT
|
||||
|
||||
|
||||
#define NODE_TO_DATA(x) ( ((char*)x) + sizeof(struct node) )
|
||||
#define DATA_TO_NODE(x) ( (struct node*)(((char*)x) - sizeof(struct node)) )
|
||||
struct ra_align(16) node{
|
||||
void *next;
|
||||
void *segment;
|
||||
#ifdef MEMPOOLASSERT
|
||||
bool used;
|
||||
uint64 magic;
|
||||
#define NODE_MAGIC 0xBEEF00EAEACAFE07ll
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
// The Pointer to this struct is the base address of the segment itself.
|
||||
struct pool_segment{
|
||||
mempool pool; // pool, this segment belongs to
|
||||
struct pool_segment *next;
|
||||
int64 num_nodes_total;
|
||||
int64 num_bytes;
|
||||
};
|
||||
|
||||
|
||||
struct mempool{
|
||||
// Settings
|
||||
char *name;
|
||||
uint64 elem_size;
|
||||
uint64 elem_realloc_step;
|
||||
int64 elem_realloc_thresh;
|
||||
|
||||
// Callbacks that get called for every node that gets allocated
|
||||
// Example usage: initialization of mutex/lock for each node.
|
||||
memPoolOnNodeAllocationProc onalloc;
|
||||
memPoolOnNodeDeallocationProc ondealloc;
|
||||
|
||||
// Locks
|
||||
SPIN_LOCK segmentLock;
|
||||
SPIN_LOCK nodeLock;
|
||||
|
||||
|
||||
// Internal
|
||||
struct pool_segment *segments;
|
||||
struct node *free_list;
|
||||
|
||||
volatile int64 num_nodes_total;
|
||||
volatile int64 num_nodes_free;
|
||||
|
||||
volatile int64 num_segments;
|
||||
volatile int64 num_bytes_total;
|
||||
|
||||
volatile int64 peak_nodes_used; // Peak Node Usage
|
||||
volatile int64 num_realloc_events; // Number of reallocations done. (allocate additional nodes)
|
||||
|
||||
// list (used for global management such as allocator..)
|
||||
struct mempool *next;
|
||||
} ra_align(8); // Dont touch the alignment, otherwise interlocked functions are broken ..
|
||||
|
||||
|
||||
///
|
||||
// Implementation:
|
||||
//
|
||||
static void segment_allocate_add(mempool p, uint64 count);
|
||||
|
||||
static SPIN_LOCK l_mempoolListLock;
|
||||
static mempool l_mempoolList = NULL;
|
||||
static rAthread l_async_thread = NULL;
|
||||
static ramutex l_async_lock = NULL;
|
||||
static racond l_async_cond = NULL;
|
||||
static volatile int32 l_async_terminate = 0;
|
||||
|
||||
static void *mempool_async_allocator(void *x){
|
||||
mempool p;
|
||||
|
||||
|
||||
while(1){
|
||||
if(l_async_terminate > 0)
|
||||
break;
|
||||
|
||||
EnterSpinLock(&l_mempoolListLock);
|
||||
|
||||
for(p = l_mempoolList; p != NULL; p = p->next){
|
||||
|
||||
if(p->num_nodes_free < p->elem_realloc_thresh){
|
||||
// add new segment.
|
||||
segment_allocate_add(p, p->elem_realloc_step);
|
||||
// increase stats counter
|
||||
InterlockedIncrement64(&p->num_realloc_events);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LeaveSpinLock(&l_mempoolListLock);
|
||||
|
||||
ramutex_lock( l_async_lock );
|
||||
racond_wait( l_async_cond, l_async_lock, -1 );
|
||||
ramutex_unlock( l_async_lock );
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}//end: mempool_async_allocator()
|
||||
|
||||
|
||||
void mempool_init(){
|
||||
|
||||
if(sizeof(struct node)%16 != 0 ){
|
||||
ShowFatalError("mempool_init: struct node alignment failure. %u != multiple of 16\n", sizeof(struct node));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Global List start
|
||||
InitializeSpinLock(&l_mempoolListLock);
|
||||
l_mempoolList = NULL;
|
||||
|
||||
// Initialize mutex + stuff needed for async allocator worker.
|
||||
l_async_terminate = 0;
|
||||
l_async_lock = ramutex_create();
|
||||
l_async_cond = racond_create();
|
||||
|
||||
l_async_thread = rathread_createEx(mempool_async_allocator, NULL, 512*1024, RAT_PRIO_NORMAL);
|
||||
if(l_async_thread == NULL){
|
||||
ShowFatalError("mempool_init: cannot spawn Async Allocator Thread.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
}//end: mempool_init()
|
||||
|
||||
|
||||
void mempool_final(){
|
||||
mempool p, pn;
|
||||
|
||||
ShowStatus("Mempool: Terminating async. allocation worker and remaining pools.\n");
|
||||
|
||||
// Terminate worker / wait until its terminated.
|
||||
InterlockedIncrement(&l_async_terminate);
|
||||
racond_signal(l_async_cond);
|
||||
rathread_wait(l_async_thread, NULL);
|
||||
|
||||
// Destroy cond var and mutex.
|
||||
racond_destroy( l_async_cond );
|
||||
ramutex_destroy( l_async_lock );
|
||||
|
||||
// Free remaining mempools
|
||||
// ((bugged code! this should halppen, every mempool should
|
||||
// be freed by the subsystem that has allocated it.)
|
||||
//
|
||||
EnterSpinLock(&l_mempoolListLock);
|
||||
p = l_mempoolList;
|
||||
while(1){
|
||||
if(p == NULL)
|
||||
break;
|
||||
|
||||
pn = p->next;
|
||||
|
||||
ShowWarning("Mempool [%s] was not properly destroyed - forcing destroy.\n", p->name);
|
||||
mempool_destroy(p);
|
||||
|
||||
p = pn;
|
||||
}
|
||||
LeaveSpinLock(&l_mempoolListLock);
|
||||
|
||||
}//end: mempool_final()
|
||||
|
||||
|
||||
static void segment_allocate_add(mempool p, uint64 count){
|
||||
|
||||
// Required Memory:
|
||||
// sz( segment )
|
||||
// count * sz( real_node_size )
|
||||
//
|
||||
// where real node size is:
|
||||
// ALIGN_TO_16( sz( node ) ) + p->elem_size
|
||||
// so the nodes usable address is nodebase + ALIGN_TO_16(sz(node))
|
||||
//
|
||||
size_t total_sz;
|
||||
struct pool_segment *seg = NULL;
|
||||
struct node *nodeList = NULL;
|
||||
struct node *node = NULL;
|
||||
char *ptr = NULL;
|
||||
uint64 i;
|
||||
|
||||
total_sz = ALIGN_TO_16( sizeof(struct pool_segment) )
|
||||
+ ( (size_t)count * (sizeof(struct node) + (size_t)p->elem_size) ) ;
|
||||
|
||||
#ifdef MEMPOOL_DEBUG
|
||||
ShowDebug("Mempool [%s] Segment AllocateAdd (num: %u, total size: %0.2fMiB)\n", p->name, count, (float)total_sz/1024.f/1024.f);
|
||||
#endif
|
||||
|
||||
// allocate! (spin forever until weve got the memory.)
|
||||
i=0;
|
||||
while(1){
|
||||
ptr = (char*)aMalloc(total_sz);
|
||||
if(ptr != NULL) break;
|
||||
|
||||
i++; // increase failcount.
|
||||
if(!(i & 7)){
|
||||
ShowWarning("Mempool [%s] Segment AllocateAdd => System seems to be Out of Memory (%0.2f MiB). Try #%u\n", (float)total_sz/1024.f/1024.f, i);
|
||||
#ifdef WIN32
|
||||
Sleep(1000);
|
||||
#else
|
||||
sleep(1);
|
||||
#endif
|
||||
}else{
|
||||
rathread_yield(); /// allow/force vuln. ctxswitch
|
||||
}
|
||||
}//endwhile: allocation spinloop.
|
||||
|
||||
// Clear Memory.
|
||||
memset(ptr, 0x00, total_sz);
|
||||
|
||||
// Initialize segment struct.
|
||||
seg = (struct pool_segment*)ptr;
|
||||
ptr += ALIGN_TO_16(sizeof(struct pool_segment));
|
||||
|
||||
seg->pool = p;
|
||||
seg->num_nodes_total = count;
|
||||
seg->num_bytes = total_sz;
|
||||
|
||||
|
||||
// Initialze nodes!
|
||||
nodeList = NULL;
|
||||
for(i = 0; i < count; i++){
|
||||
node = (struct node*)ptr;
|
||||
ptr += sizeof(struct node);
|
||||
ptr += p->elem_size;
|
||||
|
||||
node->segment = seg;
|
||||
#ifdef MEMPOOLASSERT
|
||||
node->used = false;
|
||||
node->magic = NODE_MAGIC;
|
||||
#endif
|
||||
|
||||
if(p->onalloc != NULL) p->onalloc( NODE_TO_DATA(node) );
|
||||
|
||||
node->next = nodeList;
|
||||
nodeList = node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Link in Segment.
|
||||
EnterSpinLock(&p->segmentLock);
|
||||
seg->next = p->segments;
|
||||
p->segments = seg;
|
||||
LeaveSpinLock(&p->segmentLock);
|
||||
|
||||
// Link in Nodes
|
||||
EnterSpinLock(&p->nodeLock);
|
||||
nodeList->next = p->free_list;
|
||||
p->free_list = nodeList;
|
||||
LeaveSpinLock(&p->nodeLock);
|
||||
|
||||
|
||||
// Increase Stats:
|
||||
InterlockedExchangeAdd64(&p->num_nodes_total, count);
|
||||
InterlockedExchangeAdd64(&p->num_nodes_free, count);
|
||||
InterlockedIncrement64(&p->num_segments);
|
||||
InterlockedExchangeAdd64(&p->num_bytes_total, total_sz);
|
||||
|
||||
}//end: segment_allocate_add()
|
||||
|
||||
|
||||
mempool mempool_create(const char *name,
|
||||
uint64 elem_size,
|
||||
uint64 initial_count,
|
||||
uint64 realloc_count,
|
||||
memPoolOnNodeAllocationProc onNodeAlloc,
|
||||
memPoolOnNodeDeallocationProc onNodeDealloc){
|
||||
//..
|
||||
uint64 realloc_thresh;
|
||||
mempool pool;
|
||||
pool = (mempool)aCalloc( 1, sizeof(struct mempool) );
|
||||
|
||||
if(pool == NULL){
|
||||
ShowFatalError("mempool_create: Failed to allocate %u bytes memory.\n", sizeof(struct mempool) );
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Check minimum initial count / realloc count requirements.
|
||||
if(initial_count < 50)
|
||||
initial_count = 50;
|
||||
if(realloc_count < 50)
|
||||
realloc_count = 50;
|
||||
|
||||
// Set Reallocation threshold to 5% of realloc_count, at least 10.
|
||||
realloc_thresh = (realloc_count/100)*5; //
|
||||
if(realloc_thresh < 10)
|
||||
realloc_thresh = 10;
|
||||
|
||||
// Initialize members..
|
||||
pool->name = aStrdup(name);
|
||||
pool->elem_size = ALIGN_TO_16(elem_size);
|
||||
pool->elem_realloc_step = realloc_count;
|
||||
pool->elem_realloc_thresh = realloc_thresh;
|
||||
pool->onalloc = onNodeAlloc;
|
||||
pool->ondealloc = onNodeDealloc;
|
||||
|
||||
InitializeSpinLock(&pool->segmentLock);
|
||||
InitializeSpinLock(&pool->nodeLock);
|
||||
|
||||
// Initial Statistic values:
|
||||
pool->num_nodes_total = 0;
|
||||
pool->num_nodes_free = 0;
|
||||
pool->num_segments = 0;
|
||||
pool->num_bytes_total = 0;
|
||||
pool->peak_nodes_used = 0;
|
||||
pool->num_realloc_events = 0;
|
||||
|
||||
//
|
||||
#ifdef MEMPOOL_DEBUG
|
||||
ShowDebug("Mempool [%s] Init (ElemSize: %u, Initial Count: %u, Realloc Count: %u)\n", pool->name, pool->elem_size, initial_count, pool->elem_realloc_step);
|
||||
#endif
|
||||
|
||||
// Allocate first segment directly :)
|
||||
segment_allocate_add(pool, initial_count);
|
||||
|
||||
|
||||
// Add Pool to the global pool list
|
||||
EnterSpinLock(&l_mempoolListLock);
|
||||
pool->next = l_mempoolList;
|
||||
l_mempoolList = pool;
|
||||
LeaveSpinLock(&l_mempoolListLock);
|
||||
|
||||
|
||||
return pool;
|
||||
}//end: mempool_create()
|
||||
|
||||
|
||||
void mempool_destroy(mempool p){
|
||||
struct pool_segment *seg, *segnext;
|
||||
struct node *niter;
|
||||
mempool piter, pprev;
|
||||
char *ptr;
|
||||
int64 i;
|
||||
|
||||
#ifdef MEMPOOL_DEBUG
|
||||
ShowDebug("Mempool [%s] Destroy\n", p->name);
|
||||
#endif
|
||||
|
||||
// Unlink from global list.
|
||||
EnterSpinLock(&l_mempoolListLock);
|
||||
piter = l_mempoolList;
|
||||
pprev = l_mempoolList;
|
||||
while(1){
|
||||
if(piter == NULL)
|
||||
break;
|
||||
|
||||
|
||||
if(piter == p){
|
||||
// unlink from list,
|
||||
//
|
||||
if(pprev == l_mempoolList){
|
||||
// this (p) is list begin. so set next as head.
|
||||
l_mempoolList = p->next;
|
||||
}else{
|
||||
// replace prevs next wuth our next.
|
||||
pprev->next = p->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pprev = piter;
|
||||
piter = piter->next;
|
||||
}
|
||||
|
||||
p->next = NULL;
|
||||
LeaveSpinLock(&l_mempoolListLock);
|
||||
|
||||
|
||||
// Get both locks.
|
||||
EnterSpinLock(&p->segmentLock);
|
||||
EnterSpinLock(&p->nodeLock);
|
||||
|
||||
|
||||
if(p->num_nodes_free != p->num_nodes_total)
|
||||
ShowWarning("Mempool [%s] Destroy - %u nodes are not freed properly!\n", p->name, (p->num_nodes_total - p->num_nodes_free) );
|
||||
|
||||
// Free All Segments (this will also free all nodes)
|
||||
// The segment pointer is the base pointer to the whole segment.
|
||||
seg = p->segments;
|
||||
while(1){
|
||||
if(seg == NULL)
|
||||
break;
|
||||
|
||||
segnext = seg->next;
|
||||
|
||||
// ..
|
||||
if(p->ondealloc != NULL){
|
||||
// walk over the segment, and call dealloc callback!
|
||||
ptr = (char*)seg;
|
||||
ptr += ALIGN_TO_16(sizeof(struct pool_segment));
|
||||
for(i = 0; i < seg->num_nodes_total; i++){
|
||||
niter = (struct node*)ptr;
|
||||
ptr += sizeof(struct node);
|
||||
ptr += p->elem_size;
|
||||
#ifdef MEMPOOLASSERT
|
||||
if(niter->magic != NODE_MAGIC){
|
||||
ShowError("Mempool [%s] Destroy - walk over segment - node %p invalid magic!\n", p->name, niter);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
p->ondealloc( NODE_TO_DATA(niter) );
|
||||
|
||||
|
||||
}
|
||||
}//endif: ondealloc callback?
|
||||
|
||||
// simple ..
|
||||
aFree(seg);
|
||||
|
||||
seg = segnext;
|
||||
}
|
||||
|
||||
// Clear node ptr
|
||||
p->free_list = NULL;
|
||||
InterlockedExchange64(&p->num_nodes_free, 0);
|
||||
InterlockedExchange64(&p->num_nodes_total, 0);
|
||||
InterlockedExchange64(&p->num_segments, 0);
|
||||
InterlockedExchange64(&p->num_bytes_total, 0);
|
||||
|
||||
LeaveSpinLock(&p->nodeLock);
|
||||
LeaveSpinLock(&p->segmentLock);
|
||||
|
||||
// Free pool itself :D
|
||||
aFree(p->name);
|
||||
aFree(p);
|
||||
|
||||
}//end: mempool_destroy()
|
||||
|
||||
|
||||
void *mempool_node_get(mempool p){
|
||||
struct node *node;
|
||||
int64 num_used;
|
||||
|
||||
if(p->num_nodes_free < p->elem_realloc_thresh)
|
||||
racond_signal(l_async_cond);
|
||||
|
||||
while(1){
|
||||
|
||||
EnterSpinLock(&p->nodeLock);
|
||||
|
||||
node = p->free_list;
|
||||
if(node != NULL)
|
||||
p->free_list = node->next;
|
||||
|
||||
LeaveSpinLock(&p->nodeLock);
|
||||
|
||||
if(node != NULL)
|
||||
break;
|
||||
|
||||
rathread_yield();
|
||||
}
|
||||
|
||||
InterlockedDecrement64(&p->num_nodes_free);
|
||||
|
||||
// Update peak value
|
||||
num_used = (p->num_nodes_total - p->num_nodes_free);
|
||||
if(num_used > p->peak_nodes_used){
|
||||
InterlockedExchange64(&p->peak_nodes_used, num_used);
|
||||
}
|
||||
|
||||
#ifdef MEMPOOLASSERT
|
||||
node->used = true;
|
||||
#endif
|
||||
|
||||
return NODE_TO_DATA(node);
|
||||
}//end: mempool_node_get()
|
||||
|
||||
|
||||
void mempool_node_put(mempool p, void *data){
|
||||
struct node *node;
|
||||
|
||||
node = DATA_TO_NODE(data);
|
||||
#ifdef MEMPOOLASSERT
|
||||
if(node->magic != NODE_MAGIC){
|
||||
ShowError("Mempool [%s] node_put failed, given address (%p) has invalid magic.\n", p->name, data);
|
||||
return; // lost,
|
||||
}
|
||||
|
||||
{
|
||||
struct pool_segment *node_seg = node->segment;
|
||||
if(node_seg->pool != p){
|
||||
ShowError("Mempool [%s] node_put faild, given node (data address %p) doesnt belongs to this pool. ( Node Origin is [%s] )\n", p->name, data, node_seg->pool);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// reset used flag.
|
||||
node->used = false;
|
||||
#endif
|
||||
|
||||
//
|
||||
EnterSpinLock(&p->nodeLock);
|
||||
node->next = p->free_list;
|
||||
p->free_list = node;
|
||||
LeaveSpinLock(&p->nodeLock);
|
||||
|
||||
InterlockedIncrement64(&p->num_nodes_free);
|
||||
|
||||
}//end: mempool_node_put()
|
||||
|
||||
|
||||
mempool_stats mempool_get_stats(mempool pool){
|
||||
mempool_stats stats;
|
||||
|
||||
// initialize all with zeros
|
||||
memset(&stats, 0x00, sizeof(mempool_stats));
|
||||
|
||||
stats.num_nodes_total = pool->num_nodes_total;
|
||||
stats.num_nodes_free = pool->num_nodes_free;
|
||||
stats.num_nodes_used = (stats.num_nodes_total - stats.num_nodes_free);
|
||||
stats.num_segments = pool->num_segments;
|
||||
stats.num_realloc_events= pool->num_realloc_events;
|
||||
stats.peak_nodes_used = pool->peak_nodes_used;
|
||||
stats.num_bytes_total = pool->num_bytes_total;
|
||||
|
||||
// Pushing such a large block over the stack as return value isnt nice
|
||||
// but lazy :) and should be okay in this case (Stats / Debug..)
|
||||
// if you dont like it - feel free and refactor it.
|
||||
return stats;
|
||||
}//end: mempool_get_stats()
|
||||
|
100
src/common/mempool.h
Normal file
100
src/common/mempool.h
Normal file
@ -0,0 +1,100 @@
|
||||
#ifndef _rA_MEMPOOL_H_
|
||||
#define _rA_MEMPOOL_H_
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
|
||||
typedef struct mempool *mempool;
|
||||
|
||||
typedef void (*memPoolOnNodeAllocationProc)(void *ptr);
|
||||
typedef void (*memPoolOnNodeDeallocationProc)(void *ptr);
|
||||
|
||||
typedef struct mempool_stats{
|
||||
int64 num_nodes_total;
|
||||
int64 num_nodes_free;
|
||||
int64 num_nodes_used;
|
||||
|
||||
int64 num_segments;
|
||||
int64 num_realloc_events;
|
||||
|
||||
int64 peak_nodes_used;
|
||||
|
||||
int64 num_bytes_total;
|
||||
} mempool_stats;
|
||||
|
||||
|
||||
//
|
||||
void mempool_init();
|
||||
void mempool_final();
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new Mempool
|
||||
*
|
||||
* @param name - Name of the pool (used for debug / error messages)
|
||||
* @param elem_size - size of each element
|
||||
* @param initial_count - preallocation count
|
||||
* @param realloc_count - #no of nodes being allocated when pool is running empty.
|
||||
* @param onNodeAlloc - Node Allocation callback (see @note!)
|
||||
* @param onNodeDealloc - Node Deallocation callback (see @note!)
|
||||
*
|
||||
* @note:
|
||||
* The onNode(De)alloc callbacks are only called once during segment allocation
|
||||
* (pool initialization / rallocation )
|
||||
* you can use this callbacks for example to initlaize a mutex or somethingelse
|
||||
* you definitly need during runtime
|
||||
*
|
||||
* @return not NULL
|
||||
*/
|
||||
mempool mempool_create(const char *name,
|
||||
uint64 elem_size,
|
||||
uint64 initial_count,
|
||||
uint64 realloc_count,
|
||||
|
||||
memPoolOnNodeAllocationProc onNodeAlloc,
|
||||
memPoolOnNodeDeallocationProc onNodeDealloc);
|
||||
|
||||
|
||||
/**
|
||||
* Destroys a Mempool
|
||||
*
|
||||
* @param pool - the mempool to destroy
|
||||
*
|
||||
* @note:
|
||||
* Everything gets deallocated, regardless if everything was freed properly!
|
||||
* So you have to ensure that all references are cleared properly!
|
||||
*/
|
||||
void mempool_destroy(mempool pool);
|
||||
|
||||
|
||||
/**
|
||||
* Gets a new / empty node from the given mempool.
|
||||
*
|
||||
* @param pool - the pool to get an empty node from.
|
||||
*
|
||||
* @return Address of empty Node
|
||||
*/
|
||||
void *mempool_node_get(mempool pool);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the given node to the given mempool
|
||||
*
|
||||
* @param pool - the pool to put the node, to
|
||||
* @param node - the node to return
|
||||
*/
|
||||
void mempool_node_put(mempool pool, void *node);
|
||||
|
||||
|
||||
/**
|
||||
* Returns Statistics for the given mempool
|
||||
*
|
||||
* @param pool - the pool to get thats for
|
||||
*
|
||||
* @note: i dont like pushing masses of values over the stack, too - but its lazy and okay for stats. (blacksirius)
|
||||
*
|
||||
* @return stats struct
|
||||
*/
|
||||
mempool_stats mempool_get_stats(mempool pool);
|
||||
|
||||
|
||||
#endif
|
@ -1,3 +1,5 @@
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#ifdef WIN32
|
||||
#include "../common/winapi.h"
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#ifndef _rA_MUTEX_H_
|
||||
#define _rA_MUTEX_H_
|
||||
|
||||
|
584
src/common/raconf.c
Normal file
584
src/common/raconf.c
Normal file
@ -0,0 +1,584 @@
|
||||
//
|
||||
// Athena style config parser
|
||||
// (would be better to have "one" implementation instead of .. 4 :)
|
||||
//
|
||||
//
|
||||
// Author: Florian Wilkemeyer <fw@f-ws.de>
|
||||
//
|
||||
// Copyright (c) RAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
//
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
#include "../common/showmsg.h"
|
||||
#include "../common/db.h"
|
||||
#include "../common/malloc.h"
|
||||
|
||||
#include "../common/raconf.h"
|
||||
|
||||
#define SECTION_LEN 32
|
||||
#define VARNAME_LEN 64
|
||||
|
||||
struct raconf {
|
||||
DBMap *db;
|
||||
};
|
||||
|
||||
|
||||
struct conf_value{
|
||||
int64 intval;
|
||||
bool bval;
|
||||
double floatval;
|
||||
size_t strval_len; // not includung \0
|
||||
char strval[16];
|
||||
};
|
||||
|
||||
|
||||
|
||||
static struct conf_value *makeValue(const char *key, char *val, size_t val_len){
|
||||
struct conf_value *v;
|
||||
char *p;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(struct conf_value);
|
||||
if(val_len >= sizeof(v->strval))
|
||||
sz += (val_len - sizeof(v->strval) + 1);
|
||||
|
||||
v = (struct conf_value*)aCalloc(1, sizeof(struct conf_value));
|
||||
if(v == NULL){
|
||||
ShowFatalError("raconf: makeValue => Out of Memory while allocating new node.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(v->strval, val, val_len);
|
||||
v->strval[val_len+1] = '\0';
|
||||
v->strval_len = val_len;
|
||||
|
||||
|
||||
// Parse boolean value:
|
||||
if((val_len == 4) && (strncmpi("true", val, 4) == 0))
|
||||
v->bval = true;
|
||||
else if((val_len == 3) && (strncmpi("yes", val, 3) == 0))
|
||||
v->bval = true;
|
||||
else if((val_len == 3) && (strncmpi("oui", val, 3) == 0))
|
||||
v->bval = true;
|
||||
else if((val_len == 2) && (strncmpi("si", val, 2) == 0))
|
||||
v->bval = true;
|
||||
else if((val_len == 2) && (strncmpi("ja", val, 2) == 0))
|
||||
v->bval = true;
|
||||
else if((val_len == 1) && (*val == '1'))
|
||||
v->bval = true;
|
||||
else if((val_len == 5) && (strncmpi("false", val, 5) == 0))
|
||||
v->bval = false;
|
||||
else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
|
||||
v->bval = false;
|
||||
else if((val_len == 3) && (strncmpi("non", val, 3) == 0))
|
||||
v->bval = false;
|
||||
else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
|
||||
v->bval = false;
|
||||
else if((val_len == 4) && (strncmpi("nein", val, 4) == 0))
|
||||
v->bval = false;
|
||||
else if((val_len == 1) && (*val == '0'))
|
||||
v->bval = false;
|
||||
else
|
||||
v->bval = false; // assume false.
|
||||
|
||||
// Parse number
|
||||
// Supported formats:
|
||||
// prefix: 0x hex .
|
||||
// postix: h for hex
|
||||
// b for bin (dual)
|
||||
if( (val_len >= 1 && (val[val_len] == 'h')) || (val_len >= 2 && (val[0] == '0' && val[1] == 'x')) ){//HEX!
|
||||
if(val[val_len] == 'h'){
|
||||
val[val_len]= '\0';
|
||||
v->intval = strtoull(val, NULL, 16);
|
||||
val[val_len] = 'h';
|
||||
}else
|
||||
v->intval = strtoull(&val[2], NULL, 16);
|
||||
}else if( val_len >= 1 && (val[val_len] == 'b') ){ //BIN
|
||||
val[val_len] = '\0';
|
||||
v->intval = strtoull(val, NULL, 2);
|
||||
val[val_len] = 'b';
|
||||
}else if( *val >='0' && *val <= '9'){ // begins with normal digit, so assume its dec.
|
||||
// is it float?
|
||||
bool is_float = false;
|
||||
|
||||
for(p = val; *p != '\0'; p++){
|
||||
if(*p == '.'){
|
||||
v->floatval = strtod(val, NULL);
|
||||
v->intval = (int64) v->floatval;
|
||||
is_float = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_float == false){
|
||||
v->intval = strtoull(val, NULL, 10);
|
||||
v->floatval = (double) v->intval;
|
||||
}
|
||||
}else{
|
||||
// Everything else: lets use boolean for fallback
|
||||
if(v->bval == true)
|
||||
v->intval = 1;
|
||||
else
|
||||
v->intval = 0;
|
||||
}
|
||||
|
||||
return v;
|
||||
}//end: makeValue()
|
||||
|
||||
|
||||
static bool configParse(raconf inst, const char *fileName){
|
||||
FILE *fp;
|
||||
char line[4096];
|
||||
char currentSection[SECTION_LEN];
|
||||
char *p;
|
||||
char c;
|
||||
int linecnt;
|
||||
size_t linelen;
|
||||
size_t currentSection_len;
|
||||
|
||||
fp = fopen(fileName, "r");
|
||||
if(fp == NULL){
|
||||
ShowError("configParse: cannot open '%s' for reading.\n", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Start with empty section:
|
||||
currentSection[0] = '\0';
|
||||
currentSection_len = 0;
|
||||
|
||||
//
|
||||
linecnt = 0;
|
||||
while(1){
|
||||
linecnt++;
|
||||
|
||||
if(fgets(line, sizeof(line), fp) != line)
|
||||
break;
|
||||
|
||||
linelen = strlen(line);
|
||||
p = line;
|
||||
|
||||
// Skip whitespaces from beginning (space and tab)
|
||||
_line_begin_skip_whities:
|
||||
c = *p;
|
||||
if(c == ' ' || c == '\t'){
|
||||
p++;
|
||||
linelen--;
|
||||
goto _line_begin_skip_whities;
|
||||
}
|
||||
|
||||
// Remove linebreaks as (cr or lf) and whitespaces from line end!
|
||||
_line_end_skip_whities_and_breaks:
|
||||
c = p[linelen-1];
|
||||
if(c == '\r' || c == '\n' || c == ' ' || c == '\t'){
|
||||
p[--linelen] = '\0';
|
||||
goto _line_end_skip_whities_and_breaks;
|
||||
}
|
||||
|
||||
// Empty line?
|
||||
// or line starts with comment (commented out)?
|
||||
if(linelen == 0 || (p[0] == '/' && p[1] == '/') || p[0] == ';')
|
||||
continue;
|
||||
|
||||
// Variable names can contain:
|
||||
// A-Za-z-_.0-9
|
||||
//
|
||||
// Sections start with [ .. ] (INI Style)
|
||||
//
|
||||
c = *p;
|
||||
|
||||
// check what we have.. :)
|
||||
if(c == '['){ // got section!
|
||||
// Got Section!
|
||||
// Search for ]
|
||||
char *start = (p+1);
|
||||
|
||||
while(1){
|
||||
++p;
|
||||
c = *p;
|
||||
|
||||
if(c == '\0'){
|
||||
ShowError("Syntax Error: unterminated Section name in %s:%u (expected ']')\n", fileName, linecnt);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}else if(c == ']'){ // closing backet (section name termination)
|
||||
if( (p - start + 1) > (sizeof(currentSection) ) ){
|
||||
ShowError("Syntax Error: Section name in %s:%u is too large (max Supported length: %u chars)\n", fileName, linecnt, sizeof(currentSection)-1);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set section!
|
||||
*p = '\0'; // add termination here.
|
||||
memcpy(currentSection, start, (p-start)+1 ); // we'll copy \0, too! (we replaced the ] backet with \0.)
|
||||
currentSection_len = (p-start);
|
||||
|
||||
break;
|
||||
|
||||
}else if( (c >= '0' && c <= '9') || (c == '-') || (c == ' ') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
|
||||
// skip .. (allowed char / specifier)
|
||||
continue;
|
||||
}else{
|
||||
ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Section name.\n", c, fileName, linecnt, (p-line));
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
}//endwhile: parse section name
|
||||
|
||||
|
||||
}else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
|
||||
// Got variable!
|
||||
// Search for '=' or ':' wich termiantes the name
|
||||
char *start = p;
|
||||
char *valuestart = NULL;
|
||||
size_t start_len;
|
||||
|
||||
while(1){
|
||||
++p;
|
||||
c = *p;
|
||||
|
||||
if(c == '\0'){
|
||||
ShowError("Syntax Error: unterminated Variable name in %s:%u\n", fileName, linecnt);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}else if( (c == '=') || (c == ':') ){
|
||||
// got name termination
|
||||
|
||||
*p = '\0'; // Terminate it so (start) will hold the pointer to the name.
|
||||
|
||||
break;
|
||||
|
||||
}else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
|
||||
// skip .. allowed char
|
||||
continue;
|
||||
}else{
|
||||
ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Variable name.\n", c, fileName, linecnt, (p-line));
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
}//endwhile: parse var name
|
||||
|
||||
start_len = (p-start);
|
||||
if(start_len >= VARNAME_LEN){
|
||||
ShowError("%s:%u Variable length exceeds limit of %u Characters.\n", fileName, linecnt, VARNAME_LEN-1);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}else if(start_len == 0){
|
||||
ShowError("%s:%u Empty Variable name is not allowed.\n", fileName, linecnt);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
valuestart = (p+1);
|
||||
|
||||
|
||||
// Skip whitespace from begin of value (tab and space)
|
||||
_skip_value_begin_whities:
|
||||
c = *valuestart;
|
||||
if(c == ' ' || c == '\t'){
|
||||
valuestart++;
|
||||
goto _skip_value_begin_whities;
|
||||
}
|
||||
|
||||
// Scan for value termination,
|
||||
// wich can be \0 or comment start (// or ; (INI) )
|
||||
//
|
||||
p = valuestart;
|
||||
while(1){
|
||||
c = *p;
|
||||
if(c == '\0'){
|
||||
// Terminated by line end.
|
||||
break;
|
||||
}else if(c == '/' && p[1] == '/'){
|
||||
// terminated by c++ style comment.
|
||||
*p = '\0';
|
||||
break;
|
||||
}else if(c == ';'){
|
||||
// terminated by ini style comment.
|
||||
*p = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
}//endwhile: search var value end.
|
||||
|
||||
|
||||
// Strip whitespaces from end of value.
|
||||
if(valuestart != p){ // not empty!
|
||||
p--;
|
||||
_strip_value_end_whities:
|
||||
c = *p;
|
||||
if(c == ' ' || c == '\t'){
|
||||
*p = '\0';
|
||||
p--;
|
||||
goto _strip_value_end_whities;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
|
||||
// Buildin Hook:
|
||||
if( stricmp(start, "import") == 0){
|
||||
if( configParse(inst, valuestart) != true){
|
||||
ShowError("%s:%u - Import of '%s' failed!\n", fileName, linecnt, valuestart);
|
||||
}
|
||||
}else{
|
||||
// put it to db.
|
||||
struct conf_value *v, *o;
|
||||
char key[ (SECTION_LEN+VARNAME_LEN+1+1) ]; //+1 for delimiter, +1 for termination.
|
||||
size_t section_len;
|
||||
|
||||
if(*currentSection == '\0'){ // empty / none
|
||||
strncpy(key, "<unnamed>",9);
|
||||
section_len = 9;
|
||||
}else{
|
||||
strncpy(key, currentSection, currentSection_len);
|
||||
section_len = currentSection_len;
|
||||
}
|
||||
|
||||
key[section_len] = '.'; // Delim
|
||||
|
||||
strncpy(&key[section_len+1], start, start_len);
|
||||
|
||||
key[section_len + start_len + 1] = '\0';
|
||||
|
||||
|
||||
v = makeValue(key, valuestart, (p-valuestart) );
|
||||
|
||||
// Try to get the old one before
|
||||
o = strdb_get(inst->db, key);
|
||||
if(o != NULL){
|
||||
strdb_remove(inst->db, key);
|
||||
aFree(o); //
|
||||
}
|
||||
|
||||
strdb_put( inst->db, key, v);
|
||||
}
|
||||
|
||||
|
||||
}else{
|
||||
ShowError("Syntax Error: unexpected Character '%c' in %s:%u (offset %u)\n", c, fileName, linecnt, (p-line) );
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
fclose(fp);
|
||||
return true;
|
||||
}//end: configParse()
|
||||
|
||||
|
||||
#define MAKEKEY(dest, section, key) { size_t section_len, key_len; \
|
||||
if(section == NULL || *section == '\0'){ \
|
||||
strncpy(dest, "<unnamed>", 9); \
|
||||
section_len = 9; \
|
||||
}else{ \
|
||||
section_len = strlen(section); \
|
||||
strncpy(dest, section, section_len); \
|
||||
} \
|
||||
\
|
||||
dest[section_len] = '.'; \
|
||||
\
|
||||
key_len = strlen(key); \
|
||||
strncpy(&dest[section_len+1], key, key_len); \
|
||||
dest[section_len + key_len + 1] = '\0'; \
|
||||
}
|
||||
|
||||
|
||||
raconf raconf_parse(const char *file_name){
|
||||
struct raconf *rc;
|
||||
|
||||
rc = aCalloc(1, sizeof(struct raconf) );
|
||||
if(rc == NULL){
|
||||
ShowFatalError("raconf_parse: failed to allocate memory for new handle\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc->db = strdb_alloc(DB_OPT_BASE | DB_OPT_DUP_KEY, 98);
|
||||
//
|
||||
|
||||
if(configParse(rc, file_name) != true){
|
||||
ShowError("Failed to Parse Configuration file '%s'\n", file_name);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}//end: raconf_parse()
|
||||
|
||||
|
||||
void raconf_destroy(raconf rc){
|
||||
DBIterator *iter;
|
||||
struct conf_value *v;
|
||||
|
||||
// Clear all entrys in db.
|
||||
iter = db_iterator(rc->db);
|
||||
for( v = (struct conf_value*)dbi_first(iter); dbi_exists(iter); v = (struct conf_value*)dbi_next(iter) ){
|
||||
aFree(v);
|
||||
}
|
||||
dbi_destroy(iter);
|
||||
|
||||
db_destroy(rc->db);
|
||||
|
||||
aFree(rc);
|
||||
|
||||
}//end: raconf_destroy()
|
||||
|
||||
bool raconf_getbool(raconf rc, const char *section, const char *key, bool _default){
|
||||
char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
||||
struct conf_value *v;
|
||||
|
||||
MAKEKEY(keystr, section, key);
|
||||
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL)
|
||||
return _default;
|
||||
else
|
||||
return v->bval;
|
||||
}//end: raconf_getbool()
|
||||
|
||||
|
||||
float raconf_getfloat(raconf rc,const char *section, const char *key, float _default){
|
||||
char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
||||
struct conf_value *v;
|
||||
|
||||
MAKEKEY(keystr, section, key);
|
||||
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL)
|
||||
return _default;
|
||||
else
|
||||
return (float)v->floatval;
|
||||
}//end: raconf_getfloat()
|
||||
|
||||
|
||||
int64 raconf_getint(raconf rc, const char *section, const char *key, int64 _default){
|
||||
char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
||||
struct conf_value *v;
|
||||
|
||||
MAKEKEY(keystr, section, key);
|
||||
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL)
|
||||
return _default;
|
||||
else
|
||||
return v->intval;
|
||||
|
||||
}//end: raconf_getint()
|
||||
|
||||
|
||||
const char* raconf_getstr(raconf rc, const char *section, const char *key, const char *_default){
|
||||
char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
||||
struct conf_value *v;
|
||||
|
||||
MAKEKEY(keystr, section, key);
|
||||
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL)
|
||||
return _default;
|
||||
else
|
||||
return v->strval;
|
||||
}//end: raconf_getstr()
|
||||
|
||||
|
||||
bool raconf_getboolEx(raconf rc, const char *section, const char *fallback_section, const char *key, bool _default){
|
||||
char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
||||
struct conf_value *v;
|
||||
|
||||
MAKEKEY(keystr, section, key);
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL){
|
||||
|
||||
MAKEKEY(keystr, fallback_section, key);
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL){
|
||||
return _default;
|
||||
}else{
|
||||
return v->bval;
|
||||
}
|
||||
|
||||
}else{
|
||||
return v->bval;
|
||||
}
|
||||
}//end: raconf_getboolEx()
|
||||
|
||||
|
||||
float raconf_getfloatEx(raconf rc,const char *section, const char *fallback_section, const char *key, float _default){
|
||||
char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
||||
struct conf_value *v;
|
||||
|
||||
MAKEKEY(keystr, section, key);
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL){
|
||||
|
||||
MAKEKEY(keystr, fallback_section, key);
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL){
|
||||
return _default;
|
||||
}else{
|
||||
return (float)v->floatval;
|
||||
}
|
||||
|
||||
}else{
|
||||
return (float)v->floatval;
|
||||
}
|
||||
|
||||
}//end: raconf_getfloatEx()
|
||||
|
||||
|
||||
int64 raconf_getintEx(raconf rc, const char *section, const char *fallback_section, const char *key, int64 _default){
|
||||
char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
||||
struct conf_value *v;
|
||||
|
||||
MAKEKEY(keystr, section, key);
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL){
|
||||
|
||||
MAKEKEY(keystr, fallback_section, key);
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL){
|
||||
return _default;
|
||||
}else{
|
||||
return v->intval;
|
||||
}
|
||||
|
||||
}else{
|
||||
return v->intval;
|
||||
}
|
||||
|
||||
}//end: raconf_getintEx()
|
||||
|
||||
|
||||
const char* raconf_getstrEx(raconf rc, const char *section, const char *fallback_section, const char *key, const char *_default){
|
||||
char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
||||
struct conf_value *v;
|
||||
|
||||
MAKEKEY(keystr, section, key);
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL){
|
||||
|
||||
MAKEKEY(keystr, fallback_section, key);
|
||||
v = strdb_get(rc->db, keystr);
|
||||
if(v == NULL){
|
||||
return _default;
|
||||
}else{
|
||||
return v->strval;
|
||||
}
|
||||
|
||||
}else{
|
||||
return v->strval;
|
||||
}
|
||||
|
||||
}//end: raconf_getstrEx()
|
59
src/common/raconf.h
Normal file
59
src/common/raconf.h
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#ifndef _rA_CONF_H_
|
||||
#define _rA_CONF_H_
|
||||
|
||||
#include "../common/cbasetypes.h"
|
||||
|
||||
// rAthena generic configuration file parser
|
||||
//
|
||||
// Config file Syntax is athena style
|
||||
// extended with ini style support (including sections)
|
||||
//
|
||||
// Comments are started with // or ; (ini style)
|
||||
//
|
||||
|
||||
typedef struct raconf *raconf;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a rAthna Configuration file
|
||||
*
|
||||
* @param file_name path to the file to parse
|
||||
*
|
||||
* @returns not NULL incase of success
|
||||
*/
|
||||
raconf raconf_parse(const char *file_name);
|
||||
|
||||
|
||||
/**
|
||||
* Frees a Handle received from raconf_parse
|
||||
*
|
||||
* @param rc - the handle to free
|
||||
*/
|
||||
void raconf_destroy(raconf rc);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value for Section / Key pair, if key not exists returns _default!
|
||||
*
|
||||
*/
|
||||
bool raconf_getbool(raconf rc, const char *section, const char *key, bool _default);
|
||||
float raconf_getfloat(raconf rc,const char *section, const char *key, float _default);
|
||||
int64 raconf_getint(raconf rc, const char *section, const char *key, int64 _default);
|
||||
const char* raconf_getstr(raconf rc, const char *section, const char *key, const char *_default);
|
||||
|
||||
/**
|
||||
* Gets the value for Section / Key pair, but has fallback section option if not found in section,
|
||||
* if not found in both - default gets returned.
|
||||
*
|
||||
*/
|
||||
bool raconf_getboolEx(raconf rc, const char *section, const char *fallback_section, const char *key, bool _default);
|
||||
float raconf_getfloatEx(raconf rc,const char *section, const char *fallback_section, const char *key, float _default);
|
||||
int64 raconf_getintEx(raconf rc, const char *section, const char *fallback_section, const char *key, int64 _default);
|
||||
const char* raconf_getstrEx(raconf rc, const char *section, const char *fallback_section, const char *key, const char *_default);
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -1,3 +1,10 @@
|
||||
//
|
||||
// Basic Threading abstraction (for pthread / win32 based systems)
|
||||
//
|
||||
// Author: Florian Wilkemeyer <fw@f-ws.de>
|
||||
//
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#ifdef WIN32
|
||||
#include "../common/winapi.h"
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#pragma once
|
||||
#ifndef _rA_THREAD_H_
|
||||
#define _rA_THREAD_H_
|
||||
|
Loading…
x
Reference in New Issue
Block a user