Annotation of OSKit-Mach/ipc/ipc_hash.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Mach Operating System
                      3:  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
                      4:  * All Rights Reserved.
                      5:  *
                      6:  * Permission to use, copy, modify and distribute this software and its
                      7:  * documentation is hereby granted, provided that both the copyright
                      8:  * notice and this permission notice appear in all copies of the
                      9:  * software, derivative works or modified versions, and any portions
                     10:  * thereof, and that both notices appear in supporting documentation.
                     11:  *
                     12:  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
                     13:  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
                     14:  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
                     15:  *
                     16:  * Carnegie Mellon requests users of this software to return to
                     17:  *
                     18:  *  Software Distribution Coordinator  or  [email protected]
                     19:  *  School of Computer Science
                     20:  *  Carnegie Mellon University
                     21:  *  Pittsburgh PA 15213-3890
                     22:  *
                     23:  * any improvements or extensions that they make and grant Carnegie Mellon
                     24:  * the rights to redistribute these changes.
                     25:  */
                     26: /*
                     27:  *     File:   ipc/ipc_hash.c
                     28:  *     Author: Rich Draves
                     29:  *     Date:   1989
                     30:  *
                     31:  *     Entry hash table operations.
                     32:  */
                     33: 
                     34: #include <mach/boolean.h>
                     35: #include <mach/port.h>
                     36: #include <kern/lock.h>
                     37: #include <kern/kalloc.h>
                     38: #include <ipc/port.h>
                     39: #include <ipc/ipc_space.h>
                     40: #include <ipc/ipc_object.h>
                     41: #include <ipc/ipc_entry.h>
                     42: #include <ipc/ipc_hash.h>
                     43: #include <ipc/ipc_init.h>
                     44: #include <ipc/ipc_types.h>
                     45: 
                     46: #include <mach_ipc_debug.h>
                     47: #if    MACH_IPC_DEBUG
                     48: #include <mach/kern_return.h>
                     49: #include <mach_debug/hash_info.h>
                     50: #include <vm/vm_map.h>
                     51: #include <vm/vm_kern.h>
                     52: #include <vm/vm_user.h>
                     53: #endif
                     54: 
                     55: 
                     56: 
                     57: /*
                     58:  *     Routine:        ipc_hash_lookup
                     59:  *     Purpose:
                     60:  *             Converts (space, obj) -> (name, entry).
                     61:  *             Returns TRUE if an entry was found.
                     62:  *     Conditions:
                     63:  *             The space must be locked (read or write) throughout.
                     64:  */
                     65: 
                     66: boolean_t
                     67: ipc_hash_lookup(space, obj, namep, entryp)
                     68:        ipc_space_t space;
                     69:        ipc_object_t obj;
                     70:        mach_port_t *namep;
                     71:        ipc_entry_t *entryp;
                     72: {
                     73:        return (ipc_hash_local_lookup(space, obj, namep, entryp) ||
                     74:                ((space->is_tree_hash > 0) &&
                     75:                 ipc_hash_global_lookup(space, obj, namep,
                     76:                                        (ipc_tree_entry_t *) entryp)));
                     77: }
                     78: 
                     79: /*
                     80:  *     Routine:        ipc_hash_insert
                     81:  *     Purpose:
                     82:  *             Inserts an entry into the appropriate reverse hash table,
                     83:  *             so that ipc_hash_lookup will find it.
                     84:  *     Conditions:
                     85:  *             The space must be write-locked.
                     86:  */
                     87: 
                     88: void
                     89: ipc_hash_insert(
                     90:        ipc_space_t     space,
                     91:        ipc_object_t    obj,
                     92:        mach_port_t     name,
                     93:        ipc_entry_t     entry)
                     94: {
                     95:        mach_port_index_t index;
                     96: 
                     97:        index = MACH_PORT_INDEX(name);
                     98:        if ((index < space->is_table_size) &&
                     99:            (entry == &space->is_table[index]))
                    100:                ipc_hash_local_insert(space, obj, index, entry);
                    101:        else
                    102:                ipc_hash_global_insert(space, obj, name,
                    103:                                       (ipc_tree_entry_t) entry);
                    104: }
                    105: 
                    106: /*
                    107:  *     Routine:        ipc_hash_delete
                    108:  *     Purpose:
                    109:  *             Deletes an entry from the appropriate reverse hash table.
                    110:  *     Conditions:
                    111:  *             The space must be write-locked.
                    112:  */
                    113: 
                    114: void
                    115: ipc_hash_delete(
                    116:        ipc_space_t     space,
                    117:        ipc_object_t    obj,
                    118:        mach_port_t     name,
                    119:        ipc_entry_t     entry)
                    120: {
                    121:        mach_port_index_t index;
                    122: 
                    123:        index = MACH_PORT_INDEX(name);
                    124:        if ((index < space->is_table_size) &&
                    125:            (entry == &space->is_table[index]))
                    126:                ipc_hash_local_delete(space, obj, index, entry);
                    127:        else
                    128:                ipc_hash_global_delete(space, obj, name,
                    129:                                       (ipc_tree_entry_t) entry);
                    130: }
                    131: 
                    132: /*
                    133:  *     The global reverse hash table holds splay tree entries.
                    134:  *     It is a simple open-chaining hash table with singly-linked buckets.
                    135:  *     Each bucket is locked separately, with an exclusive lock.
                    136:  *     Within each bucket, move-to-front is used.
                    137:  */
                    138: 
                    139: ipc_hash_index_t ipc_hash_global_size;
                    140: ipc_hash_index_t ipc_hash_global_mask;
                    141: 
                    142: #define IH_GLOBAL_HASH(space, obj)                                     \
                    143:        (((((ipc_hash_index_t) ((vm_offset_t)space)) >> 4) +            \
                    144:          (((ipc_hash_index_t) ((vm_offset_t)obj)) >> 6)) &             \
                    145:         ipc_hash_global_mask)
                    146: 
                    147: typedef struct ipc_hash_global_bucket {
                    148:        decl_simple_lock_data(, ihgb_lock_data)
                    149:        ipc_tree_entry_t ihgb_head;
                    150: } *ipc_hash_global_bucket_t;
                    151: 
                    152: #define        IHGB_NULL       ((ipc_hash_global_bucket_t) 0)
                    153: 
                    154: #define        ihgb_lock_init(ihgb)    simple_lock_init(&(ihgb)->ihgb_lock_data)
                    155: #define        ihgb_lock(ihgb)         simple_lock(&(ihgb)->ihgb_lock_data)
                    156: #define        ihgb_unlock(ihgb)       simple_unlock(&(ihgb)->ihgb_lock_data)
                    157: 
                    158: ipc_hash_global_bucket_t ipc_hash_global_table;
                    159: 
                    160: /*
                    161:  *     Routine:        ipc_hash_global_lookup
                    162:  *     Purpose:
                    163:  *             Converts (space, obj) -> (name, entry).
                    164:  *             Looks in the global table, for splay tree entries.
                    165:  *             Returns TRUE if an entry was found.
                    166:  *     Conditions:
                    167:  *             The space must be locked (read or write) throughout.
                    168:  */
                    169: 
                    170: boolean_t
                    171: ipc_hash_global_lookup(
                    172:        ipc_space_t             space,
                    173:        ipc_object_t            obj,
                    174:        mach_port_t             *namep,
                    175:        ipc_tree_entry_t        *entryp)
                    176: {
                    177:        ipc_hash_global_bucket_t bucket;
                    178:        ipc_tree_entry_t this, *last;
                    179: 
                    180:        assert(space != IS_NULL);
                    181:        assert(obj != IO_NULL);
                    182: 
                    183:        bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)];
                    184:        ihgb_lock(bucket);
                    185: 
                    186:        if ((this = bucket->ihgb_head) != ITE_NULL) {
                    187:                if ((this->ite_object == obj) &&
                    188:                    (this->ite_space == space)) {
                    189:                        /* found it at front; no need to move */
                    190: 
                    191:                        *namep = this->ite_name;
                    192:                        *entryp = this;
                    193:                } else for (last = &this->ite_next;
                    194:                            (this = *last) != ITE_NULL;
                    195:                            last = &this->ite_next) {
                    196:                        if ((this->ite_object == obj) &&
                    197:                            (this->ite_space == space)) {
                    198:                                /* found it; move to front */
                    199: 
                    200:                                *last = this->ite_next;
                    201:                                this->ite_next = bucket->ihgb_head;
                    202:                                bucket->ihgb_head = this;
                    203: 
                    204:                                *namep = this->ite_name;
                    205:                                *entryp = this;
                    206:                                break;
                    207:                        }
                    208:                }
                    209:        }
                    210: 
                    211:        ihgb_unlock(bucket);
                    212:        return this != ITE_NULL;
                    213: }
                    214: 
                    215: /*
                    216:  *     Routine:        ipc_hash_global_insert
                    217:  *     Purpose:
                    218:  *             Inserts an entry into the global reverse hash table.
                    219:  *     Conditions:
                    220:  *             The space must be write-locked.
                    221:  */
                    222: 
                    223: void
                    224: ipc_hash_global_insert(
                    225:        ipc_space_t             space,
                    226:        ipc_object_t            obj,
                    227:        mach_port_t             name,
                    228:        ipc_tree_entry_t        entry)
                    229: {
                    230:        ipc_hash_global_bucket_t bucket;
                    231: 
                    232: 
                    233:        assert(entry->ite_name == name);
                    234:        assert(space != IS_NULL);
                    235:        assert(entry->ite_space == space);
                    236:        assert(obj != IO_NULL);
                    237:        assert(entry->ite_object == obj);
                    238: 
                    239:        space->is_tree_hash++;
                    240:        assert(space->is_tree_hash <= space->is_tree_total);
                    241: 
                    242:        bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)];
                    243:        ihgb_lock(bucket);
                    244: 
                    245:        /* insert at front of bucket */
                    246: 
                    247:        entry->ite_next = bucket->ihgb_head;
                    248:        bucket->ihgb_head = entry;
                    249: 
                    250:        ihgb_unlock(bucket);
                    251: }
                    252: 
                    253: /*
                    254:  *     Routine:        ipc_hash_global_delete
                    255:  *     Purpose:
                    256:  *             Deletes an entry from the global reverse hash table.
                    257:  *     Conditions:
                    258:  *             The space must be write-locked.
                    259:  */
                    260: 
                    261: void
                    262: ipc_hash_global_delete(
                    263:        ipc_space_t             space,
                    264:        ipc_object_t            obj,
                    265:        mach_port_t             name,
                    266:        ipc_tree_entry_t        entry)
                    267: {
                    268:        ipc_hash_global_bucket_t bucket;
                    269:        ipc_tree_entry_t this, *last;
                    270: 
                    271:        assert(entry->ite_name == name);
                    272:        assert(space != IS_NULL);
                    273:        assert(entry->ite_space == space);
                    274:        assert(obj != IO_NULL);
                    275:        assert(entry->ite_object == obj);
                    276: 
                    277:        assert(space->is_tree_hash > 0);
                    278:        space->is_tree_hash--;
                    279: 
                    280:        bucket = &ipc_hash_global_table[IH_GLOBAL_HASH(space, obj)];
                    281:        ihgb_lock(bucket);
                    282: 
                    283:        for (last = &bucket->ihgb_head;
                    284:             (this = *last) != ITE_NULL;
                    285:             last = &this->ite_next) {
                    286:                if (this == entry) {
                    287:                        /* found it; remove from bucket */
                    288: 
                    289:                        *last = this->ite_next;
                    290:                        break;
                    291:                }
                    292:        }
                    293:        assert(this != ITE_NULL);
                    294: 
                    295:        ihgb_unlock(bucket);
                    296: }
                    297: 
                    298: /*
                    299:  *     Each space has a local reverse hash table, which holds
                    300:  *     entries from the space's table.  In fact, the hash table
                    301:  *     just uses a field (ie_index) in the table itself.
                    302:  *
                    303:  *     The local hash table is an open-addressing hash table,
                    304:  *     which means that when a collision occurs, instead of
                    305:  *     throwing the entry into a bucket, the entry is rehashed
                    306:  *     to another position in the table.  In this case the rehash
                    307:  *     is very simple: linear probing (ie, just increment the position).
                    308:  *     This simple rehash makes deletions tractable (they're still a pain),
                    309:  *     but it means that collisions tend to build up into clumps.
                    310:  *
                    311:  *     Because at least one entry in the table (index 0) is always unused,
                    312:  *     there will always be room in the reverse hash table.  If a table
                    313:  *     with n slots gets completely full, the reverse hash table will
                    314:  *     have one giant clump of n-1 slots and one free slot somewhere.
                    315:  *     Because entries are only entered into the reverse table if they
                    316:  *     are pure send rights (not receive, send-once, port-set,
                    317:  *     or dead-name rights), and free entries of course aren't entered,
                    318:  *     I expect the reverse hash table won't get unreasonably full.
                    319:  *
                    320:  *     Ordered hash tables (Amble & Knuth, Computer Journal, v. 17, no. 2,
                    321:  *     pp. 135-142.) may be desirable here.  They can dramatically help
                    322:  *     unsuccessful lookups.  But unsuccessful lookups are almost always
                    323:  *     followed by insertions, and those slow down somewhat.  They
                    324:  *     also can help deletions somewhat.  Successful lookups aren't affected.
                    325:  *     So possibly a small win; probably nothing significant.
                    326:  */
                    327: 
                    328: #define        IH_LOCAL_HASH(obj, size)                                \
                    329:                ((((mach_port_index_t) (obj)) >> 6) % (size))
                    330: 
                    331: /*
                    332:  *     Routine:        ipc_hash_local_lookup
                    333:  *     Purpose:
                    334:  *             Converts (space, obj) -> (name, entry).
                    335:  *             Looks in the space's local table, for table entries.
                    336:  *             Returns TRUE if an entry was found.
                    337:  *     Conditions:
                    338:  *             The space must be locked (read or write) throughout.
                    339:  */
                    340: 
                    341: boolean_t
                    342: ipc_hash_local_lookup(
                    343:        ipc_space_t     space,
                    344:        ipc_object_t    obj,
                    345:        mach_port_t     *namep,
                    346:        ipc_entry_t     *entryp)
                    347: {
                    348:        ipc_entry_t table;
                    349:        ipc_entry_num_t size;
                    350:        mach_port_index_t hindex, index;
                    351: 
                    352:        assert(space != IS_NULL);
                    353:        assert(obj != IO_NULL);
                    354: 
                    355:        table = space->is_table;
                    356:        size = space->is_table_size;
                    357:        hindex = IH_LOCAL_HASH(obj, size);
                    358: 
                    359:        /*
                    360:         *      Ideally, table[hindex].ie_index is the name we want.
                    361:         *      However, must check ie_object to verify this,
                    362:         *      because collisions can happen.  In case of a collision,
                    363:         *      search farther along in the clump.
                    364:         */
                    365: 
                    366:        while ((index = table[hindex].ie_index) != 0) {
                    367:                ipc_entry_t entry = &table[index];
                    368: 
                    369:                if (entry->ie_object == obj) {
                    370:                        *namep = MACH_PORT_MAKEB(index, entry->ie_bits);
                    371:                        *entryp = entry;
                    372:                        return TRUE;
                    373:                }
                    374: 
                    375:                if (++hindex == size)
                    376:                        hindex = 0;
                    377:        }
                    378: 
                    379:        return FALSE;
                    380: }
                    381: 
                    382: /*
                    383:  *     Routine:        ipc_hash_local_insert
                    384:  *     Purpose:
                    385:  *             Inserts an entry into the space's reverse hash table.
                    386:  *     Conditions:
                    387:  *             The space must be write-locked.
                    388:  */
                    389: 
                    390: void
                    391: ipc_hash_local_insert(
                    392:        ipc_space_t             space,
                    393:        ipc_object_t            obj,
                    394:        mach_port_index_t       index,
                    395:        ipc_entry_t             entry)
                    396: {
                    397:        ipc_entry_t table;
                    398:        ipc_entry_num_t size;
                    399:        mach_port_index_t hindex;
                    400: 
                    401:        assert(index != 0);
                    402:        assert(space != IS_NULL);
                    403:        assert(obj != IO_NULL);
                    404: 
                    405:        table = space->is_table;
                    406:        size = space->is_table_size;
                    407:        hindex = IH_LOCAL_HASH(obj, size);
                    408: 
                    409:        assert(entry == &table[index]);
                    410:        assert(entry->ie_object == obj);
                    411: 
                    412:        /*
                    413:         *      We want to insert at hindex, but there may be collisions.
                    414:         *      If a collision occurs, search for the end of the clump
                    415:         *      and insert there.
                    416:         */
                    417: 
                    418:        while (table[hindex].ie_index != 0) {
                    419:                if (++hindex == size)
                    420:                        hindex = 0;
                    421:        }
                    422: 
                    423:        table[hindex].ie_index = index;
                    424: }
                    425: 
                    426: /*
                    427:  *     Routine:        ipc_hash_local_delete
                    428:  *     Purpose:
                    429:  *             Deletes an entry from the space's reverse hash table.
                    430:  *     Conditions:
                    431:  *             The space must be write-locked.
                    432:  */
                    433: 
                    434: void
                    435: ipc_hash_local_delete(
                    436:        ipc_space_t             space,
                    437:        ipc_object_t            obj,
                    438:        mach_port_index_t       index,
                    439:        ipc_entry_t             entry)
                    440: {
                    441:        ipc_entry_t table;
                    442:        ipc_entry_num_t size;
                    443:        mach_port_index_t hindex, dindex;
                    444: 
                    445:        assert(index != MACH_PORT_NULL);
                    446:        assert(space != IS_NULL);
                    447:        assert(obj != IO_NULL);
                    448: 
                    449:        table = space->is_table;
                    450:        size = space->is_table_size;
                    451:        hindex = IH_LOCAL_HASH(obj, size);
                    452: 
                    453:        assert(entry == &table[index]);
                    454:        assert(entry->ie_object == obj);
                    455: 
                    456:        /*
                    457:         *      First check we have the right hindex for this index.
                    458:         *      In case of collision, we have to search farther
                    459:         *      along in this clump.
                    460:         */
                    461: 
                    462:        while (table[hindex].ie_index != index) {
                    463:                if (table[hindex].ie_index == 0)
                    464:                {
                    465:                        static int gak = 0;
                    466:                        if (gak == 0)
                    467:                        {
                    468:                                printf("gak! entry wasn't in hash table!\n");
                    469:                                gak = 1;
                    470:                        }
                    471:                        return;
                    472:                }
                    473:                if (++hindex == size)
                    474:                        hindex = 0;
                    475:        }
                    476: 
                    477:        /*
                    478:         *      Now we want to set table[hindex].ie_index = 0.
                    479:         *      But if we aren't the last index in a clump,
                    480:         *      this might cause problems for lookups of objects
                    481:         *      farther along in the clump that are displaced
                    482:         *      due to collisions.  Searches for them would fail
                    483:         *      at hindex instead of succeeding.
                    484:         *
                    485:         *      So we must check the clump after hindex for objects
                    486:         *      that are so displaced, and move one up to the new hole.
                    487:         *
                    488:         *              hindex - index of new hole in the clump
                    489:         *              dindex - index we are checking for a displaced object
                    490:         *
                    491:         *      When we move a displaced object up into the hole,
                    492:         *      it creates a new hole, and we have to repeat the process
                    493:         *      until we get to the end of the clump.
                    494:         */
                    495: 
                    496:        for (dindex = hindex; index != 0; hindex = dindex) {
                    497:                for (;;) {
                    498:                        mach_port_index_t tindex;
                    499:                        ipc_object_t tobj;
                    500: 
                    501:                        if (++dindex == size)
                    502:                                dindex = 0;
                    503:                        assert(dindex != hindex);
                    504: 
                    505:                        /* are we at the end of the clump? */
                    506: 
                    507:                        index = table[dindex].ie_index;
                    508:                        if (index == 0)
                    509:                                break;
                    510: 
                    511:                        /* is this a displaced object? */
                    512: 
                    513:                        tobj = table[index].ie_object;
                    514:                        assert(tobj != IO_NULL);
                    515:                        tindex = IH_LOCAL_HASH(tobj, size);
                    516: 
                    517:                        if ((dindex < hindex) ?
                    518:                            ((dindex < tindex) && (tindex <= hindex)) :
                    519:                            ((dindex < tindex) || (tindex <= hindex)))
                    520:                                break;
                    521:                }
                    522: 
                    523:                table[hindex].ie_index = index;
                    524:        }
                    525: }
                    526: 
                    527: /*
                    528:  *     Routine:        ipc_hash_init
                    529:  *     Purpose:
                    530:  *             Initialize the reverse hash table implementation.
                    531:  */
                    532: 
                    533: void
                    534: ipc_hash_init(void)
                    535: {
                    536:        ipc_hash_index_t i;
                    537: 
                    538:        /* if not configured, initialize ipc_hash_global_size */
                    539: 
                    540:        if (ipc_hash_global_size == 0) {
                    541:                ipc_hash_global_size = ipc_tree_entry_max >> 8;
                    542:                if (ipc_hash_global_size < 32)
                    543:                        ipc_hash_global_size = 32;
                    544:        }
                    545: 
                    546:        /* make sure it is a power of two */
                    547: 
                    548:        ipc_hash_global_mask = ipc_hash_global_size - 1;
                    549:        if ((ipc_hash_global_size & ipc_hash_global_mask) != 0) {
                    550:                natural_t bit;
                    551: 
                    552:                /* round up to closest power of two */
                    553: 
                    554:                for (bit = 1;; bit <<= 1) {
                    555:                        ipc_hash_global_mask |= bit;
                    556:                        ipc_hash_global_size = ipc_hash_global_mask + 1;
                    557: 
                    558:                        if ((ipc_hash_global_size & ipc_hash_global_mask) == 0)
                    559:                                break;
                    560:                }
                    561:        }
                    562: 
                    563:        /* allocate ipc_hash_global_table */
                    564: 
                    565:        ipc_hash_global_table = (ipc_hash_global_bucket_t)
                    566:                kalloc((vm_size_t) (ipc_hash_global_size *
                    567:                                    sizeof(struct ipc_hash_global_bucket)));
                    568:        assert(ipc_hash_global_table != IHGB_NULL);
                    569: 
                    570:        /* and initialize it */
                    571: 
                    572:        for (i = 0; i < ipc_hash_global_size; i++) {
                    573:                ipc_hash_global_bucket_t bucket;
                    574: 
                    575:                bucket = &ipc_hash_global_table[i];
                    576:                ihgb_lock_init(bucket);
                    577:                bucket->ihgb_head = ITE_NULL;
                    578:        }
                    579: }
                    580: 
                    581: #if    MACH_IPC_DEBUG
                    582: 
                    583: /*
                    584:  *     Routine:        ipc_hash_info
                    585:  *     Purpose:
                    586:  *             Return information about the global reverse hash table.
                    587:  *             Fills the buffer with as much information as possible
                    588:  *             and returns the desired size of the buffer.
                    589:  *     Conditions:
                    590:  *             Nothing locked.  The caller should provide
                    591:  *             possibly-pageable memory.
                    592:  */
                    593: 
                    594: 
                    595: ipc_hash_index_t
                    596: ipc_hash_info(
                    597:        hash_info_bucket_t      *info,
                    598:        mach_msg_type_number_t count)
                    599: {
                    600:        ipc_hash_index_t i;
                    601: 
                    602:        if (ipc_hash_global_size < count)
                    603:                count = ipc_hash_global_size;
                    604: 
                    605:        for (i = 0; i < count; i++) {
                    606:                ipc_hash_global_bucket_t bucket = &ipc_hash_global_table[i];
                    607:                unsigned int bucket_count = 0;
                    608:                ipc_tree_entry_t entry;
                    609: 
                    610:                ihgb_lock(bucket);
                    611:                for (entry = bucket->ihgb_head;
                    612:                     entry != ITE_NULL;
                    613:                     entry = entry->ite_next)
                    614:                        bucket_count++;
                    615:                ihgb_unlock(bucket);
                    616: 
                    617:                /* don't touch pageable memory while holding locks */
                    618:                info[i].hib_count = bucket_count;
                    619:        }
                    620: 
                    621:        return ipc_hash_global_size;
                    622: }
                    623: 
                    624: #endif /* MACH_IPC_DEBUG */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.