2 * ASCII-format record and config file reader/writer
7 * train <trainpn> at [-]<foredetectpn>:<maxinto>+-<uncertainty>
8 * train <trainpn> is <addr> <head>+<detectable>+<tail>
9 * train <trainpn> step <step>=<speed> <upwait>/<downwait>
10 * seg <segpn> has [-]<ownerpn>
11 * seg <segpn> at <movposcomb>
13 * speed is in um/s, upwait and downwait are in us
21 /*---------- input and error handling ----------*/
23 static const char *filename;
25 void record_yyerror(const char *m) {
26 die("config: %s:%d: %s", filename, record_yylineno, m);
29 /*---------- pname lookup (also handles train counting) ----------*/
31 static char **train_pnames;
33 Train *record_pname2train(const char *pname) {
37 for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
38 if (!strcmp(*trap, pname)) goto found;
40 if (trains) record_yyerror("number of trains changed between passes!");
42 train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
43 train_pnames[tran]= mstrdup(pname);
46 if (!trains) return 0;
50 Segment *record_pname2seg(const char *pname) {
52 const SegmentInfo *segi;
54 for (i=0, segi=info_segments;
57 if (!strcmp(segi->pname, pname))
60 return 0; /* silently discard data for segments no longer in the layout */
63 /*---------- zone allocator for strings ----------*/
65 typedef struct TempZoneBlock TempZoneBlock;
66 struct TempZoneBlock {
67 TempZoneBlock *next; /* for deallocation */
71 static TempZoneBlock *tempzones_head;
72 static char *tempzone_ptr;
73 static int tempzone_remain, tempzone_head_len;
75 char *record_tempzone_strdup(const char *s) {
80 if (l > tempzone_remain) {
82 assert(l < INT_MAX/20);
83 tempzone_head_len= tempzone_remain= l*10 + 65536;
84 new= mmalloc(sizeof(new) + tempzone_remain);
85 tempzone_ptr= (char*)new + sizeof(new);
86 new->next= tempzones_head;
96 void record_tempzone_clear(void) {
97 TempZoneBlock *clear, *clear_next;
98 if (!tempzones_head) return;
100 clear= tempzones_head->next;
101 while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
102 tempzones_head->next= 0;
103 tempzone_remain= tempzone_head_len;
104 tempzone_ptr= tempzones_head->buf;
107 /*---------- semantic actions ----------*/
109 * These are all called only during the second pass, when trains is non-0.
110 * Also, all Train* and Segment* arguments are known to be non-0.
113 void record_train_is(Train *tra, int addr, int head, int det, int tail) {
116 tra->detectable= det;
120 void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
121 tra->foredetect= seg;
123 tra->uncertainty= unc;
124 tra->backwards= backw;
127 void record_seg_has(Segment *seg, int backw, Train *tra) {
129 seg->tr_backwards= backw;
132 void record_seg_at(Segment *seg, const char *movposcombpname) {
133 const SegPosCombInfo *spci;
136 for (i=0, spci=seg->i->poscombs;
137 i<seg->i->n_poscombs;
139 if (!strcmp(spci->pname, movposcombpname))
147 /*---------- speed curves ----------*/
149 static SpeedCurveEntry *curvebuf;
150 static int curvebufsz, curvebufused;
152 void record_train_step_count(void) {
156 void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
158 SpeedCurveEntry *new;
161 if (curvebufused >= curvebufsz)
162 record_yyerror("more speed points on 2nd pass!");
164 if (!tra->accel.curve) {
165 tra->accel.curve= curvebuf + curvebufused;
168 /* Make room for an extra speed step point for this train,
169 * by moving all of the other trains up. First move the data: */
170 memmove(tra->accel.curve + tra->accel.curvesz + 1,
171 tra->accel.curve + tra->accel.curvesz,
172 (char*)(curvebuf + curvebufused)
173 - (char*)(tra->accel.curve + tra->accel.curvesz));
174 /* Then adjust everyone's pointers: */
175 for (i=0, other=trains;
178 if (other != tra && other->accel.curve &&
179 other->accel.curve >= tra->accel.curve)
180 other->accel.curve++;
181 /* ... that whole thing is O(n^2) if the speed points aren't sorted
182 * by train; we hope they are, in which case we are always adding
183 * to the last train in the list and there is then no data to copy,
184 * which makes it O(n*t) where n is the number of speed points and
185 * t is the number of trains. If we cared we could optimise away
186 * the loop in the common case where this train is right at the end
187 * but a special case seems like asking for a bug when we do have
188 * out of order speed points. */
190 new= &tra->accel.curve[tra->accel.curvesz++];
194 new->speed= speed * 1e-6 * SPEED_UNIT;
195 new->upwait= upw * 1e-3;
196 new->downwait= downw * 1e-3;
199 static int speedcurveentry_compare(const void *av, const void *bv) {
200 const SpeedCurveEntry *a= av, *b= bv;
201 if (a->step == b->step)
202 record_yyerror("multiple speed curve points at same step");
203 return a->step - b->step;
206 static void sort_curves(void) {
210 for (i=0, tra=trains;
213 if (tra->accel.curve) {
214 if (tra->accel.curvesz < 2)
215 die("config: speed curve too short for %s", tra->pname);
216 qsort(tra->accel.curve, tra->accel.curvesz,
217 sizeof(*tra->accel.curve), speedcurveentry_compare);
218 if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
219 die("config: speed curve missing zero point for %s", tra->pname);
224 /*---------- persistent data file layout ----------*/
226 static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
230 while (*offset % sz) {
231 if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
236 r= (Byte*)mapbase + *offset;
245 static void alloc(void) {
246 Train *tra; Segment *seg; const SegmentInfo *segi;
249 int i, phase, offset, datalen=0;
251 #define ALLOC(array,count) \
252 ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
254 for (phase=0; ; phase++) {
255 /* phase 0: count up how much space will be needed
256 * phase 1: fill in most of the details, leaving header blank
257 * phase 2: fill in the header, and then exit before doing rest
262 mapbase= record_allocate(datalen);
264 #define PHI_SAVE(x) { \
268 memcpy(p, &(x), sizeof(x)); \
271 DO_PERSIST_HEADER_ITEMS(PHI_SAVE, PHI_SAVE, PHI_SAVE)
276 ALLOC(trains, n_trains);
277 for (i=0, tra=trains, trap=train_pnames;
279 i++, tra++, trap++) {
281 ALLOC(pname, strlen(*trap)+1);
283 strcpy(pname, *trap);
285 tra->pname= *trap= pname;
291 ALLOC(segments, info_nsegments);
293 for (i=0, seg=segments, segi=info_segments;
295 i++, seg++, segi++) {
305 curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
308 /*---------- entrypoint from main, and its subroutines ----------*/
310 static void parse_file(const char *why) {
313 f= fopen(filename,"r");
314 if (!f) diee("config: cannot open %s: %s", why, filename);
319 static void parse_pass(const char **argv) {
320 while ((filename= *argv++))
321 parse_file("commandline-specified record file");
323 filename= persist_record_converted;
325 parse_file("converted persistent data file");
328 void records_parse(const char **argv) {
329 parse_pass(argv); /* trains==0: counts trains and curve points. */
331 parse_pass(argv); /* trains!=0: populates data area */
332 record_tempzone_clear();