chiark / gitweb /
Merge and end branch-hostside-wip-2008-01-25 PROPERLY; cvs up -j branch-hostside...
[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  *
14  * speed is in um/s, upwait and downwait are in us
15  */
16
17 #include <sys/mman.h>
18
19 #include "record-i.h"
20 #include "record-l.h"
21
22 /*---------- input and error handling ----------*/
23
24 static const char *filename;
25
26 void record_yyerror(const char *m) {
27   die("config: %s:%d: %s", filename, record_yylineno, m);
28 }
29
30 /*---------- pname lookup (also handles train counting) ----------*/
31
32 static char **train_pnames;
33
34 Train *record_pname2train(const char *pname) {
35   int tran;
36   char **trap;
37   
38   for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
39     if (!strcmp(*trap, pname)) goto found;
40   }
41   if (trains) record_yyerror("number of trains changed between passes!");
42   tran= n_trains++;
43   train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
44   train_pnames[tran]= mstrdup(pname);
45
46 found:
47   if (!trains) return 0;
48   return trains + tran;
49 }
50
51 Segment *record_pname2seg(const char *pname) {
52   SEG_IV;
53
54   FOR_SEG
55     if (!strcmp(segi->pname, pname))
56       return seg;
57   
58   return 0; /* silently discard data for segments no longer in the layout */
59 }
60
61 /*---------- zone allocator for strings ----------*/
62
63 typedef struct TempZoneBlock TempZoneBlock;
64 struct TempZoneBlock {
65   TempZoneBlock *next; /* for deallocation */
66   char buf[1];
67 };
68
69 static TempZoneBlock *tempzones_head;
70 static char *tempzone_ptr;
71 static int tempzone_remain, tempzone_head_len;
72
73 char *record_tempzone_strdup(const char *s) {
74   int l;
75   char *r;
76   
77   l= strlen(s) + 1;
78   if (l > tempzone_remain) {
79     TempZoneBlock *new;
80     assert(l < INT_MAX/20);
81     tempzone_head_len= tempzone_remain= l*10 + 65536;
82     new= mmalloc(sizeof(new) + tempzone_remain);
83     tempzone_ptr= (char*)new + sizeof(new);
84     new->next= tempzones_head;
85     tempzones_head= new;
86   }
87   r= tempzone_ptr;
88   tempzone_remain -= l;
89   tempzone_ptr += l;
90   memcpy(r,s,l);
91   return r;
92 }
93
94 void record_tempzone_clear(void) {
95   TempZoneBlock *clear, *clear_next;
96   if (!tempzones_head) return;
97
98   clear= tempzones_head->next;
99   while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
100   tempzones_head->next= 0;
101   tempzone_remain= tempzone_head_len;
102   tempzone_ptr= tempzones_head->buf;
103 }
104
105 /*---------- semantic actions ----------*/
106 /*
107  * These are all called only during the second pass, when trains is non-0.
108  * Also, all Train* and Segment* arguments are known to be non-0.
109  */
110
111 void record_train_is(Train *tra, int addr, int head, int det, int tail) {
112   tra->addr= addr;
113   tra->head= head;
114   tra->detectable= det;
115   tra->tail= tail;
116 }
117
118 void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
119   tra->foredetect= seg;
120   tra->maxinto= maxi;
121   tra->uncertainty= unc;
122   tra->backwards= backw;
123 }
124   
125 void record_train_home(Train *tra, int backw, Segment *seg) {
126   if (!tra) return;
127   seg->home= tra;
128   seg->ho_backwards= backw;
129 }
130   
131 void record_seg_has(Segment *seg, int backw, Train *tra) {
132   seg->owner= tra;
133   seg->tr_backwards= backw;
134 }
135
136 void record_seg_at(Segment *seg, const char *movposcombpname) {
137   const SegPosCombInfo *spci;
138   int poscomb;
139   
140   for (poscomb=0, spci=seg->i->poscombs;
141        poscomb < seg->i->n_poscombs;
142        poscomb++, spci++)
143     if (!strcmp(spci->pname, movposcombpname))
144       goto found;
145   return;
146   
147 found:
148   seg->movposcomb= poscomb;
149 }
150
151 /*---------- speed curves ----------*/
152
153 static SpeedCurveEntry *curvebuf;
154 static int curvebufsz, curvebufused;
155
156 void record_train_step_count(void) {
157   curvebufsz++;
158 }
159
160 void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
161   Train *other;
162   SpeedCurveEntry *new;
163   int i;
164   
165   if (curvebufused >= curvebufsz)
166     record_yyerror("more speed points on 2nd pass!");
167
168   if (!tra->accel.curve) {
169     tra->accel.curve= curvebuf + curvebufused;
170   }
171
172   /* Make room for an extra speed step point for this train,
173    * by moving all of the other trains up.  First move the data: */
174   memmove(tra->accel.curve + tra->accel.curvesz + 1,
175           tra->accel.curve + tra->accel.curvesz,
176           (char*)(curvebuf + curvebufused)
177           - (char*)(tra->accel.curve + tra->accel.curvesz));
178   /* Then adjust everyone's pointers: */
179   for (i=0, other=trains;
180        i<n_trains;
181        i++, other++)
182     if (other != tra && other->accel.curve &&
183         other->accel.curve >= tra->accel.curve)
184       other->accel.curve++;
185   /* ... that whole thing is O(n^2) if the speed points aren't sorted
186    * by train; we hope they are, in which case we are always adding
187    * to the last train in the list and there is then no data to copy,
188    * which makes it O(n*t) where n is the number of speed points and
189    * t is the number of trains.  If we cared we could optimise away
190    * the loop in the common case where this train is right at the end
191    * but a special case seems like asking for a bug when we do have
192    * out of order speed points. */
193
194   new= &tra->accel.curve[tra->accel.curvesz++];
195   curvebufused++;
196
197   new->step= step;
198   new->speed= speed * 1e-6 * SPEED_UNIT;
199   new->upwait= upw * 1e-3;
200   new->downwait= downw * 1e-3;
201 }
202
203 static int speedcurveentry_compare(const void *av, const void *bv) {
204   const SpeedCurveEntry *a= av, *b= bv;
205   if (a->step == b->step)
206     record_yyerror("multiple speed curve points at same step");
207   return a->step - b->step;
208 }
209
210 static void sort_curves(void) {
211   TRA_IV;
212
213   FOR_TRA {
214     if (tra->accel.curve) {
215       if (tra->accel.curvesz < 2)
216         die("config: speed curve too short for %s", tra->pname);
217       qsort(tra->accel.curve, tra->accel.curvesz,
218             sizeof(*tra->accel.curve), speedcurveentry_compare);
219       if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
220         die("config: speed curve missing zero point for %s", tra->pname);
221     }
222   }
223 }
224
225 /*---------- persistent data file layout ----------*/
226
227 static void *alloc_some(void *mapbase, int *offset, size_t sz, int count) {
228   void *r;
229   size_t totalsz;
230   
231   while (*offset % sz) {
232     if (mapbase) ((Byte*)mapbase)[*offset]= 0xaa;
233     (*offset)++;
234   }
235   totalsz= sz*count;
236   if (mapbase) {
237     r= (Byte*)mapbase + *offset;
238     memset(r,0,totalsz);
239   } else {
240     r= 0;
241   }
242   *offset += totalsz;
243   return r;
244 }
245
246 static void alloc(void) {
247   TRA_IV;
248   SEG_IV;
249   void *mapbase=0;
250   char **trap;
251   int phase, offset, datalen=0;
252
253 #define ALLOC(array,count) \
254   ((array)= alloc_some(mapbase,&offset,sizeof(*(array)),(count)))
255
256   for (phase=0; ; phase++) {
257     /* phase 0: count up how much space will be needed
258      * phase 1: fill in most of the details, leaving header blank
259      * phase 2: fill in the header, and then exit before doing rest
260      */
261     offset= 0;
262
263     if (phase==1)
264       mapbase= record_allocate(datalen);
265
266 #define PHI_SAVE(x) {                           \
267       typeof(x) *p;                             \
268       ALLOC(p,1);                               \
269       if (phase==2)                             \
270         memcpy(p, &(x), sizeof(x));             \
271     }
272
273     DO_PERSIST_HEADER_ITEMS(PHI_SAVE, PHI_SAVE, PHI_SAVE)
274     
275     if (phase==2)
276       break;
277
278     ALLOC(trains, n_trains);
279     FOR_TRAIN(tra, trap=train_pnames, 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_SEG {
294         seg->owner= 0;
295         seg->movposcomb= -1;
296         seg->i= segi;
297       }
298
299     if (phase==0)
300       datalen= offset;
301   }
302
303   curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
304 }
305   
306 /*---------- entrypoint from main, and its subroutines ----------*/
307
308 static void parse_file(const char *why) {
309   FILE *f;
310
311   f= fopen(filename,"r");
312   if (!f) diee("config: cannot open %s: %s", why, filename);
313   record_yyrestart(f);
314   record_yyparse();
315 }
316
317 static void parse_pass(const char **argv) {
318   while ((filename= *argv++))
319     parse_file("commandline-specified record file");
320
321   filename= persist_record_converted;
322   if (filename)
323     parse_file("converted persistent data file");
324 }
325
326 void records_parse(const char **argv) {
327   parse_pass(argv); /* trains==0: counts trains and curve points. */
328   alloc();
329   parse_pass(argv); /* trains!=0: populates data area */
330   record_tempzone_clear();
331   sort_curves();
332 }