chiark / gitweb /
bc2be620f510b86eb5c84a70370fd214a2026446
[trains.git] / hostside / record.c
1 /*
2  * ASCII-format record and config file reader/writer
3  *
4  * File format:
5  *
6  *  max-trains num
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>
12  *
13  * speed is in um/s, upwait and downwait are in us
14  */
15
16 #include <sys/mman.h>
17
18 #include "record-i.h"
19 #include "record-l.h"
20
21 /*---------- input and error handling ----------*/
22
23 static const char *filename;
24
25 void record_yyerror(const char *m) {
26   die("config: %s:%d: %s", filename, record_yylineno, m);
27 }
28
29 /*---------- pname lookup (also handles train counting) ----------*/
30
31 static char **train_pnames;
32
33 Train *record_pname2train(const char *pname) {
34   int tran;
35   char **trap;
36   
37   for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
38     if (!strcmp(*trap, pname)) goto found;
39   }
40   if (trains) record_yyerror("number of trains changed between passes!");
41   tran= n_trains++;
42   train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
43   train_pnames[tran]= mstrdup(pname);
44
45 found:
46   if (!trains) return 0;
47   return trains + tran;
48 }
49
50 Segment *record_pname2seg(const char *pname) {
51   int i;
52   const SegmentInfo *segi;
53   
54   for (i=0, segi=info_segments;
55        i<NUM_SEGMENTS;
56        i++, segi++)
57     if (!strcmp(segi->pname, pname))
58       return segments + i;
59   
60   return 0; /* silently discard data for segments no longer in the layout */
61 }
62
63 /*---------- zone allocator for strings ----------*/
64
65 typedef struct TempZoneBlock TempZoneBlock;
66 struct TempZoneBlock {
67   TempZoneBlock *next; /* for deallocation */
68   char buf[1];
69 };
70
71 static TempZoneBlock *tempzones_head;
72 static char *tempzone_ptr;
73 static int tempzone_remain, tempzone_head_len;
74
75 char *record_tempzone_strdup(const char *s) {
76   int l;
77   char *r;
78   
79   l= strlen(s) + 1;
80   if (l > tempzone_remain) {
81     TempZoneBlock *new;
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;
87     tempzones_head= new;
88   }
89   r= tempzone_ptr;
90   tempzone_remain -= l;
91   tempzone_ptr += l;
92   memcpy(r,s,l);
93   return r;
94 }
95
96 void record_tempzone_clear(void) {
97   TempZoneBlock *clear, *clear_next;
98   if (!tempzones_head) return;
99
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;
105 }
106
107 /*---------- semantic actions ----------*/
108 /*
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.
111  */
112
113 void record_train_is(Train *tra, int addr, int head, int det, int tail) {
114   tra->addr= addr;
115   tra->head= head;
116   tra->detectable= det;
117   tra->tail= tail;
118 }
119
120 void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
121   tra->foredetect= seg;
122   tra->maxinto= maxi;
123   tra->uncertainty= unc;
124   tra->backwards= backw;
125 }
126   
127 void record_seg_has(Segment *seg, int backw, Train *tra) {
128   seg->owner= tra;
129   seg->tr_backwards= backw;
130 }
131
132 void record_seg_at(Segment *seg, const char *movposcombpname) {
133   const SegPosCombInfo *spci;
134   int i;
135   
136   for (i=0, spci=seg->i->poscombs;
137        i<seg->i->n_poscombs;
138        i++, spci++)
139     if (!strcmp(spci->pname, movposcombpname))
140       goto found;
141   return;
142   
143 found:
144   seg->movposcomb= i;
145 }
146
147 /*---------- speed curves ----------*/
148
149 static SpeedCurveEntry *curvebuf;
150 static int curvebufsz, curvebufused;
151
152 void record_train_step_count(void) {
153   curvebufsz++;
154 }
155
156 void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
157   Train *other;
158   SpeedCurveEntry *new;
159   int i;
160   
161   if (curvebufused >= curvebufsz)
162     record_yyerror("more speed points on 2nd pass!");
163
164   if (!tra->accel.curve) {
165     tra->accel.curve= curvebuf + curvebufused;
166   }
167
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;
176        i<n_trains;
177        i++, other++)
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. */
189
190   new= &tra->accel.curve[tra->accel.curvesz++];
191   curvebufused++;
192
193   new->step= step;
194   new->speed= speed * 1e-6 * SPEED_UNIT;
195   new->upwait= upw * 1e-3;
196   new->downwait= downw * 1e-3;
197 }
198
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;
204 }
205
206 static void sort_curves(void) {
207   int i;
208   Train *tra;
209   
210   for (i=0, tra=trains;
211        i<n_trains;
212        i++, tra++) {
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);
220     }
221   }
222 }
223
224 /*---------- persistent data file layout ----------*/
225
226 static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
227   void *r;
228   size_t totalsz;
229   
230   while (*offset % sz) {
231     if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
232     (*offset)++;
233   }
234   totalsz= sz*count;
235   if (mapbase) {
236     r= (Byte*)mapbase + *offset;
237     memset(r,0,totalsz);
238   } else {
239     r= 0;
240   }
241   *offset += totalsz;
242   return r;
243 }
244
245 static void alloc(void) {
246   Train *tra; Segment *seg; const SegmentInfo *segi;
247   void *mapbase=0;
248   char **trap;
249   int i, phase, offset, datalen=0;
250
251 #define ALLOC(array,count) \
252   ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
253
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
258      */
259     offset= 0;
260
261     if (phase==1)
262       mapbase= record_allocate(datalen);
263
264 #define PHI_SAVE(x) {                           \
265       typeof(x) *p;                             \
266       ALLOC(p,1);                               \
267       if (phase==2)                             \
268         memcpy(p, &(x), sizeof(x));             \
269     }
270
271     DO_PERSIST_HEADER_ITEMS(PHI_SAVE, PHI_SAVE, PHI_SAVE)
272     
273     if (phase==2)
274       break;
275
276     ALLOC(trains, n_trains);
277     for (i=0, tra=trains, trap=train_pnames;
278          i<n_trains;
279          i++, tra++, trap++) {
280       char *pname;
281       ALLOC(pname, strlen(*trap)+1);
282       if (phase) {
283         strcpy(pname, *trap);
284         free(*trap);
285         tra->pname= *trap= pname;
286         tra->addr= -1;
287         tra->foredetect= 0;
288       } 
289     }
290
291     ALLOC(segments, info_nsegments);
292     if (phase)
293       for (i=0, seg=segments, segi=info_segments;
294            i<NUM_SEGMENTS;
295            i++, seg++, segi++) {
296         seg->owner= 0;
297         seg->movposcomb= -1;
298         seg->i= segi;
299       }
300
301     if (phase==0)
302       datalen= offset;
303   }
304
305   curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
306 }
307   
308 /*---------- entrypoint from main, and its subroutines ----------*/
309
310 static void parse_file(const char *why) {
311   FILE *f;
312
313   f= fopen(filename,"r");
314   if (!f) diee("config: cannot open %s: %s", why, filename);
315   record_yyrestart(f);
316   record_yyparse();
317 }
318
319 static void parse_pass(const char **argv) {
320   while ((filename= *argv++))
321     parse_file("commandline-specified record file");
322
323   filename= persist_record_converted;
324   if (filename)
325     parse_file("converted persistent data file");
326 }
327
328 void records_parse(const char **argv) {
329   parse_pass(argv); /* trains==0: counts trains and curve points. */
330   alloc();
331   parse_pass(argv); /* trains!=0: populates data area */
332   record_tempzone_clear();
333   sort_curves();
334 }