diff --git a/engine/common/zone.c b/engine/common/zone.c index 6b9cae24..85394e22 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -18,6 +18,15 @@ GNU General Public License for more details. #define MEMHEADER_SENTINEL1 0xDEADF00D #define MEMHEADER_SENTINEL2 0xDF +#ifdef XASH_CUSTOM_SWAP +#include "platform/swap/swap.h" +#define Q_malloc SWAP_Malloc +#define Q_free SWAP_Free +#else +#define Q_malloc malloc +#define Q_free free +#endif + typedef struct memheader_s { struct memheader_s *next; // next and previous memheaders in chain belonging to pool @@ -58,7 +67,7 @@ void *_Mem_Alloc( byte *poolptr, size_t size, qboolean clear, const char *filena // big allocations are not clumped pool->realsize += sizeof( memheader_t ) + size + sizeof( int ); - mem = (memheader_t *)malloc( sizeof( memheader_t ) + size + sizeof( int )); + mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( int )); if( mem == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline ); mem->filename = filename; @@ -74,7 +83,8 @@ void *_Mem_Alloc( byte *poolptr, size_t size, qboolean clear, const char *filena mem->prev = NULL; pool->chain = mem; if( mem->next ) mem->next->prev = mem; - if( clear ) memset((void *)((byte *)mem + sizeof( memheader_t )), 0, mem->size ); + //if( clear ) + memset((void *)((byte *)mem + sizeof( memheader_t )), 0, mem->size ); return (void *)((byte *)mem + sizeof( memheader_t )); } @@ -128,7 +138,7 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline pool->totalsize -= mem->size; pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( int ); - free( mem ); + Q_free( mem ); } void _Mem_Free( void *data, const char *filename, int fileline ) @@ -166,7 +176,7 @@ byte *_Mem_AllocPool( const char *name, const char *filename, int fileline ) { mempool_t *pool; - pool = (mempool_t *)malloc( sizeof( mempool_t )); + pool = (mempool_t *)Q_malloc( sizeof( mempool_t )); if( pool == NULL ) Sys_Error( "Mem_AllocPool: out of memory (allocpool at %s:%i)\n", filename, fileline ); memset( pool, 0, sizeof( mempool_t )); @@ -203,7 +213,7 @@ void _Mem_FreePool( byte **poolptr, const char *filename, int fileline ) while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline ); // free the pool itself memset( pool, 0xBF, sizeof( mempool_t )); - free( pool ); + Q_free( pool ); *poolptr = NULL; } } diff --git a/engine/platform/swap/kmalloc.c b/engine/platform/swap/kmalloc.c new file mode 100644 index 00000000..6e205b4f --- /dev/null +++ b/engine/platform/swap/kmalloc.c @@ -0,0 +1,488 @@ +/* $NetBSD: malloc.c,v 1.8 1997/04/07 03:12:14 christos Exp $ */ + +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91"; +#else +static char *rcsid = "$NetBSD: malloc.c,v 1.8 1997/04/07 03:12:14 christos Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * malloc.c (Caltech) 2/21/82 + * Chris Kingsley, kingsley@cit-20. + * + * This is a very fast storage allocator. It allocates blocks of a small + * number of different sizes, and keeps free lists of each size. Blocks that + * don't exactly fit are passed up to the next larger size. In this + * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. + * This is designed for use in a virtual memory environment. + */ + +#include +#include +#include +#include "platform/swap/swap.h" +/* #define MSTATS 1 */ + +#ifndef _WIN32 +#include +#define NULL 0 +#else + +#define u_char unsigned char +#define u_long unsigned long +#define getpagesize() 4096 +#define caddr_t size_t +#define bcopy(a,b,c) memcpy(b,a,c) + +#endif + + +static void morecore(int); +/* + * The overhead on a block is at least 4 bytes. When free, this space + * contains a pointer to the next free block, and the bottom two bits must + * be zero. When in use, the first byte is set to MAGIC, and the second + * byte is the size index. The remaining bytes are for alignment. + * If range checking is enabled then a second word holds the size of the + * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC). + * The order of elements is critical: ov_magic must overlay the low order + * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern. + */ +union overhead { + union overhead *ov_next; /* when free */ + struct { + u_char ovu_magic; /* magic number */ + u_char ovu_index; /* bucket # */ +#ifdef RCHECK + u_short ovu_rmagic; /* range magic number */ + u_long ovu_size; /* actual block size */ +#endif + } ovu; +#define ov_magic ovu.ovu_magic +#define ov_index ovu.ovu_index +#define ov_rmagic ovu.ovu_rmagic +#define ov_size ovu.ovu_size +}; + +static int findbucket( union overhead *freep, +int srchlen); +#define MAGIC 0xef /* magic # on accounting info */ +#define RMAGIC 0x5555 /* magic # on range info */ + +#ifdef RCHECK +#define RSLOP sizeof (u_short) +#else +#define RSLOP 0 +#endif + +/* + * nextf[i] is the pointer to the next free block of size 2^(i+3). The + * smallest allocatable block is 8 bytes. The overhead information + * precedes the data area returned to the user. + */ +#define NBUCKETS 30 +static union overhead *nextf[NBUCKETS]; +/* extern char *sbrk(); */ + +static int pagesz; /* page size */ +static int pagebucket; /* page size bucket */ + +#ifdef MSTATS +/* + * nmalloc[i] is the difference between the number of mallocs and frees + * for a given block size. + */ +static unsigned int nmalloc[NBUCKETS]; +#include +#endif + +#if defined(DEBUG) || defined(RCHECK) +#define ASSERT(p) if (!(p)) botch(__STRING(p)) +#include /* */ +static +botch(s) + char *s; +{ + fprintf(stderr, "\r\nassertion botched: %s\r\n", s); + (void) fflush(stderr); /* just in case user buffered it */ + abort(); +} +#else +#define ASSERT(p) +#endif + +#include + +/* */ +/* malloc */ +/* malloc */ +void * SWAP_Malloc( size_t nbytes ) +{ + register union overhead *op; + register int bucket; + register long n; + register unsigned amt; + + /* + * First time malloc is called, setup page size and + * align break pointer so all data will be page aligned. + */ + if (pagesz == 0) { + pagesz = n = getpagesize(); + op = (union overhead *)SWAP_Sbrk( 0 ); + n = n - sizeof (*op) - ((long)op & (n - 1)); + if (n < 0) + n += pagesz; + if (n) { + if (SWAP_Sbrk(n) == (char *)-1) { + return (NULL); + } + } + bucket = 0; + amt = 8; + while (pagesz > amt) { + amt <<= 1; + bucket++; + } + pagebucket = bucket; + } + /* + * Convert amount of memory requested into closest block size + * stored in hash buckets which satisfies request. + * Account for space used per block for accounting. + */ + if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) { +#ifndef RCHECK + amt = 8; /* size of first bucket */ + bucket = 0; +#else + amt = 16; /* size of first bucket */ + bucket = 1; +#endif + n = -((long)sizeof (*op) + RSLOP); + } else { + amt = pagesz; + bucket = pagebucket; + } + while (nbytes > amt + n) { + amt <<= 1; + if (amt == 0) { + return (NULL); + } + bucket++; + } + /* + * If nothing in hash bucket right now, + * request more memory from the system. + */ + if ((op = nextf[bucket]) == NULL) { + morecore(bucket); + if ((op = nextf[bucket]) == NULL) { + return (NULL); + } + } + /* remove from linked list */ + nextf[bucket] = op->ov_next; + op->ov_magic = MAGIC; + op->ov_index = bucket; +#ifdef MSTATS + nmalloc[bucket]++; +#endif +#ifdef RCHECK + /* + * Record allocated size of block and + * bound space with magic numbers. + */ + op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); + op->ov_rmagic = RMAGIC; + *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; +#endif + return ((char *)(op + 1)); +} + +void * SWAP_Calloc(size_t nelem, size_t elsize) { + void * ptr = SWAP_Malloc (nelem * elsize); + // Zero out the malloc'd block. + memset (ptr, 0, nelem * elsize); + return ptr; +} + + +/* + * Allocate more memory to the indicated bucket. + */ +static void +morecore(int bucket) +{ + register union overhead *op; + register long sz; /* size of desired block */ + long amt; /* amount to allocate */ + int nblks; /* how many blocks we get */ + + /* + * sbrk_size <= 0 only for big, FLUFFY, requests (about + * 2^30 bytes on a VAX, I think) or for a negative arg. + */ + sz = 1 << (bucket + 3); +#ifdef DEBUG + ASSERT(sz > 0); +#else + if (sz <= 0) + return; +#endif + if (sz < pagesz) { + amt = pagesz; + nblks = amt / sz; + } else { + amt = sz + pagesz; + nblks = 1; + } + op = (union overhead *)SWAP_Sbrk(amt); + /* no more room! */ + if ((long)op == -1) + return; + /* + * Add new memory allocated to that on + * free list for this hash bucket. + */ + nextf[bucket] = op; + while (--nblks > 0) { + op->ov_next = (union overhead *)((caddr_t)op + sz); + op = (union overhead *)((caddr_t)op + sz); + } +} +/* */ +/* free */ +/* free */ +void SWAP_Free( void *cp) + +{ + register long size; + register union overhead *op; + + if (cp == NULL) + return; + op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); +#ifdef DEBUG + ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ +#else + if (op->ov_magic != MAGIC) + return; /* sanity */ +#endif +#ifdef RCHECK + ASSERT(op->ov_rmagic == RMAGIC); + ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); +#endif + size = op->ov_index; + ASSERT(size < NBUCKETS); + op->ov_next = nextf[size]; /* also clobbers ov_magic */ + nextf[size] = op; +#ifdef MSTATS + nmalloc[size]--; +#endif +} + + +/* EDB: added size lookup */ + +size_t SWAP_MallocUsableSize(void * cp) +{ + register long size; + register union overhead *op; + + if (cp == NULL) + return 0; + op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); +#ifdef DEBUG + ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ +#else + if (op->ov_magic != MAGIC) + return 0; /* sanity */ +#endif +#ifdef RCHECK + ASSERT(op->ov_rmagic == RMAGIC); + ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); +#endif + return op->ov_index; +} +/* End EDB */ + + +/* + * When a program attempts "storage compaction" as mentioned in the + * old malloc man page, it realloc's an already freed block. Usually + * this is the last block it freed; occasionally it might be farther + * back. We have to search all the free lists for the block in order + * to determine its bucket: 1st we make one pass thru the lists + * checking only the first block in each; if that fails we search + * ``realloc_srchlen'' blocks in each list for a match (the variable + * is extern so the caller can modify it). If that fails we just copy + * however many bytes was given to realloc() and hope it's not huge. + */ +int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ + +/* */ +/* realloc */ +/* realloc */ +void * +SWAP_Realloc( void *cp, +size_t nbytes) + +{ + register u_long onb; + register long i; + union overhead *op; + char *res; + int was_alloced = 0; + + if (cp == NULL) + return (SWAP_Malloc(nbytes)); + if (nbytes == 0) { + SWAP_Free (cp); + return NULL; + } + op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); + if (op->ov_magic == MAGIC) { + was_alloced++; + i = op->ov_index; + } else { + /* + * Already free, doing "compaction". + * + * Search for the old block of memory on the + * free list. First, check the most common + * case (last element free'd), then (this failing) + * the last ``realloc_srchlen'' items free'd. + * If all lookups fail, then assume the size of + * the memory block being realloc'd is the + * largest possible (so that all "nbytes" of new + * memory are copied into). Note that this could cause + * a memory fault if the old area was tiny, and the moon + * is gibbous. However, that is very unlikely. + */ + if ((i = findbucket(op, 1)) < 0 && + (i = findbucket(op, realloc_srchlen)) < 0) + i = NBUCKETS; + } + onb = 1 << (i + 3); + if (onb < pagesz) + onb -= sizeof (*op) + RSLOP; + else + onb += pagesz - sizeof (*op) - RSLOP; + /* avoid the copy if same size block */ + if (was_alloced) { + if (i) { + i = 1 << (i + 2); + if (i < pagesz) + i -= sizeof (*op) + RSLOP; + else + i += pagesz - sizeof (*op) - RSLOP; + } + if (nbytes <= onb && nbytes > i) { +#ifdef RCHECK + op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); + *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; +#endif + return(cp); + } else + SWAP_Free(cp); + } + if ((res = SWAP_Malloc(nbytes)) == NULL) + return (NULL); + if (cp != res) /* common optimization if "compacting" */ + bcopy(cp, res, (nbytes < onb) ? nbytes : onb); + return (res); +} + +/* + * Search ``srchlen'' elements of each free list for a block whose + * header starts at ``freep''. If srchlen is -1 search the whole list. + * Return bucket number, or -1 if not found. + */ +static int +findbucket( union overhead *freep, +int srchlen) + +{ + register union overhead *p; + register int i, j; + + for (i = 0; i < NBUCKETS; i++) { + j = 0; + for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { + if (p == freep) + return (i); + j++; + } + } + return (-1); +} + +#ifdef MSTATS +/* + * mstats - print out statistics about malloc + * + * Prints two lines of numbers, one showing the length of the free list + * for each size category, the second showing the number of mallocs - + * frees for each size category. + */ +/* */ +mstats(void) +{ + register int i, j; + register union overhead *p; + int totfree = 0, + totused = 0; + + fprintf(stderr, "Memory allocation statistics\nfree:\t"); + for (i = 0; i < NBUCKETS; i++) { + for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) + ; + fprintf(stderr, " %d", j); + totfree += j * (1 << (i + 3)); + } + fprintf(stderr, "\nused:\t"); + for (i = 0; i < NBUCKETS; i++) { + fprintf(stderr, " %d", nmalloc[i]); + totused += nmalloc[i] * (1 << (i + 3)); + } + fprintf(stderr, "\n\tTotal in use: %d, total free: %d, total allocated: %d\n", + totused, totfree, totused + totfree); +} +#endif + diff --git a/engine/platform/swap/sbrk.c b/engine/platform/swap/sbrk.c new file mode 100644 index 00000000..76af1990 --- /dev/null +++ b/engine/platform/swap/sbrk.c @@ -0,0 +1,108 @@ +/* +sbrk.c - swap memory allocation +Copyright (C) 2019 mittorn + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define _GNU_SOURCE +#include +#include "platform/swap/swap.h" +#include "string.h" + +#ifndef XASH_DEFAULT_SWAP_PATH +#define XASH_DEFAULT_SWAP_PATH "/tmp/xash3d-swap" +#endif +#define PAGE_SIZE 4096 +static struct sbrk_state_s +{ + void *top; + int fd; + size_t size; + size_t prealloc; + pid_t pid; +} s; + +static void SWAP_Initialize(void) +{ + char *path; + char *prealloc = getenv("SWAP_SIZE"); + int fd; + + if( s.top ) + return; + + path = getenv("SWAP_PATH"); + if( !path ) + path = XASH_DEFAULT_SWAP_PATH; + fd = open( path, O_CREAT|O_RDWR, 0600 ); + + if( prealloc ) s.prealloc = atoi(prealloc); + else s.prealloc = 128*1024*1024; + s.prealloc &= ~(PAGE_SIZE - 1); + + s.fd = fd; + ftruncate( fd, s.prealloc ); + s.top = mmap( 0, s.prealloc, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ); + + // space will be freed on exit + //unlink(path); +} + +void *SWAP_Sbrk(size_t size) +{ + char buf[64]; + SWAP_Initialize(); + + if( size == 0 ) + return s.top; + else if( size > 0 ) + { + void *res; + + //write(1, buf, snprintf(buf, 32, "allocating %d\n", size) ); + res = s.top; + s.size += size; + s.top = res + size; + if( s.size + size > s.prealloc ) + return (void*)-1; + + memset( res, 0, size ); + return res; + + } + else + { + void *res = s.top; + + if( -size > s.size ) + res = (void*)-1; + else + { + s.top += size; + s.size += size; + //write(1, buf, snprintf(buf, 32, "freed %d\n", -size) ); + } + + return res; + } +} + + diff --git a/engine/platform/swap/swap.h b/engine/platform/swap/swap.h new file mode 100644 index 00000000..342ca8e9 --- /dev/null +++ b/engine/platform/swap/swap.h @@ -0,0 +1,21 @@ +/* +sbrk_swap.h - swap memory allocation +Copyright (C) 2019 mittorn + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +//#include +void *SWAP_Sbrk( size_t size ); +void *SWAP_Malloc( size_t size ); +void *SWAP_Calloc( size_t nelem, size_t size ); +void SWAP_Free( void *cp ); +void *SWAP_Realloc( void *cp, size_t size ); +size_t SWAP_MallocUsableSize( void * cp );