chiark / gitweb /
b3b508f86e6856feed5967ae5c31f347e41ed695
[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  *  train <trainpn> home [-]<segpn> [-]<segpn> [-]<segpn>
11  *  seg <segpn> has [-]<ownerpn>
12  *  seg <segpn> at <movposcomb>
13  *  feature <trainorfeatpn> <letter> is <addr> <nmranum>
14  *  feature <trainorfeatpn> <letter> is <addr> step [-]<nmranum>
15  *
16  * speed is in um/s, upwait and downwait are in us
17  */
18
19 #include <sys/mman.h>
20
21 #include "record-i.h"
22 #include "record-l.h"
23
24 /*---------- input and error handling ----------*/
25
26 static const char *filename;
27
28 void record_yyerror(const char *m) {
29   die("config: %s:%d: %s", filename, record_yylineno, m);
30 }
31
32 /*---------- pname lookup (also handles train counting) ----------*/
33
34 static char **train_pnames;
35
36 Train *record_pname2train(const char *pname) {
37   int tran;
38   char **trap;
39   
40   for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
41     if (!strcmp(*trap, pname)) goto found;
42   }
43   if (trains) record_yyerror("number of trains changed between passes!");
44   tran= n_trains++;
45   train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
46   train_pnames[tran]= mstrdup(pname);
47
48 found:
49   if (!trains) return 0;
50   return trains + tran;
51 }
52
53 Segment *record_pname2seg(const char *pname) {
54   SEG_IV;
55
56   FOR_SEG
57     if (!strcmp(segi->pname, pname))
58       return seg;
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_train_home(Train *tra, int backw, Segment *seg) {
128   if (!tra) return;
129   seg->home= tra;
130   seg->ho_backwards= backw;
131 }
132   
133 void record_seg_has(Segment *seg, int backw, Train *tra) {
134   seg->owner= tra;
135   seg->tr_backwards= backw;
136 }
137
138 void record_seg_at(Segment *seg, const char *movposcombpname) {
139   const SegPosCombInfo *spci;
140   int poscomb;
141   
142   for (poscomb=0, spci=seg->i->poscombs;
143        poscomb < seg->i->n_poscombs;
144        poscomb++, spci++)
145     if (!strcmp(spci->pname, movposcombpname))
146       goto found;
147   return;
148   
149 found:
150   seg->movposcomb= poscomb;
151 }
152
153 /*---------- speed curves ----------*/
154
155 static SpeedCurveEntry *curvebuf;
156 static int curvebufsz, curvebufused;
157
158 void record_train_step_count(void) {
159   curvebufsz++;
160 }
161
162 void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
163   Train *other;
164   SpeedCurveEntry *new;
165   int i;
166   
167   if (curvebufused >= curvebufsz)
168     record_yyerror("more speed points on 2nd pass!");
169
170   if (!tra->accel.curve) {
171     tra->accel.curve= curvebuf + curvebufused;
172   }
173
174   /* Make room for an extra speed step point for this train,
175    * by moving all of the other trains up.  First move the data: */
176   memmove(tra->accel.curve + tra->accel.curvesz + 1,
177           tra->accel.curve + tra->accel.curvesz,
178           (char*)(curvebuf + curvebufused)
179           - (char*)(tra->accel.curve + tra->accel.curvesz));
180   /* Then adjust everyone's pointers: */
181   for (i=0, other=trains;
182        i<n_trains;
183        i++, other++)
184     if (other != tra && other->accel.curve &&
185         other->accel.curve >= tra->accel.curve)
186       other->accel.curve++;
187   /* ... that whole thing is O(n^2) if the speed points aren't sorted
188    * by train; we hope they are, in which case we are always adding
189    * to the last train in the list and there is then no data to copy,
190    * which makes it O(n*t) where n is the number of speed points and
191    * t is the number of trains.  If we cared we could optimise away
192    * the loop in the common case where this train is right at the end
193    * but a special case seems like asking for a bug when we do have
194    * out of order speed points. */
195
196   new= &tra->accel.curve[tra->accel.curvesz++];
197   curvebufused++;
198
199   new->step= step;
200   new->speed= speed * 1e-6 * SPEED_UNIT;
201   new->upwait= upw * 1e-3;
202   new->downwait= downw * 1e-3;
203 }
204
205 static int speedcurveentry_compare(const void *av, const void *bv) {
206   const SpeedCurveEntry *a= av, *b= bv;
207   if (a->step == b->step)
208     record_yyerror("multiple speed curve points at same step");
209   return a->step - b->step;
210 }
211
212 static void sort_curves(void) {
213   TRA_IV;
214
215   FOR_TRA {
216     if (tra->accel.curve) {
217       if (tra->accel.curvesz < 2)
218         die("config: speed curve too short for %s", tra->pname);
219       qsort(tra->accel.curve, tra->accel.curvesz,
220             sizeof(*tra->accel.curve), speedcurveentry_compare);
221       if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
222         die("config: speed curve missing zero point for %s", tra->pname);
223     }
224   }
225 }
226
227 /*---------- persistent data file layout ----------*/
228
229 static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
230   void *r;
231   size_t totalsz;
232   
233   while (*offset % sz) {
234     if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
235     (*offset)++;
236   }
237   totalsz= sz*count;
238   if (mapbase) {
239     r= (Byte*)mapbase + *offset;
240     memset(r,0,totalsz);
241   } else {
242     r= 0;
243   }
244   *offset += totalsz;
245   return r;
246 }
247
248 static void alloc(void) {
249   TRA_IV;
250   SEG_IV;
251   void *mapbase=0;
252   char **trap;
253   int phase, offset, datalen=0;
254
255 #define ALLOC(array,count) \
256   ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
257
258   for (phase=0; ; phase++) {
259     /* phase 0: count up how much space will be needed
260      * phase 1: fill in most of the details, leaving header blank
261      * phase 2: fill in the header, and then exit before doing rest
262      */
263     offset= 0;
264
265     if (phase==1)
266       mapbase= record_allocate(datalen);
267
268 #define PHI_SAVE(x) {                           \
269       typeof(x) *p;                             \
270       ALLOC(p,1);                               \
271       if (phase==2)                             \
272         memcpy(p, &(x), sizeof(x));             \
273     }
274
275     DO_PERSIST_HEADER_ITEMS(PHI_SAVE, PHI_SAVE, PHI_SAVE)
276     
277     if (phase==2)
278       break;
279
280     ALLOC(trains, n_trains);
281     FOR_TRAIN(tra, trap=train_pnames, trap++) {
282       char *pname;
283       ALLOC(pname, strlen(*trap)+1);
284       if (phase) {
285         strcpy(pname, *trap);
286         free(*trap);
287         tra->pname= *trap= pname;
288         tra->addr= -1;
289         tra->foredetect= 0;
290       } 
291     }
292
293     ALLOC(segments, info_nsegments);
294     if (phase)
295       FOR_SEG {
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 }