如果有以下功能,请记下通话的位置free(tmp)
:
int *power_arr(int *n, int nlength, int exp, int *res_length)
{
int *tmp, *rt, *bufp;
int bufp_length, i, dbg_i;
rt = malloc(sizeof(int) * 1000);
bufp = malloc(sizeof(int) * 1000);
if (!rt || !bufp)
{
return NULL;
}
copy(rt, n, nlength);
copy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0)
{
for (i = *n - 1; i > 0; i--)
{
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
if (!tmp)
{
exit(-1);
}
copy(rt, tmp, *res_length);
//free(tmp); // produces undefined output?
}
copy(bufp, rt, *res_length);
bufp_length = *res_length;
}
free(tmp);
free(bufp);
return rt;
}
以下主要功能的结果:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int b[] = { 3 };
int r, i;
int *rlength, *res;
r = 0;
rlength = &r;
res = power_arr(b, 1, 3, rlength);
printf("Length = %d\n", *rlength);
for (i = 0; i < *rlength; i++)
{
printf("i=");
printf("%d\n", res[i]);
}
printf("\n");
exit(0);
}
时:
Length = 2
i=2
i=7
我对第一个场景的理解是,每次后续tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
调用都会发生内存泄漏。在哪一点上,我决定将调用移到 for 循环内的free(tmp)
。移动后,我注意到输出已经改变,如下所示:
Length = 4
i=1018670
i=4
i=2
i=7
我可以看到答案从我[3]开始。为什么free(tmp)
调用的移动会产生这种影响?
我的理解是tmp
在free()
调用后成为悬空指针。然后,重新分配函数sum()
返回的值 - 通过调用malloc()
检索该值。在哪一点,通过将free()
的呼叫置于其原始位置会发生内存泄漏。当tmp
的值发生变化时,只有释放分配给它的最后一个指针。
以下是支持功能的代码。
int *pad(int *n, int nlength, int new_length, enum SIDE side)
{
int i, j;
int *padded;
if (nlength < 1 || new_length <= nlength)
{
return NULL;
}
padded = calloc(new_length, sizeof(int));
if (!padded)
{
return NULL;
}
if (side == LOW)
{
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--)
{
padded[j--] = n[i];
}
}
else
{
j = 0;
for (i = 0; i < nlength; i++)
{
padded[j++] = n[i];
}
}
return padded;
}
int *trim(int *n, int nlength, int *res_length)
{
int i, j;
int *res;
for (i = 0; i < nlength; i++)
{
if (n[i] > 0)
{
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res)
{
return NULL;
}
j = 0;
while (i < nlength)
{
res[j++] = n[i++];
}
return res;
}
int *sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
int i, tmp, carry;
int *result, *trimmed, *op1, *op2;
enum SIDE side = LOW;
if (nlength == mlength)
{
op1 = n;
op2 = m;
}
else if (nlength > mlength)
{
op1 = n;
op2 = pad(m, mlength, nlength, side);
}
else
{
op1 = m;
op2 = pad(n, nlength, mlength, side);
}
result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result)
{
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--)
{
tmp = op1[i] + op2[i] + carry;
if (carry > 0)
{
carry = 0;
}
if (tmp >= 10)
{
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
if (carry > 0)
{
result[0] = carry--;
}
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = trim(result, *sum_length, sum_length);
free(result);
if (!trimmed)
{
return NULL;
}
return trimmed;
}
void copy(int *to, int *from, int length)
{
int i;
for (i = 0; i < length; i++)
{
to[i] = from[i];
}
}
在实现第一篇文章中建议的更改后, double free 错误已经开始发生,要进行调试,我将以下打印语句添加到power_arr()
。以下输出显示tmp
正在从sum()
分配与初始调用时收到的值相同的值。为什么呢?
更新了显示调试printf
语句的代码:
for (i = *n - 1; i > 0; i--)
{
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
printf("first tmp = %d\n", tmp);
if (!tmp)
{
printf("tmp was null\n");
exit(-1);
}
copy(rt, tmp, *res_length);
printf("second tmp = %d\n", tmp);\
if (tmp != NULL)
{
printf("freeing tmp\n");
free(tmp);
tmp = NULL;
}
printf("tmp = %d\n", tmp);
}
输出:
first tmp = 11227072
second tmp = 11227072
freeing tmp
tmp = 0
first tmp = 11227072 <-- Why has the pointer value not changed?
second tmp = 11227072
freeing tmp <-- Double free now occuring.
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000000ab4fc0 ***
Aborted
我相信我已将该错误追踪到trim()
功能。我已经使用循环发布了该函数,以连续执行10次。正如您在输出中看到的那样,调用trim()
的{{1}}会在后续调用中返回相同的指针值。然而,每次连续调用free都不会触发 double free 错误。为什么会这样?
malloc()
输出:
int main()
{
int i, j, length;
int n[] = { 4, 5, 6 };
int m[] = { 0, 3, 5 };
int *num;
int *trimmed, *trimmed_length;
trimmed_length = &length;
for (i = 0; i < 10; i++)
{
num = (i % 2 == 0) ? n : m;
trimmed = trim(num, 3, trimmed_length);
if (!trimmed)
{
printf("trimmed was null\n");
exit(-1);
}
for (j = 0; j < *trimmed_length; j++)
{
printf("%d", trimmed[j]);
}
printf("\n");
free(trimmed);
}
exit(0);
}
int *trim(int *n, int nlength, int *res_length)
{
int i, j;
int *res;
for (i = 0; i < nlength; i++)
{
if (n[i] > 0)
{
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res)
{
return NULL;
}
j = 0;
while (i < nlength)
{
res[j++] = n[i++];
}
printf("Returned pointer from trim() %d\n", res);
return res;
}
这似乎也是我原始问题中的行为 - 触发双重免费错误。为什么在这种特殊情况下没有遇到 double free 错误?
答案 0 :(得分:1)
你有许多内存泄漏点和一个完整的错误,这就是免费失败的原因[由于同一指针的双重释放]。
注意:我已对这个答案做了更新,但它太大了,不适合这里,所以我把它作为第二个答案发布了
我已将所有文件合并为一个所以我可以编译它[请原谅无偿的样式清理],修复了错误,并注释了所有的热点[这个编译,但我没有测试它]:
#include <stdlib.h>
#include <stdio.h>
enum SIDE {
LOW
};
#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))
// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free"
#define FREEME(_ptr) \
do { \
if (_ptr != NULL) \
free(_ptr); \
_ptr = NULL; \
} while (0)
int *
pad(int *n, int nlength, int new_length, enum SIDE side)
{
int i,
j;
int *padded;
if (nlength < 1 || new_length <= nlength) {
return NULL;
}
padded = calloc(new_length, sizeof(int));
if (!padded) {
return NULL;
}
if (side == LOW) {
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--) {
padded[j--] = n[i];
}
}
else {
j = 0;
for (i = 0; i < nlength; i++) {
padded[j++] = n[i];
}
}
return padded;
}
int *
trim(int *n, int nlength, int *res_length)
{
int i,
j;
int *res;
for (i = 0; i < nlength; i++) {
if (n[i] > 0) {
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res) {
return NULL;
}
j = 0;
while (i < nlength) {
res[j++] = n[i++];
}
return res;
}
int *
sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
int i,
tmp,
carry;
int *result,
*trimmed,
*op1,
*op2;
int padflg;
enum SIDE side = LOW;
// NOTE: this helps us remember whether to free op2 or not
padflg = 1;
// NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
// function -- _this_ is the cause of the bug
// case (1)
if (nlength == mlength) {
op1 = n;
op2 = m;
padflg = 0;
}
// NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
// doesn't leak
// case (2)
else if (nlength > mlength) {
op1 = n;
op2 = pad(m, mlength, nlength, side);
}
// case (3)
else {
op1 = m;
op2 = pad(n, nlength, mlength, side);
}
result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result) {
if (padflg)
FREEME(op2);
FREEME(result);
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
tmp = op1[i] + op2[i] + carry;
if (carry > 0) {
carry = 0;
}
if (tmp >= 10) {
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
// NOTE: we want to free op2 for case (2)/(3) but we didn't remember
// how we got it: (1) means no free, (2)/(3) means free
// only free if this if we called pad, and _not_ if this pointer belongs
// to caller
if (padflg)
FREEME(op2);
if (carry > 0) {
result[0] = carry--;
}
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = trim(result, *sum_length, sum_length);
free(result);
return trimmed;
}
void
copy(int *to, int *from, int length)
{
int i;
for (i = 0; i < length; i++) {
to[i] = from[i];
}
}
int *
power_arr(int *n, int nlength, int exp, int *res_length)
{
int *tmp,
*rt,
*bufp;
int bufp_length,
i;
// NOTE: rt/bufp are memory leaks -- they are never freed
rt = malloc(sizeof(int) * 1000);
bufp = malloc(sizeof(int) * 1000);
// NOTE: this is a memory leak -- if one is null, but the other is non-null,
// you must free the non-null one or it leaks
if (!rt || !bufp) {
FREEME(rt);
FREEME(bufp);
return NULL;
}
copy(rt, n, nlength);
copy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0) {
for (i = *n - 1; i > 0; i--) {
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
if (!tmp) {
exit(-1);
}
copy(rt, tmp, *res_length);
// NOTE: this will now work because of the padflg changes in
// sum
#if 0
// free(tmp); // produces undefined output?
#else
FREEME(tmp);
#endif
}
copy(bufp, rt, *res_length);
bufp_length = *res_length;
}
FREEME(bufp);
return rt;
}
int
main(void)
{
int b[] = { 3 };
int r,
i;
int *rlength,
*res;
r = 0;
rlength = &r;
res = power_arr(b, 1, 3, rlength);
printf("Length = %d\n", *rlength);
for (i = 0; i < *rlength; i++) {
printf("i=");
printf("%d\n", res[i]);
}
printf("\n");
exit(0);
}
答案 1 :(得分:0)
更新:我在这里添加第二个答案,因为新的代码示例太大,无法作为我之前的更新。
我发现了一些问题。如果重复运行原始测试程序,则每次运行时会给出不同的[错误]答案。也就是说,从脚本循环它。
我无法用现有的函数和参数修复剩余的bug。纯粹的复杂性增加了问题。
一旦我意识到你真正想做的事情(例如多精度数学),我就可以应用几种简化:
我没有到处传递int *vals, int *len
,而是创建了一个“大号”struct
:
- 包含指向数据和长度的指针
- 数据缓冲区的长度可以动态增长/缩小
- 有参考计数
你在“大端”模式下进行数学运算。这使得有必要经常重新分配(例如在pad
和copy
中)。这也增加了复杂性。我将其切换为使用“小端”,很多更容易使用。而且,它也有点[相当]更快。
我还在您的电源功能中注意到您使用sum
来执行基本上rt += bufp;
的操作。因此,我创建了一个版本sumeq
,它直接在power rt
值上运行,这进一步简化了事情。
注意:您仍然可以使用big endian,但我见过的大多数软件包都没用。仍然,创建/增长结构的基本例程是endian不可知的,你可以创建我创建的小端函数的大端版本[来自你的大端函数]
这是实际的代码。它是可构建的和可运行的。它将测试所有版本,包括您的原始代码[有问题]。
// badfree -- test program
#define TSTDEFALL
// badfree/badfree.h -- badfree control
#ifndef _badfree_badfree_h_
#define _badfree_badfree_h_
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifdef DEBUG
#define dbgprtf(_fmt...) printf(_fmt)
#define dbgexec(_exec) _exec
#else
#define dbgprtf(_fmt...) /**/
#define dbgexec(_exec) /**/
#endif
#define dbgleshow(_num,_who) dbgexec(leshow(_num,0,_who))
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
fflush(stdout); \
_sysfault(); \
} while (0)
typedef unsigned int u32;
#if BIGDIG == 1
typedef char bigdig_t;
#elif BIGDIG == 2
typedef short bigdig_t;
#else
typedef int bigdig_t;
#endif
typedef bigdig_t *bigdig_p;
typedef const bigdig_t *bigdig_pc;
// multiprecision number control
struct bignum {
u32 num_opts; // options
const char *num_tag; // symbol name
int num_refcnt; // reference count
int num_curlen; // current length
int num_maxlen; // maximum length
bigdig_p num_base; // pointer to vector
};
typedef struct bignum bgn_t;
typedef bgn_t *bgn_p;
typedef const bgn_t *bgn_pc;
// num_opts definitions
#define BGNASTRUCT (1 << 0) // struct was allocated
#define BGNABASE (1 << 1) // num_base was allocated
#define BGNINITAV (1 << 2) // bgninit -- init values
#define BGNINITDUP (1 << 3) // bgninit -- init values
#define BGNTRIM (1 << 4) // trim number
#define BGNRAW (1 << 5) // output number in raw order
#define BGNACQUIRE(_num) \
bgnacquire(_num)
#define BGNRELEASE(_num) \
_num = bgnrelease(_num)
#define BGNINIT(_sym,_opt,_len...) \
_sym = bgninit(#_sym,_opt,_len)
#define BGNASSERT(_num,_opt) \
do { \
if (((_num)->num_opts & _opt) == (_opt)) \
break; \
sysfault("BGNASSERT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
num,num->num_opts,_opt); \
} while (0)
#define BGNASSERT_NOT(_num,_opt) \
do { \
if (((_num)->num_opts & _opt) != (_opt)) \
break; \
sysfault("BGNASSERT_NOT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
num,num->num_opts,_opt); \
} while (0)
enum SIDE {
LOW
};
#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))
#define MIN(_x,_y) (((_x) < (_y)) ? (_x) : (_y))
// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free" and use of pointer after it has been freed
#define FREEME(_ptr) \
do { \
if (_ptr != NULL) \
free(_ptr); \
_ptr = NULL; \
} while (0)
// test control
struct tstctl {
int tst_allow; // allow test to be performed
const char *tst_tag; // name of test
void (*tst_fnc)(void); // test function
};
typedef struct tstctl tstctl_t;
typedef tstctl_t *tstctl_p;
typedef const tstctl_t *tstctl_pc;
#define FORTSTALL(_tst) \
_tst = tstlist; _tst->tst_tag != NULL; ++_tst
int
main(int argc, char **argv);
void
dotests(void);
void
dotest(tstctl_p tst);
void
usage(void);
void
becopy(bigdig_t *to, const bigdig_t *from, int length);
bigdig_t *
bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side);
bigdig_t *
betrim(const bigdig_t *n, int *res_length);
bigdig_t *
besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
int *sum_length);
bigdig_t *
be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length);
void
betest_orig(void);
void
betest_trim(void);
void
betest_show(bigdig_pc num,int len,const char *sym);
bgn_p
bgninit(const char *tag,u32 opt,int maxlen,...);
bgn_p
bgnacquire(bgn_p num);
bgn_p
bgnrelease(bgn_p num);
void
bgngrow(bgn_p num,int newlen);
void
bgnshrink(bgn_p num);
void
lecopy(bgn_p to,bgn_pc from);
bgn_p
lepad(bgn_pc num,int newlen);
void
letrim(bgn_p num);
int
_letrim(bgn_pc num);
void
leshow(bgn_pc num,u32 opt,const char *who);
void
lesumeq(bgn_p result,bgn_p op2);
bgn_p
le_power_arr_eq(bgn_pc num,int exp);
bgn_p
lesumrt(bgn_p n,bgn_p m);
bgn_p
le_power_arr_rt(bgn_pc n, int exp);
void
letest_lesumrt(void);
void
letest_lesumeq(void);
void
_sysfault(void);
void
reverse(bigdig_p arr, int length);
#define TSTDEF(_on,_fnc) \
{ .tst_allow = _on, .tst_tag = #_fnc, .tst_fnc = _fnc }
#ifdef TSTDEFALL
tstctl_t tstlist[] = {
TSTDEF(1,betest_orig),
TSTDEF(0,betest_trim),
TSTDEF(1,letest_lesumrt),
TSTDEF(1,letest_lesumeq),
{ .tst_tag = NULL }
};
#endif
#endif
// badfree/bemath -- big endian math
void
becopy(bigdig_t *to, const bigdig_t *from, int length)
{
int i;
for (i = 0; i < length; i++)
to[i] = from[i];
}
bigdig_t *
bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side)
{
int i;
int j;
bigdig_t *padded;
if (nlength < 1 || new_length <= nlength) {
sysfault("bepad: length fault -- nlength=%d new_length=%d\n",
nlength,new_length);
return NULL;
}
padded = calloc(new_length,sizeof(bigdig_t));
if (!padded)
return NULL;
if (side == LOW) {
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--)
padded[j--] = n[i];
}
else {
j = 0;
for (i = 0; i < nlength; i++)
padded[j++] = n[i];
}
return padded;
}
bigdig_t *
betrim(const bigdig_t *n, int *res_length)
{
int i;
int j;
int nlength;
bigdig_t *res;
nlength = *res_length;
for (i = 0; i < nlength; i++) {
if (n[i] > 0)
break;
}
nlength -= i;
*res_length = nlength;
res = malloc(sizeof(bigdig_t) * nlength);
if (!res) {
sysfault("betrim: null malloc\n");
return NULL;
}
j = 0;
for (; i < nlength; ++i, ++j)
res[j] = n[i];
return res;
}
bigdig_t *
besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
int *sum_length)
{
int i;
int tmp;
int carry;
bigdig_t *result;
bigdig_t *trimmed;
bigdig_t *op1;
bigdig_t *op2;
int padflg;
enum SIDE side = LOW;
// NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
// function -- _this_ is the cause of the bug
// case (1)
if (nlength == mlength) {
op1 = n;
op2 = m;
padflg = 0;
}
// NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
// doesn't leak
// case (2)
else if (nlength > mlength) {
op1 = n;
op2 = bepad(m, mlength, nlength, side);
padflg = 1;
}
// case (3)
else {
op1 = m;
op2 = bepad(n, nlength, mlength, side);
padflg = 2;
}
result = malloc(sizeof(bigdig_t) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result) {
sysfault("besum: null fault -- padflg=%d op1=%p op2=%p result=%p\n",
padflg,op1,op2,result);
if (padflg)
FREEME(op2);
FREEME(result);
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
tmp = op1[i] + op2[i] + carry;
if (carry > 0)
carry = 0;
if (tmp >= 10) {
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
// NOTE: we want to free op2 for case (2)/(3) but we didn't remember
// how we got it: (1) means no free, (2)/(3) means free
// only free if this if we called bepad, and _not_ if this pointer belongs
// to caller
if (padflg)
FREEME(op2);
if (carry > 0)
result[0] = carry;
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = betrim(result, sum_length);
FREEME(result);
return trimmed;
}
bigdig_t *
be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length)
{
bigdig_t *tmp;
bigdig_t *rt;
bigdig_t *bufp;
int bufp_length;
int i;
// NOTE: rt/bufp are memory leaks -- they are never freed
rt = malloc(sizeof(bigdig_t) * 1000);
bufp = malloc(sizeof(bigdig_t) * 1000);
// NOTE: this is a memory leak -- if one is null, but the other is non-null,
// you must free the non-null one or it leaks
if (!rt || !bufp) {
FREEME(rt);
FREEME(bufp);
return NULL;
}
becopy(rt, n, nlength);
becopy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0) {
for (i = *n - 1; i > 0; i--) {
tmp = besum(rt, nlength, bufp, bufp_length, res_length);
if (tmp == NULL)
sysfault("be_power_arr: null besum return\n");
nlength = *res_length;
becopy(rt, tmp, nlength);
// NOTE: this will now work because of the padflg changes in
// besum
#if 0
// free(tmp); // produces undefined output?
#else
FREEME(tmp);
#endif
}
becopy(bufp, rt, *res_length);
bufp_length = *res_length;
}
FREEME(bufp);
return rt;
}
// badfree/betest -- big endian tests
void
betest_orig(void)
{
#if 1
bigdig_t b[] = { 3 };
#else
const bigdig_t b[] = { 3 };
#endif
int rlength;
int exp;
bigdig_t *res;
exp = 3;
printf("\n");
printf("betest_orig: exp=%d\n",exp);
betest_show(b,1,"b");
rlength = 0;
res = be_power_arr(b, 1, exp, &rlength);
betest_show(res,rlength,"res");
FREEME(res);
}
void
betest_trim(void)
{
int i;
int j;
int length;
const bigdig_t n[] = { 4, 5, 6 };
const bigdig_t m[] = { 0, 3, 5 };
const bigdig_t *num;
bigdig_t *trimmed;
printf("\n");
printf("betest_trim:\n");
for (i = 0; i < 10; i++) {
num = (i % 2 == 0) ? n : m;
length = 3;
trimmed = betrim(num, &length);
if (!trimmed)
sysfault("betest_trim: trimmed was null\n");
printf("pass %d: num=%p trimmed=%p\n",i,num,trimmed);
for (j = 0; j < length; j++)
printf(" %d", trimmed[j]);
printf("\n");
FREEME(trimmed);
}
}
// betest_show -- show number
void
betest_show(bigdig_pc num,int len,const char *sym)
{
int i;
printf(" sym %s length %d --",sym,len);
for (i = 0; i < len; i++)
printf(" %d",num[i]);
printf("\n");
}
// badfree/bgn -- big number control
// bgninit -- create new number
bgn_p
bgninit(const char *tag,u32 opt,int maxlen,...)
// opt -- options (BGNINIT* -- has initializer data)
// maxlen -- length of number
{
va_list ap;
int i;
bgn_pc from;
bgn_p num;
va_start(ap,maxlen);
num = calloc(1,sizeof(bgn_t));
opt |= BGNASTRUCT;
opt |= BGNABASE;
num->num_opts = opt;
num->num_tag = tag;
num->num_refcnt = 1;
if (maxlen <= 0)
maxlen = 1;
from = NULL;
if (opt & BGNINITDUP) {
from = va_arg(ap,bgn_pc);
maxlen = MAX(maxlen,from->num_curlen);
}
num->num_maxlen = maxlen;
num->num_base = calloc(maxlen,sizeof(bigdig_t));
// initialize from varargs
if (opt & BGNINITAV) {
for (i = 0; i < maxlen; ++i)
num->num_base[i] = va_arg(ap,int);
num->num_curlen = maxlen;
}
// initialize by cloning data
if (opt & BGNINITDUP) {
maxlen = from->num_curlen;
for (i = 0; i < maxlen; ++i)
num->num_base[i] = from->num_base[i];
num->num_curlen = maxlen;
}
va_end(ap);
return num;
}
// bgnacquire -- increment reference count
bgn_p
bgnacquire(bgn_p num)
{
num->num_refcnt += 1;
return num;
}
// bgnrelease -- decrement reference count and deallocate
bgn_p
bgnrelease(bgn_p num)
{
if (--num->num_refcnt == 0) {
if (num->num_opts & BGNABASE)
FREEME(num->num_base);
if (num->num_opts & BGNASTRUCT)
FREEME(num);
// this zaps caller's pointer
num = NULL;
}
return num;
}
// bgngrow -- grow allocated number to given length
void
bgngrow(bgn_p num,int newlen)
{
int growlen;
int maxlen;
int i;
BGNASSERT(num,BGNABASE);
maxlen = num->num_maxlen;
growlen = newlen - maxlen;
if (growlen > 0) {
maxlen += growlen;
num->num_base = realloc(num->num_base,sizeof(bigdig_t) * maxlen);
// zero extend the new area
for (i = num->num_maxlen; i < maxlen; ++i)
num->num_base[i] = 0;
num->num_maxlen = maxlen;
}
}
// bgnshrink -- shrink allocated number to current length
void
bgnshrink(bgn_p num)
{
int curlen;
BGNASSERT(num,BGNABASE);
curlen = num->num_curlen;
if (num->num_maxlen > curlen) {
num->num_base = realloc(num->num_base,sizeof(bigdig_t) * curlen);
num->num_maxlen = curlen;
}
}
// badfree/lecom -- little endian math common
// lecopy -- copy big number
void
lecopy(bgn_p to,bgn_pc from)
{
int newlen;
int i;
dbgprtf("lecopy: ENTER\n");
dbgleshow(to,"lecopy");
dbgleshow(from,"lecopy");
newlen = from->num_curlen;
bgngrow(to,newlen);
for (i = 0; i < newlen; ++i)
to->num_base[i] = from->num_base[i];
to->num_curlen = newlen;
dbgleshow(to,"lecopy");
dbgprtf("lecopy: EXIT\n");
}
// lepad -- clone and pad number
bgn_p
lepad(bgn_pc num,int newlen)
{
int i;
int curlen;
bgn_p padded;
curlen = num->num_curlen;
#if 0
if ((curlen < 1) || (newlen <= curlen)) {
sysfault("lepad: length fault -- curlen=%d newlen=%d\n",curlen,newlen);
return NULL;
}
#endif
BGNINIT(padded,0,newlen);
if (!padded) {
sysfault("lepad: bgninit returned null\n");
return NULL;
}
// copy existing digits
for (i = 0; i < curlen; ++i)
padded->num_base[i] = num->num_base[i];
// zero extend the larger number
for (; i < newlen; ++i)
padded->num_base[i] = 0;
return padded;
}
// letrim -- get rid of leading zeroes by adjusting current length
void
letrim(bgn_p num)
{
num->num_curlen = _letrim(num);
}
// _letrim -- get rid of leading zeroes by adjusting current length
// RETURNS: trimmed length
int
_letrim(bgn_pc num)
{
int i;
int curlen;
curlen = num->num_curlen;
for (i = curlen - 1; i >= 0; --i) {
if (num->num_base[i] > 0)
break;
}
if (i <= 0)
i = 1;
return i;
}
// leshow -- show number
void
leshow(bgn_pc num,u32 opt,const char *who)
{
int curlen;
int i;
if (opt & BGNTRIM)
curlen = _letrim(num);
else
curlen = num->num_curlen;
if (who != NULL)
printf("%s: ",who);
printf("sym=%s ref=%d len=%d/%d",
num->num_tag,num->num_refcnt,curlen,num->num_maxlen);
printf(" trim=%s order=%s",
(opt & BGNTRIM) ? "yes" : "no",
(opt & BGNRAW) ? "raw" : "flip");
printf("\n");
if (who != NULL)
printf("%s:",who);
printf(" ");
if (opt & BGNRAW) {
for (i = 0; i < curlen; ++i)
printf(" %d",num->num_base[i]);
}
else {
for (i = curlen - 1; i >= 0; --i)
printf(" %d",num->num_base[i]);
}
printf("\n");
}
// badfree/lesumeq -- little endian sum / power (in-place)
// lesumeq -- do x += y
void
lesumeq(bgn_p result,bgn_p op2)
{
int op2len;
int i;
int tmp;
int carry;
int maxlen;
int minlen;
op2len = op2->num_curlen;
tmp = result->num_curlen;
maxlen = MAX(tmp,op2len);
minlen = MIN(tmp,op2len);
bgngrow(result,maxlen + 1);
carry = 0;
i = 0;
for (; i < minlen; ++i) {
tmp = result->num_base[i] + op2->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
result->num_base[i] = tmp;
}
++maxlen;
for (; i < maxlen; ++i) {
if (! carry)
break;
tmp = result->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
result->num_base[i] = tmp;
}
result->num_curlen = maxlen;
}
// le_power_arr_eq -- raise number to power
bgn_p
le_power_arr_eq(bgn_pc num,int exp)
{
bgn_p rtpwr;
bgn_p bufp;
int icur;
int ilim;
BGNINIT(rtpwr,BGNINITDUP,1000,num);
BGNINIT(bufp,BGNINITDUP,1000,num);
ilim = num->num_base[0];
ilim -= 1;
while (--exp > 0) {
for (icur = 0; icur < ilim; ++icur)
lesumeq(rtpwr,bufp);
lecopy(bufp,rtpwr);
}
BGNRELEASE(bufp);
return rtpwr;
}
// badfree/lesumrt -- little endian sum / power (alloc/return mode)
bgn_p
lesumrt(bgn_p n,bgn_p m)
{
int i;
int tmp;
int carry;
int maxlen;
bgn_p rtsum;
bgn_p op1;
bgn_p op2;
dbgprtf("lesumrt: ENTER\n");
dbgleshow(n,"lesumrt");
dbgleshow(m,"lesumrt");
// case (1)
if (n->num_curlen == m->num_curlen) {
op1 = BGNACQUIRE(n);
op2 = BGNACQUIRE(m);
}
// case (2)
else if (n->num_curlen > m->num_curlen) {
op1 = BGNACQUIRE(n);
op2 = lepad(m,n->num_curlen);
}
// case (3)
else {
op1 = BGNACQUIRE(m);
op2 = lepad(n,m->num_curlen);
}
maxlen = MAX(n->num_curlen,m->num_curlen);
dbgprtf("lesumrt: PAD maxlen=%d\n",maxlen);
BGNINIT(rtsum,0,maxlen + 1);
carry = 0;
for (i = 0; i < maxlen; ++i) {
tmp = op1->num_base[i] + op2->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
rtsum->num_base[i] = tmp;
}
rtsum->num_base[i] += carry;
rtsum->num_curlen = maxlen + 1;
BGNRELEASE(op1);
BGNRELEASE(op2);
dbgleshow(rtsum,"lesumrt");
dbgprtf("lesumrt: EXIT\n");
return rtsum;
}
bgn_p
le_power_arr_rt(bgn_pc n, int exp)
{
bgn_p tmp;
bgn_p rtpwr;
bgn_p bufp;
int icur;
int ilim;
dbgprtf("le_power_arr_rt: ENTER\n");
BGNINIT(rtpwr,BGNINITDUP,1000,n);
BGNINIT(bufp,BGNINITDUP,1000,n);
ilim = n->num_base[0];
ilim -= 1;
while (--exp > 0) {
for (icur = 0; icur < ilim; ++icur) {
tmp = lesumrt(rtpwr,bufp);
if (tmp == NULL)
sysfault("le_power_arr_rt: null lesumrt return\n");
lecopy(rtpwr,tmp);
BGNRELEASE(tmp);
}
lecopy(bufp,rtpwr);
}
BGNRELEASE(bufp);
dbgprtf("le_power_arr_rt: EXIT\n");
return rtpwr;
}
// badfree/letest -- little endian tests
void
letest_lesumrt(void)
{
bgn_p b;
int exp;
bgn_p res;
exp = 3;
printf("\n");
printf("letest_lesumrt: exp=%d\n",exp);
BGNINIT(b,BGNINITAV,1,3);
leshow(b,0,"letest_lesumrt");
res = le_power_arr_rt(b,exp);
leshow(res,0,"letest_lesumrt");
BGNRELEASE(res);
BGNRELEASE(b);
}
void
letest_lesumeq(void)
{
bgn_p b;
int exp;
bgn_p res;
exp = 3;
printf("\n");
printf("letest_lesumeq: exp=%d\n",exp);
BGNINIT(b,BGNINITAV,1,3);
leshow(b,0,"letest_lesumeq");
res = le_power_arr_eq(b,exp);
leshow(res,0,"letest_lesumeq");
BGNRELEASE(res);
BGNRELEASE(b);
}
// badfree/util -- utility functions
void
_sysfault(void)
{
exit(1);
}
void
reverse(bigdig_p arr, int length)
{
int lhs;
int rhs;
bigdig_t tmp;
for (lhs = 0, rhs = length - 1; lhs < rhs; ++lhs, --rhs) {
tmp = arr[lhs];
arr[lhs] = arr[rhs];
arr[rhs] = tmp;
}
}
int opt_fork;
int opt_T;
// main -- main program
int
main(int argc, char **argv)
{
char *cp;
tstctl_p tst;
int tstno;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'f':
opt_fork = 1;
break;
case 'h':
usage();
break;
case 'T':
opt_T = atoi(cp + 2);
break;
default:
usage();
break;
}
}
if (opt_T <= 0)
opt_T = 1;
for (FORTSTALL(tst))
tst->tst_allow = (argc <= 0);
for (; argc > 0; --argc, ++argv) {
cp = *argv;
for (FORTSTALL(tst)) {
if (strcmp(tst->tst_tag, cp) == 0)
tst->tst_allow = 1;
}
}
for (tstno = 0; tstno < opt_T; ++tstno)
dotests();
return 0;
}
// dotests -- perform tests
void
dotests(void)
{
tstctl_p tst;
pid_t pid;
int status;
for (FORTSTALL(tst)) {
if (!tst->tst_allow)
continue;
if (!opt_fork) {
dotest(tst);
continue;
}
pid = fork();
if (pid) {
waitpid(pid, &status, 0);
continue;
}
dotest(tst);
exit(0);
}
}
// dotest -- perform test
void
dotest(tstctl_p tst)
{
tst->tst_fnc();
}
// usage -- show usage
void
usage(void)
{
tstctl_pc tst;
printf("usage: [options] [test names]\n");
printf("\n");
printf("options:\n");
printf(" -f -- run tests in forked child\n");
printf(" -T<repeat> -- test repeat count\n");
printf("\n");
printf("tests:\n");
for (FORTSTALL(tst))
printf(" %s\n", tst->tst_tag);
exit(1);
}