+
+/**
+ * Overlay
+ */
+static struct Overlay {
+ struct Source source;
+ struct Region *table;
+ size_t count;
+ size_t limit;
+} overlay; // The current overlay.
+
+static void usage();
+
+/**
+ * Find the nearest overlay.table region below pos. Returns the index,
+ * or -1 if there is none, i.e. pos < overlay.table[0].
+ */
+static ssize_t overlay_prior_fragment(off_t pos) {
+ size_t lo = 0, hi = overlay.count;
+ while ( lo < hi ) {
+ size_t m = ( lo + hi ) / 2;
+ if ( m == lo ) {
+ return overlay.table[m].beg <= pos? m : -1;
+ }
+ if ( overlay.table[m].beg <= pos ) {
+ lo = m;
+ } else {
+ hi = m;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Save the entry count for overlay.table as 64-bit integer
+ * immediately following the overlay content at the index
+ * corresponding to the fused file size.
+ */
+static void overlay_save_count() {
+ lseek( overlay.source.fd, overlay.source.to, SEEK_SET );
+ size_t size = sizeof( overlay.count );
+ char *p = (char *) &overlay.count ;
+ while ( size > 0 ) {
+ size_t n = write( overlay.source.fd, p, size );
+ if ( n < 0 ) {
+ perror( overlay.source.filename );
+ exit( 1 );
+ }
+ size -= n;
+ p += n;
+ }
+ if ( overlay.source.dirty++ > 1000 ) {
+ fsync( overlay.source.fd );
+ overlay.source.dirty = 0;
+ }
+}
+
+/**
+ * Update the on-disk cache of overlay.table between the given
+ * indexes. The table is laid out immediately following the table
+ * count with each region saved as two 64-bit unsigned integers.
+ */
+static void overlay_save_table(size_t lo,size_t hi) {
+ char *p = (char *) &overlay.table[ lo ];
+ size_t pos = overlay.source.to + sizeof( overlay.count ) +
+ lo * sizeof( struct Region );
+ size_t size = ( hi - lo ) * sizeof( struct Region );
+ if ( pos != lseek( overlay.source.fd, pos, SEEK_SET ) ) {
+ fprintf( stderr, "%s: seek error\n", overlay.source.filename );
+ exit( 1 );
+ }
+ while ( size > 0 ) {
+ size_t n = write( overlay.source.fd, p, size );
+ if ( n < 0 ) {
+ perror( overlay.source.filename );
+ exit( 1 );
+ }
+ size -= n;
+ p += n;
+ }
+ if ( overlay.source.dirty++ > 1000 ) {
+ fsync( overlay.source.fd );
+ overlay.source.dirty = 0;
+ }
+}
+
+/**
+ * Insert a new region at index p, with previous portion [p,count]
+ * moved up to make space.
+ */
+static void overlay_insert(size_t p,off_t beg,off_t end) {
+ size_t bytes;
+ // Grow the table if needed
+ if ( overlay.count >= overlay.limit ) {
+ overlay.limit = overlay.count + 10;
+ bytes = overlay.limit * sizeof( struct Region );
+ overlay.table = overlay.table?
+ realloc( overlay.table, bytes ) : malloc( bytes );
+ }
+ bytes = ( overlay.count++ - p ) * sizeof( struct Region );
+ if ( bytes ) {
+ memmove( (char*) &overlay.table[ p+1 ],
+ (char*) &overlay.table[ p ],
+ bytes );
+ }
+ overlay.table[ p ].beg = beg;
+ overlay.table[ p ].end = end;
+ overlay_save_count();
+}
+
+/**
+ * Delete the region entry at p by moving the portion [p+1,count]
+ * down.
+ */
+static void overlay_delete(size_t p) {
+ size_t bytes = ( --overlay.count - p ) * sizeof( struct Region );
+ if ( bytes ) {
+ memmove( (char*) &overlay.table[ p ],
+ (char*) &overlay.table[ p+1 ],
+ bytes );
+ }
+}
+
+/**
+ * Mark the given region as updated, i.e. written to the overlay. The
+ * mark region may attach to prior marked regions or be a new,
+ * separate region. If attaching, it causes the prior regions to
+ * expand and the table adjusted by deleting any regions that become
+ * fully contained in other regions.
+ */
+static void overlay_mark(off_t beg,off_t end) {
+#if DEBUG
+ fprintf( stderr, "overlay_mark( %ld, %ld )\n", beg, end );
+#endif
+ int deleted = 0;
+ ssize_t q;
+ ssize_t p = overlay_prior_fragment( beg );
+ // p is the nearest region below or at beg (or -1)
+ if ( p >= 0 && beg <= overlay.table[p].end ) {
+ // p overlaps mark region
+ if ( end <= overlay.table[p].end ) {
+ // region p covers mark region already
+#if DEBUG
+ fprintf( stderr, "overlay covering ( %ld %ld )\n",
+ overlay.table[p].beg, overlay.table[p].end );
+#endif
+ return;
+ }
+ // the new mark region extends region p
+ overlay.table[p].end = end;
+ q = p+1;
+ while ( q < overlay.count &&
+ overlay.table[q].beg <= overlay.table[p].end ) {
+ // Extended region merges with subsequent region
+ if ( overlay.table[p].end < overlay.table[q].end ) {
+ overlay.table[p].end = overlay.table[q].end;
+ }
+ overlay_delete( q );
+ deleted++;
+ }
+ if ( deleted ) {
+ overlay_save_count();
+ q = overlay.count;
+ }
+ overlay_save_table( p, q );
+#if DEBUG
+ fprintf( stderr, "overlay expand ( %ld %ld ) deleted %d\n",
+ overlay.table[p].beg, overlay.table[p].end, deleted );
+#endif
+ return;
+ }
+ // The prior region p does not expand into new mark region
+ p++; // subsequent region
+ if ( p >= overlay.count || end < overlay.table[p].beg ) {
+ // New mark region is a separate region at p
+ overlay_insert( p, beg, end );
+#if DEBUG
+ fprintf( stderr, "overlay new ( %ld %ld )\n",
+ overlay.table[p].beg, overlay.table[p].end );
+#endif
+ overlay_save_table( p, overlay.count );
+ return;
+ }
+ // New marks start before and overlap with region p => change p
+ // and handle any subsequent regions being covered
+ overlay.table[p].beg = beg;
+ q = p+1;
+ if ( overlay.table[p].end < end ) {
+ overlay.table[p].end = end;
+ while ( q < overlay.count &&
+ overlay.table[q].beg <= overlay.table[p].end ) {
+ if ( overlay.table[p].end < overlay.table[q].end ) {
+ overlay.table[p].end = overlay.table[q].end;
+ }
+ overlay_delete( q );
+ deleted++;
+ }
+ if ( deleted ) {
+ overlay_save_count();
+ q = overlay.count;
+ }
+ }
+ overlay_save_table( p, q );
+#if DEBUG
+ fprintf( stderr, "overlay before ( %ld %ld ) deleted %d\n",
+ overlay.table[p].beg, overlay.table[p].end, deleted );
+#endif
+}
+
+/**
+ * Capture overlay filenames for processing after source fragments.
+ */
+static void overlay_setup(char *filenames) {
+ overlay.source.filename = filenames;
+}
+
+// Duplicate the source record data at the given index.
+static void dup_source_item(int index) {
+ sources.count++;
+ sources.array = realloc(
+ sources.array, sources.count * sizeof( struct Source ) );
+ if ( sources.array == 0 ) {
+ fprintf( stderr, "** OOM when expanding frament table\n" );
+ usage();
+ }
+ // copy elements from [index] to [count-1] one element down
+ size_t size = ( sources.count - index - 1 ) * sizeof( struct Source );
+ memmove( &sources.array[ index+1 ], &sources.array[ index ], size );
+}
+