/* transform the graph to a shortest-path tree by marking tree edges */ void mapit() { register p_node n; register p_link l; p_link lparent; static int firsttime = 0; vprintf(stderr, "*** mapping\n"); Tflag = Tflag && Vflag; /* tracing here only if verbose */ /* re-use the hash table space for the heap */ Heap = (p_link *) Table; Hashpart = pack(0L, Tabsize - 1); /* expunge penalties from -a option and make circular copy lists */ resetnodes(); if (firsttime++) { if (Linkout && *Linkout) /* dump cheapest links */ showlinks(); if (Graphout && *Graphout) /* dump the edge list */ dumpgraph(); } /* insert Home to get things started */ l = newlink(); /* link to get things started */ getlink(l)->l_to = Home; (void) dehash(Home); insert(l); /* main mapping loop */ remap: Heaphighwater = Nheap; while ((lparent = min_node()) != 0) { chkheap(1); getlink(lparent)->l_flag |= LTREE; n = getlink(lparent)->l_to; if (Tflag && maptrace(n, n)) fprintf(stderr, "%s -> %s mapped\n", getnode(getnode(n)->n_parent)->n_name, getnode(n)->n_name); if (getnode(n)->n_flag & MAPPED) die("mapped node in heap"); getnode(n)->n_flag |= MAPPED; /* add children to heap */ heapchildren(n); } vprintf(stderr, "heap high water mark was %ld\n", Heaphighwater); /* sanity check on implementation */ if (Nheap != 0) die("null entry in heap"); if (Hashpart < Tabsize) { /* * add back links from unreachable hosts to * reachable neighbors, then remap. * * asymptotically, this is quadratic; in * practice, this is done once or twice. */ backlinks(); if (Nheap) goto remap; } if (Hashpart < Tabsize) { fputs("You can't get there from here:\n", stderr); for ( ; Hashpart < Tabsize; Hashpart++) { fprintf(stderr, "\t%s", getnode(Table[Hashpart])->n_name); if (getnode(Table[Hashpart])->n_flag & ISPRIVATE) fputs(" (private)", stderr); putc('\n', stderr); } } }
static void multikey_block(unsigned char** strings, size_t n, size_t depth) { if (n < 10000) { mkqsort(strings, n, depth); return; } assert(n > B); static Buckets buckets; static std::array<unsigned char*, 32*B> temp_space; static FreeBlocks freeblocks; const CharT partval = pseudo_median<CharT>(strings, n, depth); BackLinks backlinks(n/B+1); std::array<size_t, 3> bucketsize; bucketsize.fill(0); buckets[0].clear(); buckets[1].clear(); buckets[2].clear(); // Initialize our list of free blocks. assert(freeblocks.empty()); for (size_t i=0; i < 32; ++i) freeblocks.push_back(&temp_space[i*B]); for (size_t i=0; i < n-n%B; i+=B) freeblocks.push_back(strings+i); // Distribute strings to buckets. Use a small cache to reduce memory // stalls. The exact size of the cache is not very important. size_t i=0; for (; i < n-n%32; i+=32) { std::array<CharT, 32> cache; for (unsigned j=0; j<32; ++j) { cache[j] = get_char<CharT>(strings[i+j], depth); } for (unsigned j=0; j<32; ++j) { const CharT c = cache[j]; const unsigned b = get_bucket(c, partval); if (bucketsize[b] % B == 0) { Block block = take_free_block(freeblocks); buckets[b].push_back(block); // Backlinks must be set for those blocks, that // use the original string array space. if (block >= strings && block < strings+n) { backlinks[(block-strings)/B] = &(buckets[b].back()); } } assert(not buckets[b].empty()); buckets[b].back()[bucketsize[b] % B] = strings[i+j]; ++bucketsize[b]; } } for (; i < n; ++i) { const CharT c = get_char<CharT>(strings[i], depth); const unsigned b = get_bucket(c, partval); if (bucketsize[b] % B == 0) { Block block = take_free_block(freeblocks); buckets[b].push_back(block); // Backlinks must be set for those blocks, that // use the original string array space. if (block >= strings && block < strings+n) { backlinks[(block-strings)/B] = &(buckets[b].back()); } } assert(not buckets[b].empty()); buckets[b].back()[bucketsize[b] % B] = strings[i]; ++bucketsize[b]; } assert(bucketsize[0]+bucketsize[1]+bucketsize[2]==n); // Process each bucket, and copy all strings in that bucket to proper // place in the original string pointer array. This means that those // positions that are occupied by other blocks must be moved to free // space etc. size_t pos = 0; for (unsigned i=0; i < 3; ++i) { if (bucketsize[i] == 0) continue; Bucket::const_iterator it = buckets[i].begin(); for (size_t bucket_pos=0; bucket_pos < bucketsize[i]; ++it, bucket_pos+=B) { const size_t block_items = std::min(size_t(B), bucketsize[i]-bucket_pos); const size_t block_overlap = (pos+block_items-1)/B; if (*it == (strings+pos)) { // Already at correct place. assert(pos%B==0); backlinks[pos/B] = 0; pos += block_items; continue; } // Don't overwrite the block in the position we are // about to write to, but copy it into safety. if (backlinks[block_overlap]) { // Take a free block. The block can be 'stale', // i.e. it can point to positions we have // already copied new strings into. Take free // blocks until we have non-stale block. Block tmp = take_free_block(freeblocks); while (tmp >= strings && tmp < strings+pos) tmp = take_free_block(freeblocks); if (tmp >= strings && tmp < strings+n) { assert(backlinks[(tmp-strings)/B]==0); backlinks[(tmp-strings)/B] = backlinks[block_overlap]; } memcpy(tmp, *(backlinks[block_overlap]), B*sizeof(unsigned char*)); *(backlinks[block_overlap]) = tmp; backlinks[block_overlap] = 0; } if (*it >= strings && *it < strings+n) { assert(*it > strings+pos); backlinks[(*it-strings)/B] = 0; } // Copy string pointers to correct position. memcpy(strings+pos, *it, block_items*sizeof(unsigned char*)); // Return block for later use. Favor those in the // temporary space. if (*it >= strings && *it < strings+n) { freeblocks.push_back(*it); } else { freeblocks.push_front(*it); } pos += block_items; } } freeblocks.clear(); backlinks.clear(); BackLinks().swap(backlinks); multikey_block<B, CharT>(strings, bucketsize[0], depth); if (not is_end(partval)) multikey_block<B, CharT>(strings+bucketsize[0], bucketsize[1], depth+sizeof(CharT)); multikey_block<B, CharT>(strings+bucketsize[0]+bucketsize[1], bucketsize[2], depth); }