3 * Main test vector driver
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
38 /*----- Output ------------------------------------------------------------*/
40 void tvec_error(struct tvec_state
*tv
, const char *msg
, ...)
41 { va_list ap
; va_start(ap
, msg
); tvec_error_v(tv
, msg
, &ap
); }
42 void tvec_error_v(struct tvec_state
*tv
, const char *msg
, va_list *ap
)
43 { tv
->output
->ops
->error(tv
->output
, msg
, ap
); exit(2); }
45 void tvec_notice(struct tvec_state
*tv
, const char *msg
, ...)
48 va_start(ap
, msg
); tvec_notice_v(tv
, msg
, &ap
); va_end(ap
);
50 void tvec_notice_v(struct tvec_state
*tv
, const char *msg
, va_list *ap
)
51 { tv
->output
->ops
->notice(tv
->output
, msg
, ap
); }
53 void tvec_syntax(struct tvec_state
*tv
, int ch
, const char *expect
, ...)
56 va_start(ap
, expect
); tvec_syntax_v(tv
, ch
, expect
, &ap
); va_end(ap
);
58 void tvec_syntax_v(struct tvec_state
*tv
, int ch
,
59 const char *expect
, va_list *ap
)
65 case EOF
: strcpy(found
, "<eof>"); break;
66 case '\n': strcpy(found
, "<eol>"); break;
68 if (isprint(ch
)) sprintf(found
, "`%c'", ch
);
69 else sprintf(found
, "<#x%02x>", ch
);
72 dstr_vputf(&d
, expect
, ap
);
73 tvec_error(tv
, "syntax error: expected %s but found %s", expect
, found
);
76 void tvec_skipgroup(struct tvec_state
*tv
, const char *excuse
, ...)
79 va_start(ap
, excuse
); tvec_skipgroup_v(tv
, excuse
, &ap
); va_end(ap
);
81 void tvec_skipgroup_v(struct tvec_state
*tv
, const char *excuse
, va_list *ap
)
83 tv
->f
|= TVSF_SKIP
; tv
->grps
[TVOUT_SKIP
]++;
84 tv
->output
->ops
->skipgroup(tv
->output
, excuse
, ap
);
87 static void set_outcome(struct tvec_state
*tv
, unsigned out
)
89 tv
->f
&= ~(TVSF_ACTIVE
| TVSF_OUTMASK
);
90 tv
->f
|= out
<< TVSF_OUTSHIFT
;
93 void tvec_skip(struct tvec_state
*tv
, const char *excuse
, ...)
96 va_start(ap
, excuse
); tvec_skip_v(tv
, excuse
, &ap
); va_end(ap
);
98 void tvec_skip_v(struct tvec_state
*tv
, const char *excuse
, va_list *ap
)
100 assert(tv
->f
&TVSF_ACTIVE
);
101 set_outcome(tv
, TVOUT_SKIP
);
102 tv
->output
->ops
->skip(tv
->output
, excuse
, ap
);
105 void tvec_fail(struct tvec_state
*tv
, const char *detail
, ...)
108 va_start(ap
, detail
); tvec_fail_v(tv
, detail
, &ap
); va_end(ap
);
110 void tvec_fail_v(struct tvec_state
*tv
, const char *detail
, va_list *ap
)
112 assert((tv
->f
&TVSF_ACTIVE
) ||
113 (tv
->f
&TVSF_OUTMASK
) == (TVOUT_LOSE
<< TVSF_OUTSHIFT
));
114 set_outcome(tv
, TVOUT_LOSE
); tv
->output
->ops
->fail(tv
->output
, detail
, ap
);
117 void tvec_mismatch(struct tvec_state
*tv
)
118 { tv
->output
->ops
->mismatch(tv
->output
); }
120 void tvec_write(struct tvec_state
*tv
, const char *p
, ...)
123 va_start(ap
, p
); tvec_write_v(tv
, p
, &ap
); va_end(ap
);
125 void tvec_write_v(struct tvec_state
*tv
, const char *p
, va_list *ap
)
129 dstr_vputf(&d
, p
, ap
); tv
->output
->ops
->write(tv
->output
, d
.buf
, d
.len
);
133 /*----- Serialization and deserialization ---------------------------------*/
135 int tvec_serialize(const struct tvec_reg
*rv
,
136 const struct tvec_regdef
*regs
,
137 unsigned nr
, size_t regsz
,
138 void **p_out
, size_t *sz_out
)
141 unsigned char *bitmap
;
142 size_t i
, nbits
, bitsz
, sz
;
143 const struct tvec_regdef
*rd
;
144 const struct tvec_reg
*r
;
147 for (rd
= regs
, nbits
= 0, sz
= 0; rd
->name
; rd
++, nbits
++) {
148 if (rd
->i
>= nr
) continue;
149 r
= TVEC_GREG(rv
, rd
->i
, regsz
); if (!(r
->f
&TVRF_LIVE
)) continue;
150 sz
+= rd
->ty
->measure(&r
->v
, rd
);
152 bitsz
= (nbits
+ 7)/8; sz
+= bitsz
;
154 p
= xmalloc(sz
); buf_init(&b
, p
, sz
);
155 bitmap
= buf_get(&b
, bitsz
); assert(bitmap
); memset(bitmap
, 0, bitsz
);
156 for (rd
= regs
, i
= 0; rd
->name
; rd
++, i
++) {
157 if (rd
->i
>= nr
) continue;
158 r
= TVEC_GREG(rv
, rd
->i
, regsz
); if (!(r
->f
&TVRF_LIVE
)) continue;
159 bitmap
[rd
->i
/8] |= 1 << rd
->i
%8;
160 if (rd
->ty
->tobuf(&b
, &r
->v
, rd
)) { rc
= -1; goto end
; }
163 if (BBAD(&b
)) { rc
= -1; goto end
; }
164 *p_out
= p
; *sz_out
= BLEN(&b
); p
= 0; rc
= 0;
170 int tvec_deserialize(struct tvec_reg
*rv
,
171 const struct tvec_regdef
*regs
,
172 unsigned nr
, size_t regsz
,
173 const void *p
, size_t sz
)
176 const unsigned char *bitmap
;
177 size_t i
, nbits
, bitsz
;
178 const struct tvec_regdef
*rd
;
182 for (rd
= regs
, nbits
= 0; rd
->name
; rd
++, nbits
++);
183 bitsz
= (nbits
+ 7)/8; sz
+= bitsz
;
185 buf_init(&b
, (/*unconst*/ void *)p
, sz
);
186 bitmap
= buf_get(&b
, bitsz
); if (!bitmap
) { rc
= -1; goto end
; }
187 for (rd
= regs
, i
= 0; rd
->name
; rd
++, i
++) {
188 if (rd
->i
>= nr
) continue;
189 if (!(bitmap
[rd
->i
/8]&(1 << rd
->i
%8))) continue;
190 r
= TVEC_GREG(rv
, rd
->i
, regsz
);
191 if (rd
->ty
->frombuf(&b
, &r
->v
, rd
)) { rc
= -1; goto end
; }
195 if (BBAD(&b
)) { rc
= -1; goto end
; }
201 /*----- Benchmarking ------------------------------------------------------*/
203 struct bench_state
*tvec_benchstate
;
208 const struct tvec_reg
*in
; struct tvec_reg
*out
;
212 static void benchloop_outer(unsigned long n
, void *p
)
213 { struct benchrun
*r
= p
; while (n
--) r
->fn(r
->in
, r
->out
, r
->ctx
); }
215 static void benchloop_inner(unsigned long n
, void *p
)
216 { struct benchrun
*r
= p
; *r
->n
= n
; r
->fn(r
->in
, r
->out
, r
->ctx
); }
218 int tvec_ensurebench(struct tvec_state
*tv
, struct bench_state
**b_out
)
220 const struct tvec_bench
*tvb
= tv
->test
->arg
.p
;
221 struct bench_state
**bb
;
222 struct bench_timer
*bt
;
224 if (tvb
->b
) bb
= tvb
->b
;
225 else bb
= &tvec_benchstate
;
228 bt
= bench_createtimer();
229 if (!bt
) { tvec_skip(tv
, "failed to create timer"); return (-1); }
230 *bb
= xmalloc(sizeof(**bb
)); bench_init(*bb
, bt
);
231 } else if (!(*bb
)->tm
)
232 { tvec_skip(tv
, "failed to create timer"); return (-1); }
238 void tvec_bench(struct tvec_state
*tv
)
240 const struct tvec_bench
*tvb
= tv
->test
->arg
.p
;
241 struct bench_state
*b
;
242 struct bench_timing tm
;
246 if (tvec_ensurebench(tv
, &b
)) goto end_0
;
248 r
.in
= tv
->in
; r
.out
= tv
->out
; r
.fn
= tv
->test
->fn
;
249 if (tvb
->ctxsz
) r
.ctx
= xmalloc(tvb
->ctxsz
);
251 if (tvb
->setup
&& tvb
->setup(tv
->in
, tv
->out
, &tvb
->arg
, r
.ctx
))
252 { tvec_skip(tv
, "benchmark setup failed"); goto end_1
; }
255 { r
.n
= 0; loopfn
= benchloop_outer
; }
257 { r
.n
= &TVEC_REG(tv
, in
, tvb
->riter
)->v
.u
; loopfn
= benchloop_inner
; }
259 tv
->output
->ops
->bbench(tv
->output
);
260 if (bench_measure(&tm
, b
, loopfn
, &r
))
261 { tv
->output
->ops
->ebench(tv
->output
, 0); goto end_2
; }
262 tv
->output
->ops
->ebench(tv
->output
, &tm
);
265 if (tvb
->teardown
) tvb
->teardown(r
.ctx
);
267 if (r
.ctx
) xfree(r
.ctx
);
272 /*----- Main machinery ----------------------------------------------------*/
274 void tvec_skipspc(struct tvec_state
*tv
)
280 if (ch
== EOF
) break;
281 else if (ch
== '\n' || !isspace(ch
)) { ungetc(ch
, tv
->fp
); break; }
285 void tvec_flushtoeol(struct tvec_state
*tv
, unsigned f
)
292 case '\n': tv
->lno
++; return;
294 case ';': f
|= TVFF_ALLOWANY
; break;
296 if (!(f
&TVFF_ALLOWANY
) && !isspace(ch
))
297 tvec_syntax(tv
, ch
, "end-of-line");
303 int tvec_nexttoken(struct tvec_state
*tv
)
305 enum { TAIL
, NEWLINE
, INDENT
, COMMENT
};
320 if (s
== NEWLINE
|| s
== INDENT
) { ungetc(ch
, tv
->fp
); return (-1); }
321 else { tv
->lno
++; s
= NEWLINE
; }
326 { if (s
== NEWLINE
) s
= INDENT
; }
327 else if (s
!= COMMENT
) {
329 if (s
== NEWLINE
) return (-1);
337 int tvec_readword(struct tvec_state
*tv
, dstr
*d
, const char *delims
,
338 const char *expect
, ...)
343 va_start(ap
, expect
);
344 rc
= tvec_readword_v(tv
, d
, delims
, expect
, &ap
);
348 int tvec_readword_v(struct tvec_state
*tv
, dstr
*d
, const char *delims
,
349 const char *expect
, va_list *ap
)
354 if (ch
== '\n' || ch
== EOF
|| ch
== ';' ||
355 (delims
&& strchr(delims
, ch
))) {
356 if (expect
) tvec_syntax(tv
, ch
, expect
, ap
);
357 else { ungetc(ch
, tv
->fp
); return (-1); }
359 if (d
->len
) DPUTC(d
, ' ');
363 } while (ch
!= EOF
&& !isspace(ch
) && (!delims
|| !strchr(delims
, ch
)));
364 DPUTZ(d
); if (ch
!= EOF
) ungetc(ch
, tv
->fp
);
368 static void init_registers(struct tvec_state
*tv
)
370 const struct tvec_regdef
*rd
;
373 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
374 assert(rd
->i
< tv
->nreg
); r
= TVEC_REG(tv
, in
, rd
->i
);
375 rd
->ty
->init(&r
->v
, rd
); r
->f
= 0;
376 if (rd
->i
< tv
->nrout
)
377 { r
= TVEC_REG(tv
, out
, rd
->i
); rd
->ty
->init(&r
->v
, rd
); r
->f
= 0; }
382 static void release_registers(struct tvec_state
*tv
)
384 const struct tvec_regdef
*rd
;
387 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
388 assert(rd
->i
< tv
->nreg
); r
= TVEC_REG(tv
, in
, rd
->i
);
389 rd
->ty
->release(&r
->v
, rd
); r
->f
= 0;
390 if (rd
->i
< tv
->nrout
)
391 { r
= TVEC_REG(tv
, out
, rd
->i
); rd
->ty
->release(&r
->v
, rd
); r
->f
= 0; }
395 void tvec_check(struct tvec_state
*tv
, const char *detail
, ...)
398 va_start(ap
, detail
); tvec_check_v(tv
, detail
, &ap
); va_end(ap
);
400 void tvec_check_v(struct tvec_state
*tv
, const char *detail
, va_list *ap
)
402 const struct tvec_regdef
*rd
;
403 const struct tvec_reg
*rin
, *rout
;
405 #define f_mismatch 1u
407 if (tv
->expst
!= tv
->st
) f
|= f_mismatch
;
408 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
409 if (rd
->i
>= tv
->nrout
) continue;
410 rin
= TVEC_REG(tv
, in
, rd
->i
); rout
= TVEC_REG(tv
, out
, rd
->i
);
411 if (!rin
->f
&TVRF_LIVE
) continue;
412 if (!rd
->ty
->eq(&rin
->v
, &rout
->v
, rd
)) f
|= f_mismatch
;
414 if (!(f
&f_mismatch
)) return;
416 tvec_fail_v(tv
, detail
, ap
);
422 void tvec_runtest(struct tvec_state
*tv
)
424 tv
->test
->fn(tv
->in
, tv
->out
, (/*unconst*/ void *)tv
->test
->arg
.p
);
428 static void begin_test(struct tvec_state
*tv
)
430 tv
->f
|= TVSF_ACTIVE
; tv
->f
&= ~TVSF_OUTMASK
; tv
->st
= '.';
431 tv
->output
->ops
->btest(tv
->output
);
434 void tvec_endtest(struct tvec_state
*tv
)
438 if (tv
->f
&TVSF_ACTIVE
) out
= TVOUT_WIN
;
439 else out
= (tv
->f
&TVSF_OUTMASK
) >> TVSF_OUTSHIFT
;
440 assert(out
< TVOUT_LIMIT
); tv
->curr
[out
]++;
441 tv
->output
->ops
->etest(tv
->output
, out
);
445 static void check(struct tvec_state
*tv
)
447 const struct tvec_regdef
*rd
;
449 if (!(tv
->f
&TVSF_OPEN
)) return;
451 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
452 if (TVEC_REG(tv
, in
, rd
->i
)->f
&TVRF_LIVE
)
453 { if (rd
->i
< tv
->nrout
) TVEC_REG(tv
, out
, rd
->i
)->f
|= TVRF_LIVE
; }
454 else if (!(rd
->f
&TVRF_OPT
))
455 tvec_error(tv
, "required register `%s' not set in test `%s'",
456 rd
->name
, tv
->test
->name
);
459 if (!(tv
->f
&TVSF_SKIP
))
460 { begin_test(tv
); tv
->test
->run(tv
); tvec_endtest(tv
); }
462 tv
->f
&= ~TVSF_OPEN
; release_registers(tv
); init_registers(tv
);
465 static void begin_test_group(struct tvec_state
*tv
)
469 tv
->output
->ops
->bgroup(tv
->output
);
472 for (i
= 0; i
< TVOUT_LIMIT
; i
++) tv
->curr
[i
] = 0;
473 if (tv
->test
->preflight
) tv
->test
->preflight(tv
);
476 void tvec_reportgroup(struct tvec_state
*tv
)
478 unsigned i
, out
, nrun
;
480 for (i
= 0, nrun
= 0; i
< TVOUT_LIMIT
; i
++)
481 { nrun
+= tv
->curr
[i
]; tv
->all
[i
] += tv
->curr
[i
]; }
483 if (tv
->curr
[TVOUT_SKIP
] == nrun
)
484 { out
= TVOUT_SKIP
; tvec_skipgroup(tv
, nrun ?
0 : "no tests to run"); }
486 if (tv
->curr
[TVOUT_LOSE
]) out
= TVOUT_LOSE
;
487 else out
= TVOUT_WIN
;
488 tv
->grps
[out
]++; tv
->output
->ops
->egroup(tv
->output
, out
);
492 static void end_test_group(struct tvec_state
*tv
)
494 if (!tv
->test
) return;
495 if (tv
->f
&TVSF_OPEN
) check(tv
);
496 if (!(tv
->f
&TVSF_SKIP
)) tvec_reportgroup(tv
);
497 release_registers(tv
); tv
->test
= 0;
500 void tvec_read(struct tvec_state
*tv
, const char *infile
, FILE *fp
)
503 const struct tvec_test
*test
;
504 const struct tvec_regdef
*rd
;
508 tv
->infile
= infile
; tv
->lno
= 1; tv
->fp
= fp
;
519 DRESET(&d
); tvec_readword(tv
, &d
, "];", "group name");
520 for (test
= tv
->tests
; test
->name
; test
++)
521 if (STRCMP(d
.buf
, ==, test
->name
)) goto found_test
;
522 tvec_error(tv
, "unknown test group `%s'", d
.buf
);
525 ch
= getc(tv
->fp
); if (ch
!= ']') tvec_syntax(tv
, ch
, "`]'");
526 tvec_flushtoeol(tv
, 0);
529 begin_test_group(tv
);
534 if (tv
->f
&TVSF_OPEN
) check(tv
);
544 { tvec_flushtoeol(tv
, TVFF_ALLOWANY
); continue; }
546 { tvec_flushtoeol(tv
, 0); check(tv
); }
547 } else if (ch
== ';')
548 { tvec_flushtoeol(tv
, TVFF_ALLOWANY
); continue; }
551 DRESET(&d
); tvec_readword(tv
, &d
, "=:;", "register name");
552 tvec_skipspc(tv
); ch
= getc(tv
->fp
);
553 if (ch
!= '=' && ch
!= ':') tvec_syntax(tv
, ch
, "`=' or `:'");
555 if (d
.buf
[0] == '@') {
556 if (STRCMP(d
.buf
, ==, "@status")) {
557 if (!(tv
->f
&TVSF_OPEN
))
558 { tv
->test_lno
= tv
->lno
; tv
->f
|= TVSF_OPEN
; }
560 if (ch
== EOF
|| ch
== '\n' || ch
== ';')
561 tvec_syntax(tv
, ch
, "status character");
564 if (ch
== EOF
|| ch
== '\n')
565 tvec_syntax(tv
, ch
, "escaped status character");
568 tvec_flushtoeol(tv
, 0);
570 tvec_error(tv
, "unknown special register `%s'", d
.buf
);
572 if (!tv
->test
) tvec_error(tv
, "no current test");
573 for (rd
= tv
->test
->regs
; rd
->name
; rd
++)
574 if (STRCMP(rd
->name
, ==, d
.buf
)) goto found_reg
;
575 tvec_error(tv
, "unknown register `%s' for test `%s'",
576 d
.buf
, tv
->test
->name
);
578 if (!(tv
->f
&TVSF_OPEN
))
579 { tv
->test_lno
= tv
->lno
; tv
->f
|= TVSF_OPEN
; }
581 r
= TVEC_REG(tv
, in
, rd
->i
);
583 tvec_error(tv
, "register `%s' already set", rd
->name
);
584 rd
->ty
->parse(&r
->v
, rd
, tv
); r
->f
|= TVRF_LIVE
;
592 tv
->infile
= 0; tv
->fp
= 0;
596 /*----- Ad-hoc testing ----------------------------------------------------*/
598 static const struct tvec_regdef no_regs
= { 0, 0, 0, 0, { 0 } };
600 static void fakerun(struct tvec_state
*tv
)
601 { assert(!"fake run function"); }
602 static void fakefn(const struct tvec_reg
*in
, struct tvec_reg
*out
, void *p
)
603 { assert(!"fake test function"); }
605 void tvec_adhoc(struct tvec_state
*tv
, struct tvec_test
*t
)
607 t
->name
= "<unset>"; t
->regs
= &no_regs
;
608 t
->preflight
= 0; t
->run
= fakerun
; t
->fn
= fakefn
; t
->arg
.p
= 0;
612 void tvec_begingroup(struct tvec_state
*tv
, const char *name
,
613 const char *file
, unsigned lno
)
615 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->tests
;
617 t
->name
= name
; tv
->test
= t
;
618 tv
->infile
= file
; tv
->lno
= tv
->test_lno
= lno
;
619 begin_test_group(tv
);
622 void tvec_endgroup(struct tvec_state
*tv
)
624 if (!(tv
->f
&TVSF_SKIP
)) tvec_reportgroup(tv
);
628 void tvec_begintest(struct tvec_state
*tv
, const char *file
, unsigned lno
)
630 tv
->infile
= file
; tv
->lno
= tv
->test_lno
= lno
;
631 begin_test(tv
); tv
->f
|= TVSF_OPEN
;
637 const char *saved_file
; unsigned saved_lno
;
640 static void adhoc_claim_setup(struct tvec_state
*tv
,
641 struct adhoc_claim
*ck
,
642 const struct tvec_regdef
*regs
,
643 const char *file
, unsigned lno
)
645 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->test
;
649 if (!(tv
->f
&TVSF_OPEN
))
650 { ck
->f
|= ACF_FRESH
; tvec_begintest(tv
, file
, lno
); }
652 ck
->saved_file
= tv
->infile
; if (file
) tv
->infile
= file
;
653 ck
->saved_lno
= tv
->test_lno
; if (file
) tv
->test_lno
= lno
;
654 t
->regs
= regs ? regs
: &no_regs
;
659 static void adhoc_claim_teardown(struct tvec_state
*tv
,
660 struct adhoc_claim
*ck
)
662 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->test
;
665 tv
->infile
= ck
->saved_file
; tv
->test_lno
= ck
->saved_lno
;
667 if (ck
->f
&ACF_FRESH
) tvec_endtest(tv
);
670 int tvec_claim(struct tvec_state
*tv
, int ok
,
671 const char *file
, unsigned lno
, const char *expr
, ...)
673 struct adhoc_claim ck
;
676 adhoc_claim_setup(tv
, &ck
, 0, file
, lno
);
678 { va_start(ap
, expr
); tvec_fail_v(tv
, expr
, &ap
); va_end(ap
); }
679 adhoc_claim_teardown(tv
, &ck
);
683 int tvec_claimeq(struct tvec_state
*tv
,
684 const struct tvec_regty
*ty
, const union tvec_misc
*arg
,
685 const char *file
, unsigned lno
, const char *expr
)
687 struct tvec_regdef regs
[2];
688 struct adhoc_claim ck
;
691 tv
->in
[0].f
= tv
->out
[0].f
= TVRF_LIVE
;
693 regs
[0].name
= "value"; regs
[0].i
= 0;
694 regs
[0].ty
= ty
; regs
[0].f
= 0;
695 if (arg
) regs
[0].arg
= *arg
;
696 else regs
[0].arg
.p
= 0;
700 adhoc_claim_setup(tv
, &ck
, regs
, file
, lno
);
701 ok
= ty
->eq(&tv
->in
[0].v
, &tv
->out
[0].v
, ®s
[0]);
702 if (!ok
) { tvec_fail(tv
, "%s", expr
); tvec_mismatch(tv
); }
703 adhoc_claim_teardown(tv
, &ck
);
707 /*----- Session lifecycle -------------------------------------------------*/
709 void tvec_begin(struct tvec_state
*tv_out
,
710 const struct tvec_info
*info
,
711 struct tvec_output
*o
)
717 assert(info
->nrout
<= info
->nreg
);
718 tv_out
->nrout
= info
->nrout
; tv_out
->nreg
= info
->nreg
;
719 tv_out
->regsz
= info
->regsz
;
720 tv_out
->in
= xmalloc(tv_out
->nreg
*tv_out
->regsz
);
721 tv_out
->out
= xmalloc(tv_out
->nrout
*tv_out
->regsz
);
722 for (i
= 0; i
< tv_out
->nreg
; i
++) {
723 TVEC_REG(tv_out
, in
, i
)->f
= 0;
724 if (i
< tv_out
->nrout
) TVEC_REG(tv_out
, out
, i
)->f
= 0;
727 for (i
= 0; i
< TVOUT_LIMIT
; i
++)
728 tv_out
->curr
[i
] = tv_out
->all
[i
] = tv_out
->grps
[i
] = 0;
730 tv_out
->tests
= info
->tests
; tv_out
->test
= 0;
731 tv_out
->infile
= 0; tv_out
->lno
= 0; tv_out
->fp
= 0;
732 o
->tv
= tv_out
; tv_out
->output
= o
;
734 tv_out
->output
->ops
->bsession(tv_out
->output
);
737 int tvec_end(struct tvec_state
*tv
)
739 int rc
= tv
->output
->ops
->esession(tv
->output
);
741 tv
->output
->ops
->destroy(tv
->output
);
742 xfree(tv
->in
); xfree(tv
->out
);
746 /*----- That's all, folks -------------------------------------------------*/