chiark / gitweb /
giant reorg abolishes TrainNum most of the time; working on making it build
[trains.git] / hostside / record.c
index 429cee20069e02a64cdc644fb53feef1e44ccf28..1f357af3ef0b23a8fda963faac07bb91239bb3f0 100644 (file)
  *  seg <segpn> at <movposcomb>
  */
 
+#include <sys/mman.h>
+
+#include "record.h"
+#include "record-l.h"
+
+/*---------- input and error handling ----------*/
+
+static const char *filename;
+
+void record_yyerror(const char *m) {
+  die("config: %s:%d: %s\n", filename, record_yylineno, m);
+}
+
+/*---------- pname lookup (also handles train counting) ----------*/
+
+static char **train_pnames;
+
+Train *record_pname2train(const char *pname) {
+  int tran;
+  char **trap;
+  
+  for (tran=0, trap=train_pnames; tran<n_trains; tran++,trap++) {
+    if (!strcmp(*trap, pname)) goto found;
+  }
+  if (trains) record_yyerror("number of trains changed between passes!");
+  tran= n_trains++;
+  train_pnames= mrealloc(train_pnames, sizeof(*train_pnames)*n_trains);
+  train_pnames[tran]= mstrdup(pname);
+
+found:
+  if (!trains) return 0;
+  return trains + tran;
+}
+
+Segment *record_pname2seg(const char *pname) {
+  int i;
+  const SegmentInfo *segi;
+  
+  for (i=0, segi=info_segments;
+       i<NUM_SEGMENTS;
+       i++, segi++)
+    if (!strcmp(segi->pname, pname))
+      return segments + i;
+  
+  return 0; /* silently discard data for segments no longer in the layout */
+}
+
+/*---------- zone allocator for strings ----------*/
+
+typedef struct TempZoneBlock TempZoneBlock;
+struct TempZoneBlock {
+  TempZoneBlock *next; /* for deallocation */
+  char buf[1];
+};
+
+static TempZoneBlock *tempzones_head;
+static char *tempzone_ptr;
+static int tempzone_remain, tempzone_head_len;
+
+char *record_tempzone_strdup(const char *s) {
+  int l;
+  char *r;
+  
+  l= strlen(s) + 1;
+  if (l > tempzone_remain) {
+    TempZoneBlock *new;
+    assert(l < INT_MAX/20);
+    tempzone_head_len= tempzone_remain= l*10 + 65536;
+    new= mmalloc(sizeof(new) + tempzone_remain);
+    tempzone_ptr= (char*)new + sizeof(new);
+    new->next= tempzones_head;
+    tempzones_head= new;
+  }
+  r= tempzone_ptr;
+  tempzone_remain -= l;
+  tempzone_ptr += l;
+  memcpy(r,s,l);
+  return r;
+}
+
+void record_tempzone_clear(void) {
+  TempZoneBlock *clear, *clear_next;
+  if (!tempzones_head) return;
+
+  clear= tempzones_head->next;
+  while (clear) { clear_next= clear->next; free(clear); clear= clear_next; }
+  tempzones_head->next= 0;
+  tempzone_remain= tempzone_head_len;
+  tempzone_ptr= tempzones_head->buf;
+}
+
+/*---------- semantic actions ----------*/
+/*
+ * These are all called only during the second pass, when trains is non-0.
+ * Also, all Train* and Segment* arguments are known to be non-0.
+ */
+
+void record_train_is(Train *tra, int addr, int head, int det, int tail) {
+  tra->addr= addr;
+  tra->head= head;
+  tra->detectable= det;
+  tra->tail= tail;
+}
+
+void record_train_at(Train *tra, int backw, Segment *seg, int maxi, int unc) {
+  tra->foredetect= seg;
+  tra->maxinto= maxi;
+  tra->uncertainty= unc;
+  tra->backwards= backw;
+}
+  
+void record_seg_has(Segment *seg, int backw, Train *tra) {
+  seg->owner= tra;
+  seg->tr_backwards= backw;
+}
+
+void record_seg_at(Segment *seg, const char *movposcombpname) {
+  const SegPosCombInfo *spci;
+  int i;
+  
+  for (i=0, spci=seg->i->poscombs;
+       i<seg->i->n_poscombs;
+       i++, spci++)
+    if (!strcmp(spci->pname, movposcombpname))
+      goto found;
+  return;
+  
+found:
+  seg->movposcomb= i;
+}
+
+/*---------- speed curves ----------*/
+
+static SpeedCurveEntry *curvebuf;
+static int curvebufsz, curvebufused;
+
+void record_train_step_count(void) {
+  curvebufsz++;
+}
+
+void record_train_step(Train *tra, int step, int speed, int upw, int downw) {
+  Train *other;
+  SpeedCurveEntry *new;
+  int i;
+  
+  if (curvebufused >= curvebufsz) record_yyerror("more speed points on 2nd pass!");
+
+  if (!tra->accel.curve) {
+    tra->accel.curve= curvebuf + curvebufused;
+  }
+
+  /* Make room for an extra speed step point for this train,
+   * by moving all of the other trains up.  First move the data: */
+  memmove(tra->accel.curve + tra->accel.curvesz + 1,
+         tra->accel.curve + tra->accel.curvesz,
+         (char*)(curvebuf + curvebufused)
+         - (char*)(tra->accel.curve + tra->accel.curvesz));
+  /* Then adjust everyone's pointers: */
+  for (i=0, other=trains;
+       i<n_trains;
+       i++, other++)
+    if (other != tra && other->accel.curve &&
+       other->accel.curve >= tra->accel.curve)
+      other->accel.curve++;
+  /* ... that whole thing is O(n^2) if the speed points aren't sorted
+   * by train; we hope they are, in which case we are always adding
+   * to the last train in the list and there is then no data to copy,
+   * which makes it O(n*t) where n is the number of speed points and
+   * t is the number of trains.  If we cared we could optimise away
+   * the loop in the common case where this train is right at the end
+   * but a special case seems like asking for a bug when we do have
+   * out of order speed points. */
+
+  new= &tra->accel.curve[tra->accel.curvesz++];
+  curvebufsz++;
+
+  new->step= step;
+  new->speed= speed;
+  new->upwait= upw;
+  new->downwait= downw;
+}
+
+static int speedcurveentry_compare(const void *av, const void *bv) {
+  const SpeedCurveEntry *a= av, *b= bv;
+  if (a->step == b->step)
+    record_yyerror("multiple speed curve points at same step");
+  return a->step - b->step;
+}
+
+static void sort_curves(void) {
+  int i;
+  Train *tra;
+  
+  for (i=0, tra=trains;
+       i<n_trains;
+       i++, tra++) {
+    if (tra->accel.curve) {
+      if (tra->accel.curvesz < 2)
+       die("config: speed curve too short for %s", tra->pname);
+      qsort(tra->accel.curve, tra->accel.curvesz,
+           sizeof(*tra->accel.curve), speedcurveentry_compare);
+      if (tra->accel.curve[0].step || tra->accel.curve[0].speed)
+       die("config: speed curve missing zero point for %s", tra->pname);
+    }
+  }
+}
+
+/*---------- entrypoint from main, and its subroutines ----------*/
+
+static void alloc(void) {
+  Train *tra; Segment *seg; const SegmentInfo *segi;
+  char **trap;
+  int i;
+  
+  segments= mmap(0, sizeof(*segments)*NUM_SEGMENTS + sizeof(*trains)*n_trains,
+                PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1,0);
+  if (!segments) diee("mmap for shared region");
+
+  for (i=0, seg=segments, segi=info_segments;
+       i<NUM_SEGMENTS;
+       i++, seg++, segi++) {
+    seg->movposcomb= -1;
+    seg->i= segi;
+  }
+
+  trains= (void*)(segments + NUM_SEGMENTS);
+  for (i=0, tra=trains, trap=train_pnames;
+       i<n_trains;
+       i++, tra++, trap++) {
+    tra->pname= *trap;
+    tra->addr= -1;
+    tra->head= -1;
+  }
+
+  curvebuf= mmalloc(sizeof(*curvebuf) * curvebufsz);
+}
+
+static void parse_pass(const char **argv) {
+  FILE *f;
+  
+  while ((filename= *argv++)) {
+    f= fopen(filename,"r");
+    if (!f) diee("config: cannot open input file: %s", filename);
+    record_yyrestart(f);
+    record_yyparse();
+  }
+}
+
+void records_parse(const char **argv) {
+  parse_pass(argv); /* trains==0: counts trains and curve points. */
+  alloc();
+  parse_pass(argv); /* trains!=0: populates data area */
+  record_tempzone_clear();
+  sort_curves();
+}