/* Mis-connector - user-space router and NATP for misconfigured hosts on LAN.
Allocations.
This file is part of mis-connector.
Copyright ⓒ 2002,2003,2004,2021 Petr Silhavy <petr.silhavy@yandex.com>
SPDX-License-Identifier: GPL-3.0-or-later
Mis-connector 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 2 of the
License, or (at your option) any later version.
Mis-connector 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.
You should have received a copy of the GNU General Public License
along with mis-connector. If not, see <http://www.gnu.org/licenses/>.
*/
#define DEBUG_MALLOC 0
#define DEBUG_MALLOC_SS 0
/*
* 76543210
* XXXXX
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
//#include <asm-generic/bitops.h>
//#include "/v/src/linux-5.9/include/asm-generic/bitops.h"
#include <assert.h>
#include "whackparam.h"
#define ADB_MM_MIN 32
#define ADB_MM_SEG 256 /* max one byte length */
#define MAP_SIZE 0x1000
#define POOL_SIZE 0x100000
static inline int
Adb_ffz(unsigned char c, int need )
{
register int j , found , r ;
for ( j = 0 , found = 0 ; j < 8 ; ++j )
if ( ! ( c & 1 << j )) /* find 1st zero */
{
if ( c < 1 << ( j + 1) && j + need <= 8 ) /* upper bits are clean */
return j ;
for ( r = j ; ++found != need ; )
if ( ( c & 1 << ++j ) || j == 8 )
return EOF ;
return r ;
}
return EOF ;
}
static unsigned char *
Adb_malloc_single_seg(struct sb *sb, unsigned char *new_bits, size_t size)
{
register unsigned char *p ;
register int i /* , bits , found , used = 0 */ ;
unsigned char *map = sb->m ;
register unsigned char needed = 0 , n ;
register int n_bits = ( size / ADB_MM_MIN ) + ( size % ADB_MM_MIN ? 1 : 0 ) ;
int zero_bit ;
unsigned char *max = (void *)map + MAP_SIZE ;
static unsigned char *cache = NULL ;
// unsigned char *old_m ;
for ( i = 0 ; i < n_bits ; ++i )
needed |= 1 << i ;
for ( p = cache ? cache : map ; p < max ; ++p )
if ( *p < 0xff && ( zero_bit = Adb_ffz(*p, n_bits)) != EOF )
{
/* zero_bit = __ffz(*p, n_bits); */
/* if ( zero_bit + n_bits > 8 ) */
/* continue ; */
n = needed << zero_bit ;
if ( ! ( *p & n ))
{
*new_bits = n ;
*p |= n ;
#if DEBUG_MALLOC_SS
printf("size: %d #bits %d needed %d zero %d ffs %d n %d p: %p max: %p\n",
size, n_bits, needed, zero_bit, ffs(*new_bits) , n, p , max);
#endif
if ( zero_bit + n_bits == 8 )
cache = NULL ;
else
cache = p ;
// old_m = map ;
return p ;
}
else abort();
}
fprintf(stderr, "Request size: %zu MM %p map->size %d\n",size, sb->m , MAP_SIZE);
WARNING("Segment full");
return NULL ;
}
/* bits = 1 + size / 32 ; */ /* BUG */
#if 0
bm_max = 1 << n_bits ;
zero = find_first_zero_bit(map, m->map_size) ;
for ( p = (void *)map + ( zero / 8) ;
p < map + m->map_size ;
++p )
if ( *p < bm_max ) /* ( *p != 255 ) */
/* if ( *p != 255 ) */
{
for ( n = needed , i = n_bits ; i < 8 ; ++i , n <<= 1 )
{
if ( ! ( *p & n ) )
{
*new_bits = n ;
*p |= n ;
#if DEBUG_MALLOC
printf("size: %d #bits %d max %d needed %d ffs %d n %d\n",size, n_bits, bm_max, needed,ffs(*new_bits) , n);
#endif
return p ;
}
}
}
fprintf(stderr, "Request size: %d MM %p map->size %d\n",size, m , m->map_size);
msg("Segment full", "");
return NULL ;
}
#endif /* 0 */
static inline unsigned char *
Adb_to_real_addr(unsigned char *bit_map_addr, unsigned char new_bits , unsigned char *map, unsigned char *pool)
{
return ( pool +
( bit_map_addr - (unsigned char *)map ) * 256 +
( ffs((int )new_bits) - 1 ) * 32 ) ;
}
#if 0
#define MALLOC_MSG() printf("size: %2.2d m->map: %p bitaddr %p new :%x real: %p\n", size, \
(void *)m + ADB_MAGIC_SIZE , \
(void *)bit_map_addr, \
new_bits,\
real_addr )
#else /* 0 */
#define MALLOC_MSG() fprintf(stderr,"Malloc size: %2.2d real: %p %s\n", size, \
real_addr , fce );
#endif /* 0 */
/* unsigned char * */
void *
Bm_alloc0(struct sb *sb, size_t size, const char *fce)
{
void *p = Bm_alloc(sb, size, fce);
if (p)
memset(p, 0 , size );
return p ;
}
/* unsigned char * */
void *
Bm_alloc(struct sb *sb, size_t size, const char *fce)
{
unsigned char *bit_map_addr , *real_addr ;
unsigned char new_bits = 0 ;
void *map = sb->m ;
void *pool = sb->pool ;
if ( size < ADB_MM_SEG )
{
if ( ! ( bit_map_addr = Adb_malloc_single_seg(sb, &new_bits, size) ) )
{
WARNING("Out of space");
return NULL ;
}
real_addr = Adb_to_real_addr(bit_map_addr, new_bits , map, pool );
#if DEBUG_MALLOC
MALLOC_MSG();
fflush(stdout);
#endif
if (( real_addr + size ) > ((unsigned char *) sb->pool + POOL_SIZE) )
BUG("Out of segment - real_addr + size > m + m->size, %p > %p",
real_addr + size, ((unsigned char *) sb->pool + POOL_SIZE));
#if DEBUG_MALLOC
/* DEBUG ******************************* */
{
char *p ;
for ( p = real_addr ; p < (char *)real_addr + size ; ++p )
*p = 0 ;
}
#endif /* 0 */
return real_addr ;
}
else
{
if ( size == ADB_MM_SEG )
{
if ( ! ( bit_map_addr = memchr(map, 0, MAP_SIZE )) )
goto Fail ;
new_bits = 0xff ;
*bit_map_addr = new_bits ;
real_addr = Adb_to_real_addr(bit_map_addr, new_bits , map, pool );
#if DEBUG_MALLOC
MALLOC_MSG();
#endif
if (( real_addr + size ) > ((unsigned char *) sb->pool + POOL_SIZE) )
BUG("Out of segment - real_addr + size > m + m->size, %p > %p",
real_addr + size, ((unsigned char *) sb->pool + POOL_SIZE));
return real_addr ;
}
else
{
int n_bytes = size / ADB_MM_SEG + 1 ;
int n_bits = ( size % ADB_MM_SEG ) / 32 + 1 ;
int i ;
size_t j ;
unsigned char *p ;
unsigned char needle[n_bytes] ;
memset(needle, 0 , n_bytes );
if ( ! ( bit_map_addr = memmem(map, MAP_SIZE , needle, n_bytes )))
goto Fail ;
for ( i = 0 ; i < n_bits ; ++i )
new_bits |= 1 << i ;
for ( i = 0 , p = bit_map_addr ; i < n_bytes ; ++i )
*p++ = 0xff ;
/* *p = new_bits ; */
real_addr = Adb_to_real_addr(bit_map_addr, 1 , map, pool ); /* 1 is for ffs() */
for ( j = 0 ; j < size ; ++j )
if ( real_addr[j] != 0xbb && real_addr[j] != 0) /* as free left it */
abort();
memset(real_addr,0, size);
#if DEBUG_MALLOC
MALLOC_MSG();
#endif
assert (( real_addr + size ) < ((unsigned char *) sb->pool + POOL_SIZE));
return real_addr ;
}
}
/* for ( p */
Fail:
fprintf(stderr,"%s() Segment full @ %p \n", fce , sb->m );
return NULL ;
}
/* real = ( bit_map_addr - m->map ) * 256 + bits * 32 + stuff */
/* size: 48 m->map: 0x30002000 bitaddr 0x30003758 new :6 real: 0x30187820 */
void
__Adb_free(struct sb *sb, unsigned char *real_addr , size_t size,
const char *fce)
{
unsigned char *bit_map_addr , new_bits = 0 ;
unsigned char *map = (void *) sb->m ;
unsigned char *pool = (void *) sb->pool ;
int first_bit_helper = ( real_addr - pool ) % 256 / 32 ;
int bits = ( size / ADB_MM_MIN ) + ( size % ADB_MM_MIN ? 1 : 0 ) /* 1 + size / 32 */ ;
bit_map_addr = ( real_addr - pool ) / 256 + map ;
if ( size < ADB_MM_SEG )
{
while ( bits-- )
{
new_bits |= 1 << first_bit_helper++ ;
}
#if DEBUG_MALLOC
printf("free() size: %2.2d m->map: %p bitaddr %p new :%x real %p *bitmap: %x\n",
size,map,
bit_map_addr, new_bits, real_addr, *bit_map_addr );
#endif /* 0 */
*bit_map_addr &= ~new_bits ;
#if DEBUG_MALLOC
printf("post-free() size: %2.2d m->map: %p bitaddr %p new :%x real %p *bitmap: %x\n",size,map,
bit_map_addr, new_bits, real_addr, *bit_map_addr );
#endif /* 0 */
}
else if ( size == ADB_MM_SEG )
{
new_bits = 0xff ;
*bit_map_addr &= ~new_bits ;
}
else /* size > ADB_MM_SEG */
{
int n_bytes = size / ADB_MM_SEG + 1 ;
int n_bits = ( size % ADB_MM_SEG ) / 32 + 1 ;
int i ;
unsigned char *p ;
for ( i = 0 ; i < n_bits ; ++i )
new_bits |= 1 << i ;
for ( i = 0 , p = bit_map_addr ; i < n_bytes ; ++i )
*p++ = 0x0 ;/* *p++ &= ~0xff ; */
/* *p &= ~new_bits ; */
}
fprintf(stderr,"Free size: %zu %p %s\n", size , real_addr, fce);
memset(real_addr,0xbb, size);
}
void *
bm_alloc(struct sb *sb, size_t size, const char *fce)
{
void *p = Bm_alloc(sb, size, fce);
if ( p == NULL )
{
kill(child, SIGTERM);
exit(1);
}
return p ;
}
void *
bm_alloc0(struct sb *sb, size_t size, const char *fce)
{
void *p = bm_alloc(sb, size, fce);
if (p)
memset(p, 0 , size );
return p ;
}