mirror of
git://git.psyced.org/git/psyclpc
synced 2024-08-15 03:20:16 +00:00
1074 lines
29 KiB
C
1074 lines
29 KiB
C
|
/*---------------------------------------------------------------------------
|
||
|
* Bitstring Efuns.
|
||
|
*
|
||
|
*---------------------------------------------------------------------------
|
||
|
* Bitstrings are a compact yet portable way to store a large number of
|
||
|
* bitflags in one value. The bits are stored by encoding them in
|
||
|
* character, and then keeping the characters around as normal strings.
|
||
|
*
|
||
|
* Each character contains 6 bits. So you can store a value
|
||
|
* between 0 and 63 ( 2^6=64) in one character. Starting
|
||
|
* character is the blank character " " which has the value 0.
|
||
|
* The first charcter in the string is the one with the lowest
|
||
|
* bits (0-5).
|
||
|
*---------------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
#include "driver.h"
|
||
|
#include "typedefs.h"
|
||
|
|
||
|
|
||
|
#include "bitstrings.h"
|
||
|
|
||
|
#include "interpret.h"
|
||
|
#include "mstrings.h"
|
||
|
#include "simulate.h"
|
||
|
#include "svalue.h"
|
||
|
#include "xalloc.h"
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
#if 0
|
||
|
|
||
|
#include <stdio.h>
|
||
|
|
||
|
static void
|
||
|
printbits (string_t *bstr)
|
||
|
|
||
|
/* Auxiliary function for debugging: print the data from a bitstring */
|
||
|
|
||
|
{
|
||
|
long len = (long)mstrsize(bstr);
|
||
|
char *cp = get_txt(bstr);
|
||
|
|
||
|
while (len > 0)
|
||
|
{
|
||
|
int bit;
|
||
|
|
||
|
for (bit = 0; bit < 6; bit++)
|
||
|
{
|
||
|
if ((*cp-' ') & (1 << bit)) putchar('1'); else putchar('.');
|
||
|
}
|
||
|
if (len > 1) putchar(' ');
|
||
|
len--;
|
||
|
cp++;
|
||
|
}
|
||
|
} /* printbits() */
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static INLINE p_int
|
||
|
last_bit (string_t *str)
|
||
|
|
||
|
/* Return the number of the last set bit in bitstring <str>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mp_int pos;
|
||
|
long len;
|
||
|
char * cstr;
|
||
|
int c;
|
||
|
|
||
|
pos = -1;
|
||
|
|
||
|
/* Get the arguments */
|
||
|
cstr = get_txt(str);
|
||
|
len = (long)mstrsize(str);
|
||
|
|
||
|
/* First, find the last non-zero character */
|
||
|
c = 0;
|
||
|
while (len-- > 0 && (c = cstr[len]) == ' ') NOOP;
|
||
|
|
||
|
if (len >= 0)
|
||
|
{
|
||
|
int pattern;
|
||
|
|
||
|
/* Found a character, now determine the bit position */
|
||
|
c -= ' ';
|
||
|
pos = 6 * len + 5;
|
||
|
for ( pattern = 1 << 5
|
||
|
; pattern && !(c & pattern)
|
||
|
; pattern >>= 1, pos--)
|
||
|
NOOP;
|
||
|
}
|
||
|
|
||
|
return pos;
|
||
|
} /* last_bit() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_clear_bit (svalue_t *sp)
|
||
|
|
||
|
/* EFUN clear_bit()
|
||
|
*
|
||
|
* string clear_bit(string str, int n)
|
||
|
*
|
||
|
* Return the new string where bit n is cleared in string str.
|
||
|
* Note that the old string str is not modified.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char *str;
|
||
|
string_t *new;
|
||
|
size_t len, ind, bitnum;
|
||
|
svalue_t *strp;
|
||
|
|
||
|
/* Get the arguments */
|
||
|
bitnum = (size_t)sp->u.number;
|
||
|
if (sp->u.number < 0)
|
||
|
errorf("clear_bit: negative bit number: %ld\n", (long)sp->u.number);
|
||
|
if (bitnum > MAX_BITS)
|
||
|
errorf("clear_bit: too big bit number: %ld\n", (long)bitnum);
|
||
|
sp = strp = sp-1;
|
||
|
|
||
|
len = mstrsize(strp->u.str);
|
||
|
ind = bitnum/6;
|
||
|
if (ind >= len)
|
||
|
{
|
||
|
/* Bits beyond the current end of the string are assumged to
|
||
|
* be cleared anyway. Therefore, return the argument unmodified.
|
||
|
*/
|
||
|
return sp;
|
||
|
}
|
||
|
|
||
|
memsafe(new = unshare_mstring(strp->u.str), mstrsize(strp->u.str)
|
||
|
, "new bitstring");
|
||
|
strp->u.str = new;
|
||
|
str = get_txt(strp->u.str);
|
||
|
|
||
|
if (str[ind] > 0x3f + ' ' || str[ind] < ' ')
|
||
|
errorf("Illegal bit pattern in clear_bit character %ld\n", (long)ind);
|
||
|
|
||
|
str[ind] = (char)(((str[ind] - ' ') & ~(1 << (bitnum % 6))) + ' ');
|
||
|
|
||
|
return sp;
|
||
|
} /* f_clear_bit() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_set_bit (svalue_t *sp)
|
||
|
|
||
|
/* EFUN set_bit()
|
||
|
*
|
||
|
* string set_bit(string str, int n)
|
||
|
*
|
||
|
* Return the new string where bit n is set in string str. Note
|
||
|
* that the old string str is not modified.
|
||
|
*
|
||
|
* The new string will automatically be extended if needed.
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char *str;
|
||
|
size_t len, ind, bitnum;
|
||
|
svalue_t *strp;
|
||
|
|
||
|
bitnum = (size_t)sp->u.number;
|
||
|
if (sp->u.number < 0)
|
||
|
errorf("set_bit: negative bit number: %ld\n", (long)sp->u.number);
|
||
|
if (bitnum > MAX_BITS)
|
||
|
errorf("set_bit: too big bit number: %ld\n", (long)bitnum);
|
||
|
sp = strp = sp-1;
|
||
|
|
||
|
len = mstrsize(strp->u.str);
|
||
|
ind = bitnum/6;
|
||
|
|
||
|
if (ind < len)
|
||
|
{
|
||
|
string_t *new;
|
||
|
|
||
|
memsafe(new = unshare_mstring(strp->u.str), mstrsize(strp->u.str)
|
||
|
, "new bitstring");
|
||
|
strp->u.str = new;
|
||
|
str = get_txt(strp->u.str);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
string_t *new;
|
||
|
|
||
|
(void)ref_mstring(strp->u.str); /* In case resize_ fails */
|
||
|
memsafe(new = resize_mstring(strp->u.str, ind+1), ind+1
|
||
|
, "new bitstring");
|
||
|
free_mstring(strp->u.str);
|
||
|
strp->u.str = new;
|
||
|
str = get_txt(strp->u.str);
|
||
|
for ( ; len <= ind; len++)
|
||
|
str[len] = ' ';
|
||
|
}
|
||
|
|
||
|
if (str[ind] > 0x3f + ' ' || str[ind] < ' ')
|
||
|
errorf("Illegal bit pattern in set_bit character %ld\n", (long)ind);
|
||
|
|
||
|
str[ind] = (char)(((str[ind] - ' ') | 1 << (bitnum % 6) ) + ' ');
|
||
|
sp = strp;
|
||
|
|
||
|
return sp;
|
||
|
} /* f_set_bit() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_test_bit (svalue_t *sp)
|
||
|
|
||
|
/* EFUN test_bit()
|
||
|
*
|
||
|
* int test_bit(string str, int n)
|
||
|
*
|
||
|
* Return 0 or 1 of bit n was set in string str.
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
size_t len;
|
||
|
int bitnum;
|
||
|
|
||
|
bitnum = sp->u.number;
|
||
|
|
||
|
if (bitnum < 0)
|
||
|
errorf("test_bit: negative bit number: %ld\n", (long)bitnum);
|
||
|
|
||
|
len = mstrsize(sp[-1].u.str);
|
||
|
if ((size_t)bitnum/6 >= len)
|
||
|
{
|
||
|
sp--;
|
||
|
free_string_svalue(sp);
|
||
|
put_number(sp, 0);
|
||
|
return sp;
|
||
|
}
|
||
|
|
||
|
if ( (get_txt((sp-1)->u.str)[bitnum/6] - ' ')
|
||
|
& 1 << (bitnum % 6) )
|
||
|
{
|
||
|
sp--;
|
||
|
free_string_svalue(sp);
|
||
|
put_number(sp, 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sp--;
|
||
|
free_string_svalue(sp);
|
||
|
put_number(sp, 0);
|
||
|
}
|
||
|
|
||
|
return sp;
|
||
|
} /* f_test_bit() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static svalue_t *
|
||
|
binop_bits (svalue_t *sp, int instr)
|
||
|
|
||
|
/* IMPLEMENTATION or_bits(), and_bits(), xor_bits()
|
||
|
*
|
||
|
* string or_bits(string str1, string str2)
|
||
|
* string and_bits(string str1, string str2)
|
||
|
* string xor_bits(string str1, string str2)
|
||
|
*
|
||
|
* Perform a binary operation <instr> on the bitstrings <str1> and <str2>
|
||
|
* and return the resulting string.
|
||
|
*
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
#define INSTR_OR_BITS 0
|
||
|
#define INSTR_AND_BITS 1
|
||
|
#define INSTR_XOR_BITS 2
|
||
|
|
||
|
{
|
||
|
size_t len1, len2, arg_len;
|
||
|
string_t *result, *arg;
|
||
|
char *restxt, *argtxt;
|
||
|
Bool use_short; /* TRUE for AND: use shorter string for result */
|
||
|
|
||
|
use_short = (instr == INSTR_AND_BITS);
|
||
|
|
||
|
/* Get the arguments.
|
||
|
* Sort the two arguments in shorter and longer string.
|
||
|
* We will try to modify one of the two strings in-place.
|
||
|
*/
|
||
|
result = NULL;
|
||
|
len1 = mstrsize(sp[-1].u.str);
|
||
|
len2 = mstrsize(sp->u.str);
|
||
|
|
||
|
if ((len1 >= len2 && !use_short)
|
||
|
|| (len1 < len2 && use_short)
|
||
|
)
|
||
|
{
|
||
|
/* AND: sp-1 is the shorter result; sp the longer argument
|
||
|
* else: sp-1 is the longer result; sp the shorter argument
|
||
|
*/
|
||
|
|
||
|
arg = sp->u.str;
|
||
|
memsafe(result = unshare_mstring(sp[-1].u.str), mstrsize(sp[-1].u.str)
|
||
|
, "new bitstring");
|
||
|
put_number(sp-1, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* AND: sp is the shorter result; sp-1 the longer argument
|
||
|
* else: sp is the longer result; sp-1 the shorter argument
|
||
|
*/
|
||
|
arg = (sp-1)->u.str;
|
||
|
memsafe(result = unshare_mstring(sp->u.str), mstrsize(sp->u.str)
|
||
|
, "new bitstring");
|
||
|
put_number(sp, 0);
|
||
|
}
|
||
|
|
||
|
/* Now perform the operation. */
|
||
|
|
||
|
restxt = get_txt(result);
|
||
|
argtxt = get_txt(arg);
|
||
|
|
||
|
arg_len = (len2 > len1) ? len1 : len2;
|
||
|
while (arg_len-- != 0)
|
||
|
{
|
||
|
char c1, c2;
|
||
|
|
||
|
c1 = restxt[arg_len];
|
||
|
c2 = argtxt[arg_len];
|
||
|
if (c1 > 0x3f + ' ' || c1 < ' ')
|
||
|
{
|
||
|
free_mstring(result);
|
||
|
errorf("Illegal bit pattern in %s character %d\n"
|
||
|
, instr == INSTR_OR_BITS
|
||
|
? "or_bits()"
|
||
|
: (instr == INSTR_AND_BITS
|
||
|
? "and_bits()"
|
||
|
: (instr == INSTR_XOR_BITS
|
||
|
? "xor_bits()"
|
||
|
: "unknown"
|
||
|
))
|
||
|
, (int)c1);
|
||
|
}
|
||
|
if (c2 > 0x3f + ' ' || c2 < ' ')
|
||
|
{
|
||
|
free_mstring(result);
|
||
|
errorf("Illegal bit pattern in %s character %d\n"
|
||
|
, instr == INSTR_OR_BITS
|
||
|
? "or_bits()"
|
||
|
: (instr == INSTR_AND_BITS
|
||
|
? "and_bits()"
|
||
|
: (instr == INSTR_XOR_BITS
|
||
|
? "xor_bits()"
|
||
|
: "unknown"
|
||
|
))
|
||
|
, (int)c2);
|
||
|
}
|
||
|
if (instr == INSTR_AND_BITS)
|
||
|
restxt[arg_len] = (char)((c1-' ') & (c2-' ')) + ' ';
|
||
|
else if (instr == INSTR_OR_BITS)
|
||
|
restxt[arg_len] = (char)((c1-' ') | (c2-' ')) + ' ';
|
||
|
else if (instr == INSTR_XOR_BITS)
|
||
|
restxt[arg_len] = (char)((c1-' ') ^ (c2-' ')) + ' ';
|
||
|
}
|
||
|
|
||
|
/* Clean up the stack and push the result. */
|
||
|
free_svalue(sp--);
|
||
|
free_svalue(sp);
|
||
|
put_string(sp, result);
|
||
|
|
||
|
return sp;
|
||
|
} /* binop_bits() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_or_bits (svalue_t *sp)
|
||
|
|
||
|
/* EFUN or_bits()
|
||
|
*
|
||
|
* string or_bits(string str1, string str2)
|
||
|
*
|
||
|
* Perform a binary operation on the bitstrings <str1> and <str2>
|
||
|
* and return the resulting string.
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
return binop_bits(sp, INSTR_OR_BITS);
|
||
|
} /* f_or_bits() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_and_bits (svalue_t *sp)
|
||
|
|
||
|
/* EFUN and_bits()
|
||
|
*
|
||
|
* string and_bits(string str1, string str2)
|
||
|
*
|
||
|
* Perform a binary operation on the bitstrings <str1> and <str2>
|
||
|
* and return the resulting string.
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
return binop_bits(sp, INSTR_AND_BITS);
|
||
|
} /* f_and_bits() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_xor_bits (svalue_t *sp)
|
||
|
|
||
|
/* EFUN xor_bits()
|
||
|
*
|
||
|
* string xor_bits(string str1, string str2)
|
||
|
*
|
||
|
* Perform a binary operation on the bitstrings <str1> and <str2>
|
||
|
* and return the resulting string.
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
return binop_bits(sp, INSTR_XOR_BITS);
|
||
|
} /* f_xor_bits() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_invert_bits (svalue_t *sp)
|
||
|
|
||
|
/* EFUN invert_bits()
|
||
|
*
|
||
|
* string invert_bits(string str)
|
||
|
*
|
||
|
* Invert all bits in the bitstring <str> and return the
|
||
|
* new string.
|
||
|
*
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char * str;
|
||
|
string_t *new;
|
||
|
long len;
|
||
|
|
||
|
/* Get the arguments */
|
||
|
|
||
|
len = (long)mstrsize(sp->u.str);
|
||
|
memsafe(new = unshare_mstring(sp->u.str), len
|
||
|
, "new bitstring");
|
||
|
sp->u.str = new;
|
||
|
str = get_txt(sp->u.str);
|
||
|
|
||
|
/* Invert the string */
|
||
|
while (len-- > 0)
|
||
|
{
|
||
|
*str = (char)(' ' + (~(*str - ' ') & 0x3F));
|
||
|
str++;
|
||
|
}
|
||
|
|
||
|
return sp;
|
||
|
} /* f_invert_bits() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_last_bit (svalue_t *sp)
|
||
|
|
||
|
/* EFUN last_bit()
|
||
|
*
|
||
|
* int last_bit(string str)
|
||
|
*
|
||
|
* Return the number of the last set bit in bitstring <str>.
|
||
|
* If no bit is set, return -1.
|
||
|
*
|
||
|
* TODO: extend this to true int-bitflags?
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mp_int pos;
|
||
|
|
||
|
pos = last_bit(sp->u.str);
|
||
|
|
||
|
/* Clear the stack and push the result */
|
||
|
free_svalue(sp);
|
||
|
put_number(sp, pos);
|
||
|
|
||
|
return sp;
|
||
|
} /* f_last_bit() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_next_bit (svalue_t *sp)
|
||
|
|
||
|
/* EFUN next_bit()
|
||
|
*
|
||
|
* int next_bit(string str, int start, int find_zero)
|
||
|
*
|
||
|
* Return the number of the next bit after position <start>
|
||
|
* in bitstring <str>. If <find_zero> is non-null, the next
|
||
|
* unset bit is found, else the next set bit.
|
||
|
* If there is no such bit, return -1.
|
||
|
*
|
||
|
* If <start> is negative, the string is searched from the
|
||
|
* beginning.
|
||
|
*
|
||
|
* TODO: extend this to true int-bitflags?
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mp_int found; /* Resultvalue */
|
||
|
size_t pos; /* Searchposition */
|
||
|
size_t search; /* Searchindex */
|
||
|
int pattern; /* Pattern for next bit to test */
|
||
|
long len; /* Length of the string */
|
||
|
long start; /* Startposition */
|
||
|
char * str; /* the bitstring */
|
||
|
int invert; /* when looking for 0 bits, an inverter mask */
|
||
|
|
||
|
/* Get the arguments */
|
||
|
|
||
|
str = get_txt((sp-2)->u.str);
|
||
|
len = (long)mstrsize((sp-2)->u.str);
|
||
|
|
||
|
start = (sp-1)->u.number;
|
||
|
if (start < 0)
|
||
|
{
|
||
|
pattern = 0x01;
|
||
|
pos = 0;
|
||
|
search = 0;
|
||
|
}
|
||
|
else if (start % 6 == 5)
|
||
|
{
|
||
|
pattern = 0x01;
|
||
|
pos = (size_t)start + 1;
|
||
|
search = (size_t)start / 6 + 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pattern = 1 << (start % 6 + 1);
|
||
|
pos = (size_t)start + 1;
|
||
|
search = (size_t)start / 6;
|
||
|
}
|
||
|
|
||
|
invert = 0;
|
||
|
if (!sp->type == T_NUMBER || sp->u.number)
|
||
|
invert = 0x3f;
|
||
|
|
||
|
/* Now search for the next bit */
|
||
|
found = -1;
|
||
|
|
||
|
while (found < 0 && (long)search < len)
|
||
|
{
|
||
|
int c = str[search] - ' ';
|
||
|
|
||
|
if (c < 0 || c > 0x3f)
|
||
|
errorf("Illegal bit pattern in next_bit character %d\n"
|
||
|
, c+' ');
|
||
|
c ^= invert;
|
||
|
|
||
|
while (found < 0 && pattern < (1 << 6))
|
||
|
{
|
||
|
if (c & pattern)
|
||
|
{
|
||
|
found = (mp_int)pos;
|
||
|
break;
|
||
|
}
|
||
|
pattern <<= 1;
|
||
|
pos++;
|
||
|
}
|
||
|
pattern = 0x01;
|
||
|
search++;
|
||
|
}
|
||
|
|
||
|
/* Cleanup the stack and push the result */
|
||
|
free_svalue(sp--);
|
||
|
free_svalue(sp--);
|
||
|
free_svalue(sp);
|
||
|
put_number(sp, found);
|
||
|
|
||
|
return sp;
|
||
|
} /* f_next_bit() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
f_count_bits (svalue_t *sp)
|
||
|
|
||
|
/* EFUN count_bits()
|
||
|
*
|
||
|
* int count_bits(string str)
|
||
|
*
|
||
|
* Return the number of set bits in bitstring <str>.
|
||
|
*
|
||
|
* TODO: Apply to an optional range (start, length) only.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char * str;
|
||
|
long count;
|
||
|
long len;
|
||
|
|
||
|
/* Get the arguments */
|
||
|
str = get_txt(sp->u.str);
|
||
|
len = (long)mstrsize(sp->u.str);
|
||
|
|
||
|
for (count = 0; len > 0; str++, len--)
|
||
|
{
|
||
|
int c = *str - ' ';
|
||
|
|
||
|
if (c > 0x3F || c < 0)
|
||
|
errorf("Illegal character in count_bits: %d\n", (int)c + ' ');
|
||
|
|
||
|
/* Count the bits in this character */
|
||
|
count += ( (c & 0x01) )
|
||
|
+ ( (c & 0x02) >> 1)
|
||
|
+ ( (c & 0x04) >> 2)
|
||
|
+ ( (c & 0x08) >> 3)
|
||
|
+ ( (c & 0x10) >> 4)
|
||
|
+ ( (c & 0x20) >> 5);
|
||
|
}
|
||
|
|
||
|
/* Return the result */
|
||
|
|
||
|
free_svalue(sp);
|
||
|
put_number(sp, count);
|
||
|
|
||
|
return sp;
|
||
|
} /* f_count_bits() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static INLINE void
|
||
|
copy_bits ( string_t * dest, p_int deststart
|
||
|
, string_t * src, p_int srcstart
|
||
|
, p_int len
|
||
|
)
|
||
|
|
||
|
/* Copy the bitrange <src>[<srcstart>..<srcstart>+<len>[ into the
|
||
|
* <dest> string starting at <deststart>.
|
||
|
* <dest> is supposed to be allocated big enough.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char * pDest, * pSrc;
|
||
|
|
||
|
if (len < 1)
|
||
|
return;
|
||
|
|
||
|
pDest = get_txt(dest) + deststart / 6;
|
||
|
pSrc = get_txt(src) + srcstart / 6;
|
||
|
|
||
|
/* Special case: both source and target bit range start on the same bit.
|
||
|
*/
|
||
|
if (deststart % 6 == srcstart % 6)
|
||
|
{
|
||
|
/* Copy the first few bits until the next byte boundary.
|
||
|
* Make sure not to overwrite preexisting bytes
|
||
|
*/
|
||
|
if (deststart % 6 != 0)
|
||
|
{
|
||
|
int getmask, storemask;
|
||
|
|
||
|
storemask = (1 << deststart % 6) - 1;
|
||
|
/* storemask is now 00011111 through 00000001 */
|
||
|
getmask = ~storemask;
|
||
|
/* getmask is now 11100000 through 11111110 */
|
||
|
|
||
|
if (len < 6 - deststart % 6)
|
||
|
{
|
||
|
/* A really small copy: we don't even need all bits from
|
||
|
* the src byte.
|
||
|
*/
|
||
|
int len2 = 6 - deststart % 6 - len;
|
||
|
int lenmask;
|
||
|
|
||
|
lenmask = (1 << len2) - 1;
|
||
|
lenmask <<= 6 - len2;
|
||
|
/* lenmask has now 1-bits for the bits we need not to copy */
|
||
|
storemask &= ~lenmask;
|
||
|
}
|
||
|
|
||
|
*pDest = ' ' + ( ((*pDest - ' ') & storemask)
|
||
|
| ((*pSrc - ' ') & getmask)
|
||
|
);
|
||
|
|
||
|
len -= 6 - deststart % 6; /* might become negative here */
|
||
|
pDest++;
|
||
|
pSrc++;
|
||
|
}
|
||
|
|
||
|
/* Do a fast byte copy */
|
||
|
if (len >= 6)
|
||
|
{
|
||
|
memcpy(pDest, pSrc, len / 6);
|
||
|
}
|
||
|
|
||
|
/* Copy the remaining bytes, if any */
|
||
|
if (len > 0 && len % 6 != 0)
|
||
|
{
|
||
|
int mask;
|
||
|
|
||
|
mask = (1 << (len % 6)) - 1;
|
||
|
/* mask is now 00011111 through 00000001 */
|
||
|
pDest[len / 6] = ((pSrc[len / 6] - ' ') & mask) + ' ';
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Copy enough bits so that deststart points to a byte boundary */
|
||
|
if (deststart % 6 != 0)
|
||
|
{
|
||
|
/* Since these are going to be at max five bits, we copy
|
||
|
* them in a loop bit by bit.
|
||
|
*/
|
||
|
|
||
|
int getmask, getbit;
|
||
|
int storemask, storebit;
|
||
|
char src_bits, dest_bits;
|
||
|
|
||
|
getbit = srcstart % 6;
|
||
|
getmask = 1 << getbit;
|
||
|
storebit = deststart % 6;
|
||
|
storemask = 1 << storebit;
|
||
|
|
||
|
src_bits = *pSrc - ' ';
|
||
|
dest_bits = *pDest - ' ';
|
||
|
|
||
|
while (len > 0 && storebit != 6)
|
||
|
{
|
||
|
if (0 != (src_bits & getmask))
|
||
|
dest_bits = dest_bits | storemask;
|
||
|
else
|
||
|
dest_bits = dest_bits & (~storemask);
|
||
|
|
||
|
srcstart++;
|
||
|
getbit++; getmask <<= 1;
|
||
|
if (getbit == 6)
|
||
|
{
|
||
|
getbit = 0;
|
||
|
getmask = 0x01;
|
||
|
pSrc++;
|
||
|
src_bits = *pSrc - ' ';
|
||
|
}
|
||
|
|
||
|
deststart++;
|
||
|
storebit++; storemask <<= 1;
|
||
|
|
||
|
len--;
|
||
|
}
|
||
|
|
||
|
*pDest = dest_bits + ' ';
|
||
|
pDest++;
|
||
|
} /* if need to align deststart */
|
||
|
|
||
|
/* deststart now points to byte boundary, that means we can
|
||
|
* now copy the bits by blockwise selection from the src string.
|
||
|
*/
|
||
|
if (len >= 6)
|
||
|
{
|
||
|
int getmask1, getmask2, len2;
|
||
|
int getshift1, getshift2;
|
||
|
|
||
|
len2 = 6 - srcstart % 6; /* Number of bits in first byte */
|
||
|
|
||
|
getmask1 = (1 << len2) - 1;
|
||
|
getmask1 <<= 6 - len2;
|
||
|
/* getmask1 has now the mask for the high order bits of the
|
||
|
* first src byte
|
||
|
*/
|
||
|
getshift1 = 6 - len2;
|
||
|
getshift2 = len2;
|
||
|
|
||
|
len2 = 6 - len2; /* Number of bits in second byte */
|
||
|
|
||
|
getmask2 = (1 << len2) - 1;
|
||
|
/* getmask2 has now the mask for the low order bits of the
|
||
|
* second src byte
|
||
|
*/
|
||
|
|
||
|
while (len >= 6)
|
||
|
{
|
||
|
*pDest = ' ' + ( ((pSrc[0]-' ') & getmask1) >> getshift1
|
||
|
|((pSrc[1]-' ') & getmask2) << getshift2
|
||
|
);
|
||
|
len -= 6;
|
||
|
pDest++;
|
||
|
pSrc++;
|
||
|
srcstart += 6;
|
||
|
deststart += 6;
|
||
|
}
|
||
|
} /* if >= 6 bits left */
|
||
|
|
||
|
/* deststart still points to byte boundary, but there are
|
||
|
* less than 6 bits left to copy.
|
||
|
*/
|
||
|
if (len > 0)
|
||
|
{
|
||
|
/* Since these are going to be at max five bits, we copy
|
||
|
* them in a loop bit by bit.
|
||
|
*/
|
||
|
|
||
|
int getmask, getbit;
|
||
|
int storemask, storebit;
|
||
|
char src_bits, dest_bits;
|
||
|
|
||
|
getbit = srcstart % 6;
|
||
|
getmask = 1 << getbit;
|
||
|
storebit = 0;
|
||
|
storemask = 0x01;
|
||
|
|
||
|
src_bits = *pSrc - ' ';
|
||
|
dest_bits = 0;
|
||
|
|
||
|
while (len > 0)
|
||
|
{
|
||
|
if (0 != (src_bits & getmask))
|
||
|
dest_bits |= storemask;
|
||
|
|
||
|
srcstart++;
|
||
|
getbit++; getmask <<= 1;
|
||
|
if (getbit == 6)
|
||
|
{
|
||
|
getbit = 0;
|
||
|
getmask = 0x01;
|
||
|
pSrc++;
|
||
|
src_bits = *pSrc - ' ';
|
||
|
}
|
||
|
|
||
|
deststart++;
|
||
|
storebit++; storemask <<= 1;
|
||
|
|
||
|
len--;
|
||
|
}
|
||
|
|
||
|
*pDest = dest_bits + ' ';
|
||
|
} /* if (1..5 bits left) */
|
||
|
} /* case selection */
|
||
|
} /* copy_bits() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
svalue_t *
|
||
|
v_copy_bits (svalue_t *sp, int num_arg)
|
||
|
|
||
|
/* EFUN copy_bits()
|
||
|
*
|
||
|
* string copy_bits(string src, string dest
|
||
|
* [, int srcstart [, int deststart [, int copylen ]]]
|
||
|
* )
|
||
|
*
|
||
|
* Copy the bitrange [<srcstart>..<srcstart>+<copylen>[ from bitstring <src>
|
||
|
* and copy it into the bitstring <dest> starting at <deststart>, overwriting
|
||
|
* the original bits at those positions.
|
||
|
* The resulting combined string is returned, the input strings remain
|
||
|
* unaffected.
|
||
|
*
|
||
|
* If <srcstart> is not given, <src> is copied from the start.
|
||
|
* If <srcstart> is negative, it is counted from one past the last set
|
||
|
* bit in the string (ie. '-1' will index the last set bit).
|
||
|
* If <deststart> is not given, <dest> will be overwritten from the start.
|
||
|
* If <deststart> is negative, it is counted from one past the last set
|
||
|
* bit in the string (ie. '-1' will index the last set bit).
|
||
|
* If <copylen> is not given, it is assumed to be infinite, ie. the result
|
||
|
* will consist of <dest> up to position <deststart>, followed by
|
||
|
* the data copied from <src>.
|
||
|
* If <copylen> is negative, the function will copy the abs(<copylen>)
|
||
|
* bits _before_ <srcstart> in to the result.
|
||
|
*
|
||
|
* None of the range limits can become negative.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
p_int srcstart, deststart, copylen;
|
||
|
p_int srclen, destlen, resultlen;
|
||
|
Bool copyall;
|
||
|
string_t *src, *dest, *result;
|
||
|
|
||
|
/* Get the optional command arguments */
|
||
|
srcstart = deststart = copylen = 0;
|
||
|
copyall = MY_TRUE;
|
||
|
|
||
|
switch (num_arg)
|
||
|
{
|
||
|
case 5:
|
||
|
copyall = MY_FALSE;
|
||
|
copylen = sp->u.number;
|
||
|
sp--;
|
||
|
num_arg--;
|
||
|
/* FALL THROUGH */
|
||
|
|
||
|
case 4:
|
||
|
deststart = sp->u.number;
|
||
|
sp--;
|
||
|
num_arg--;
|
||
|
/* FALL THROUGH */
|
||
|
|
||
|
case 3:
|
||
|
srcstart = sp->u.number;
|
||
|
sp--;
|
||
|
num_arg--;
|
||
|
/* FALL THROUGH */
|
||
|
}
|
||
|
inter_sp = sp;
|
||
|
|
||
|
/* Get the fixed command arguments and check for consistency */
|
||
|
dest = sp->u.str;
|
||
|
src = sp[-1].u.str;
|
||
|
|
||
|
sp++; /* We might need to save a precautionary reference to the result */
|
||
|
|
||
|
srclen = last_bit(src)+1;
|
||
|
destlen = last_bit(dest)+1;
|
||
|
|
||
|
if (srcstart < 0 && srcstart + srclen < 0)
|
||
|
errorf("Bad argument 3 to copy_bits(): Index %ld is out of range "
|
||
|
"(last bit: %ld).\n"
|
||
|
, (long)srcstart, (long)srclen);
|
||
|
if (srcstart < 0)
|
||
|
srcstart += srclen;
|
||
|
|
||
|
if (deststart < 0 && deststart + destlen < 0)
|
||
|
errorf("Bad argument 4 to copy_bits(): Index %ld is out of range "
|
||
|
"(last bit: %ld).\n"
|
||
|
, (long)deststart, (long)destlen);
|
||
|
if (deststart < 0)
|
||
|
deststart += destlen;
|
||
|
|
||
|
if (!copyall && copylen < 0)
|
||
|
{
|
||
|
if (srcstart + copylen < 0)
|
||
|
errorf("Bad argument 5 to copy_bits(): Length %ld out of range "
|
||
|
"(start index: %ld).\n"
|
||
|
, (long)copylen, (long)srcstart);
|
||
|
|
||
|
srcstart += copylen;
|
||
|
copylen = -copylen;
|
||
|
}
|
||
|
|
||
|
/* Test the input strings for sanity */
|
||
|
{
|
||
|
char *cp;
|
||
|
long len;
|
||
|
|
||
|
len = (long)mstrsize(src);
|
||
|
cp = get_txt(src);
|
||
|
for ( ; len > 0; len--, cp++)
|
||
|
{
|
||
|
int c = *cp - ' ';
|
||
|
if (c < 0 || c > 0x3f)
|
||
|
errorf("Bad argument 1 to copy_bits(): String contains "
|
||
|
"illegal character %d\n", c + ' ');
|
||
|
}
|
||
|
|
||
|
len = (long)mstrsize(dest);
|
||
|
cp = get_txt(dest);
|
||
|
for ( ; len > 0; len--, cp++)
|
||
|
{
|
||
|
int c = *cp - ' ';
|
||
|
if (c < 0 || c > 0x3f)
|
||
|
errorf("Bad argument 2 to copy_bits(): String contains "
|
||
|
"illegal character %d\n", c + ' ');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Do the copying - some constellations are really simple */
|
||
|
if (copyall)
|
||
|
{
|
||
|
if (srcstart == 0 && deststart == 0)
|
||
|
result = ref_mstring(src);
|
||
|
else
|
||
|
{
|
||
|
if (srclen > srcstart)
|
||
|
copylen = srclen - srcstart;
|
||
|
else
|
||
|
copylen = 0;
|
||
|
|
||
|
if (PINT_MAX - copylen < deststart)
|
||
|
errorf("copy_bits: result length exceeds numerical limit: "
|
||
|
"%ld + %ld\n"
|
||
|
, (long)deststart, (long)copylen
|
||
|
);
|
||
|
resultlen = deststart + copylen;
|
||
|
if (resultlen > MAX_BITS || resultlen < 0)
|
||
|
errorf("copy_bits: Result too big: %lu bits\n"
|
||
|
, (unsigned long)resultlen);
|
||
|
|
||
|
/* Get the result string and store the reference on the stack
|
||
|
* for error cleanups.
|
||
|
*/
|
||
|
memsafe(result = alloc_mstring((resultlen + 5) / 6)
|
||
|
, (resultlen + 5) / 6, "new bitstring");
|
||
|
memset(get_txt(result), ' ', (resultlen + 5) / 6);
|
||
|
inter_sp = sp; put_string(sp, result);
|
||
|
|
||
|
/* Copy the first part of dest into the result */
|
||
|
if (deststart > 0)
|
||
|
{
|
||
|
if (deststart > destlen)
|
||
|
copy_bits(result, 0, dest, 0, destlen);
|
||
|
else
|
||
|
copy_bits(result, 0, dest, 0, deststart);
|
||
|
}
|
||
|
|
||
|
/* Now append the src */
|
||
|
copy_bits(result, deststart, src, srcstart, copylen);
|
||
|
}
|
||
|
}
|
||
|
else if (copylen == 0)
|
||
|
result = ref_mstring(dest);
|
||
|
else if (srcstart == 0 && deststart == 0
|
||
|
&& copylen >= destlen && copylen >= srclen)
|
||
|
{
|
||
|
result = ref_mstring(src);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p_int destendlen; /* Length of the end part of dest to copy */
|
||
|
p_int srccopylen; /* Actual number of bits to copy from src */
|
||
|
|
||
|
/* Get the length to copy and the length of the result */
|
||
|
srccopylen = copylen;
|
||
|
if (srcstart >= srclen - copylen)
|
||
|
srccopylen = srclen - srcstart;
|
||
|
|
||
|
if (PINT_MAX - copylen < deststart)
|
||
|
errorf("copy_bits: result length exceeds numerical limit: %ld + %ld\n"
|
||
|
, (long)deststart, (long)copylen
|
||
|
);
|
||
|
|
||
|
resultlen = deststart + copylen;
|
||
|
if (resultlen < destlen)
|
||
|
resultlen = destlen;
|
||
|
|
||
|
if (resultlen > MAX_BITS || resultlen < 0)
|
||
|
errorf("copy_bits: Result too big: %lu bits\n"
|
||
|
, (unsigned long)resultlen);
|
||
|
|
||
|
destendlen = destlen - (deststart + copylen);
|
||
|
|
||
|
/* Get the result string and store the reference on the stack
|
||
|
* for error cleanups.
|
||
|
*/
|
||
|
memsafe(result = alloc_mstring((resultlen + 5) / 6)
|
||
|
, (resultlen + 5) / 6, "new bitstring");
|
||
|
memset(get_txt(result), ' ', (resultlen + 5) / 6);
|
||
|
inter_sp = sp; put_string(sp, result);
|
||
|
|
||
|
/* Copy the first part of dest into the result */
|
||
|
if (deststart > 0)
|
||
|
{
|
||
|
if (deststart > destlen)
|
||
|
copy_bits(result, 0, dest, 0, destlen);
|
||
|
else
|
||
|
copy_bits(result, 0, dest, 0, deststart);
|
||
|
}
|
||
|
|
||
|
/* Copy the source part into the result */
|
||
|
copy_bits(result, deststart, src, srcstart, srccopylen);
|
||
|
|
||
|
/* Copy the remainder of dest into the result */
|
||
|
if (destendlen > 0)
|
||
|
{
|
||
|
copy_bits( result, deststart + copylen
|
||
|
, dest, deststart + copylen
|
||
|
, destendlen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Clean up the stack and push the result */
|
||
|
sp--; /* The result reference for error cleanup */
|
||
|
free_svalue(sp--);
|
||
|
free_svalue(sp);
|
||
|
|
||
|
put_string(sp, result);
|
||
|
return sp;
|
||
|
} /* v_copy_bits() */
|
||
|
|
||
|
/***************************************************************************/
|
||
|
|