--------------------- PatchSet 3343 Date: 2001/11/20 06:59:14 Author: jkay Branch: push Tag: (none) Log: Greg Plaxton's algorithm for widely distributing hint cache root server duty. Members: src/Plaxton.c:1.1->1.1.2.1 --- /dev/null Wed Feb 14 00:55:47 2007 +++ squid/src/Plaxton.c Wed Feb 14 00:56:31 2007 @@ -0,0 +1,2419 @@ +/* + * $Date: 2001/11/20 06:59:14 $ $Id: Plaxton.c,v 1.1.2.1 2001/11/20 06:59:14 jkay Exp $ + * + * DEBUG: section 56 Greg Plaxton's per-object hierarchy. + * AUTHOR: Mike Dahlin, UT Austin, 1998 + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * -------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by + * the National Science Foundation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +/* + *------------------------------------------------------------------ + * LESS Group + * Computer Sciences Department + * University of Texas at Austin + * + * Plaxton.c --- Dynamic algorithm via plaxton's algorithm. + *------------------------------------------------------------------ + */ + +/* + * Plaxton.c interface notes, from Mike Dahlin's comments in HCPlax.h: + * + * Each children queue holds messages that should go to a set of + * "equivalent" children (eg. the children at some level of the + * hierarchy.) There are HCHPlax_NChildrenQs() such queues; + * num = HCPlax_GetChildAddrs(q, arraypointer) allocates + * an array of addresses for a particular queue and returns + * the number of children. + * The rule is simple -- when I send a message to children about + * an object, I send it to all children who match that object's + * key in fewer bits than I do. + * + * Each parent queue holds messages that should go to one parent. + * In general, we may have different parents for different objects. + * So, enqueue messages for an object in the queue + * HCPlax_ParentQIndex(URLKey). + * + * We also need to help figure out the hierarchy for Plaxton's + * algorithm. Plaxton's algorithm figures out who the parents + * are, but it doesn't know who the children are. We observe + * messages to keep in sync. + * On recv message: + * recv parent msg from parent: process parent message + * recv child msg from child: process child message + * recv parent msg from !parent: send "I am not child" to parent; + * drop message + * recv child msg from !child: add child to child list; + * process child message + * recv "I am not child" from child: remove child + */ + +#include "squid.h" + + +#define HCPLAX_DEBUG 56 + +/* + * Array [BITS_PER_BYTE * sizeof(NodeKeyKey) + 1] of List_Links. + */ +#define BITS_PER_BYTE 8 +#define BITS_PER_KEY (8 * sizeof(GenericKey)) + +static GenericKey myNodeKey; + +static int initialized = 0; + +/* + * For keeping track of children + */ +static int nChildLists = -9999; +static List_Links *childrenListA; +static int *childrenCountA = NULL; /* Entry i is the number of children + * that match in i bits. */ +static int childrenCountTot = 0; + +/* + * For keeping track of parents + */ +int Plaxton_bucketsPerLevel = -999; +static int nParentLevels = -999; +static int bitsPerLevel = -999; +static int maxParentBucket = -9999; +/* + * parentListA is an array [nParentLevels][Plaxton_bucketsPerLevel] + * where entry k = l*bitsPerLevel + i (0 <= i < 2^bitsPerLevel) + * contains a list of all known nodes whose node key matches + * the local node key in the lowest l*bitsPerLevel bits + * and whose next bitsPerLevel bits have the same bit pattern + * as i + * For example, with bitsPerLevel = 2, if our local node key + * were 010110, a node with key 111010 would belong in level 1 + * (since the bottom two bits match us but the next two bits dont + * in entry 10), so node 111010 would go into bucket + * 1*Plaxton_bucketsPerLevel + 10 = 4 + 2 = 6. + * Maintain the invariant that the entries in each bucket are sorted + * by their distance from me with the closest node always + * at the head of its list. + */ +static List_Links *parentListA = NULL; +/* + * Special case of parent finding when I match in as many bits + * as anyone. In case of a tie for root, + * we all need to agree on whom among us is the root. We need + * to do so in a way that avoids loops (and hopefully which + * gets everyone to agree on the same root.) Solution: if + * an object matches me in k bits and I know of no node + * that matches the object in more than k bits, choose as + * the root for that object, the node that matches the + * object (and me) in k bits and which has the highest + * known NodeKey. + * + * highestMatchA is an array [BITS_PER_KEY] where entry k + * contains a list of all nodes that match me in at least + * the bottom k bits. The lists all include me. The lists + * are sorted by the integer value of the key with the + * largest value at the head of the list. + */ +static List_Links *highestMatchA = NULL; + +static int normalParentIndex(int bucket); +static int fullMatchIndex(GenericKey k); +static int firstLevelBucketForMatch(int matchBits); +static int highestMatchIndex(int matchBits); +static void childAdd _PARAMS((NodeKey *key)); +static NodeKey *childFind _PARAMS((NodeKey *key)); +static void freeList(List_Links *list); +static void addNodeParentList(struct sockaddr_in *addNode); +static void removeNodeParentList(struct sockaddr_in *goneNode); +static void changeDistanceParentList(struct sockaddr_in *changeDistNode); +static int parentListIndex(NodeKey *n); +static void addNodeHighestMatch(struct sockaddr_in *addNode); +static void matchInsert(List_Links *l, struct sockaddr_in *a); +static void removeNodeHighestMatch(struct sockaddr_in *goneNode); +static void matchRemove(List_Links *l, struct sockaddr_in *a); +static int scanForParentMatch(int maxTry, int minTry, const URLKey *key, + int *parentIndexRet, int *amIRootRet); +static GenericKey makeMatchKey(const URLKey *key, int matchBits, + int permuteVal); + +#ifdef DOTEST +static void selfTestChildren(); +static int selfTestFindAddr(int addr, struct sockaddr_in *childrenA, + int childCount); +static void selfTestParents(); +static void testParentsBinaryTree(void); +static void testParentsMultiRootCase(void); +static void stressTestParents(void); +static void testParentsInternals(void); +static void testParentsInternals2(void); +static void testMakeMatchKey(void); +#endif /* DOTEST */ + +/* + *------------------------------------------------------------------ + * + * plaxtonInit -- + * + * description. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------4.15.1998----------- + */ +void plaxtonInit(int bitsPerLevel_) +{ + int ii; + int nParentBuckets; + struct sockaddr_in *me = &Config.Sockaddr.http->s; + NodeKey *key; + + assert(nChildLists < 0); /* We shouldn't be initialized yet */ + + assert(!initialized); + initialized = 1; + + myNodeKey = NodeKK_Key(me); + /* + * List i contains all children that match me in i bits. + * Legal values for i are [0, BITS_PER_KEY] + */ + nChildLists = BITS_PER_KEY + 1; + assert(childrenListA == NULL); + childrenListA = xmalloc(nChildLists * sizeof(List_Links)); + assert(childrenListA); + childrenCountA = xmalloc(nChildLists * sizeof(int)); + for(ii = 0; ii < nChildLists; ii++){ + List_Init(&childrenListA[ii]); + childrenCountA[ii] = 0; + } + childrenCountTot = 0; + + /* + * Parents + */ + bitsPerLevel = bitsPerLevel_; + nParentLevels = (BITS_PER_KEY) / bitsPerLevel; + if((BITS_PER_KEY) % bitsPerLevel != 0){ + nParentLevels++; + } + Plaxton_bucketsPerLevel = 1; + for(ii = 0; ii < bitsPerLevel; ii++){ + Plaxton_bucketsPerLevel *= 2; + } + nParentBuckets = Plaxton_bucketsPerLevel * nParentLevels; + maxParentBucket = nParentBuckets - 1; + assert(parentListA == NULL); + parentListA = (List_Links *)xmalloc(nParentBuckets * sizeof(List_Links)); + assert(parentListA); + for(ii = 0; ii < nParentBuckets; ii++){ + List_Init(&parentListA[ii]); + } + assert(highestMatchA == NULL); + highestMatchA = (List_Links *)xmalloc(BITS_PER_KEY * sizeof(List_Links)); + assert(highestMatchA); + for(ii = 0; ii < BITS_PER_KEY; ii++){ + List_Init(&highestMatchA[ii]); + key = (NodeKey *)xmalloc(sizeof(NodeKey)); + NodeKey_Init(key, me); + List_Insert((List_Links *)key, LIST_ATFRONT(&highestMatchA[ii])); + } +} + +/* + *------------------------------------------------------------------ + * + * plaxtonDestroy -- + * + * description. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------4.16.1998------ + */ +void plaxtonDestroy(void) +{ + int ii; + + assert(initialized); + initialized = 0; + + assert(nChildLists > 0); /* Are we initialized? */ + for(ii = 0; ii < nChildLists; ii++){ + freeList(&childrenListA[ii]); + } + xfree(childrenListA); + childrenListA = NULL; + xfree(childrenCountA); + childrenCountA = NULL; + childrenCountTot = -999999; + nChildLists = -88888; + + /* + * Parents + */ + for(ii = 0; ii < nParentLevels * Plaxton_bucketsPerLevel; ii++){ + freeList(&parentListA[ii]); + } + xfree(parentListA); + parentListA = NULL; + nParentLevels = -8888; + bitsPerLevel = -8888; + Plaxton_bucketsPerLevel = -8888; + for(ii = 0; ii < BITS_PER_KEY; ii++){ + freeList(&highestMatchA[ii]); + } + xfree(highestMatchA); + highestMatchA = NULL; +} + + +/* + *------------------------------------------------------------------ + * + * freeList -- + * + * Free all elements on a list of NodeKeys + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.16.1998-------- + */ +static void +freeList(List_Links *list) +{ + NodeKey *k; + assert(list); + while(!List_IsEmpty(list)){ + k = (NodeKey *)List_First(list); + List_Remove((List_Links *)k); + xfree(k); + } +} + +/* + *------------------------------------------------------------------ + * + * plaxtonAddChild -- + * + * Add the specified child to the list of children. + * We keep a separate list of children for each + * number of bits that can match the local addrss key. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.9.1998--------- + */ +void +plaxtonAddChild (struct sockaddr_in *child) +{ + NodeKey *key; + + assert(initialized); + assert(nChildLists > 0); /* Are we initialized? */ + key = (NodeKey *)xmalloc(sizeof(NodeKey)); + NodeKey_Init(key, child); + if(childFind(key)){ + xfree(key); + return; + } + childAdd(key); +} + +/* + *------------------------------------------------------------------ + * + * plaxtonRemoveChild -- + * + * Remove the specified child from its list of children. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------4.9.1998----- + */ +void +plaxtonRemoveChild(struct sockaddr_in *child) +{ + NodeKey key, *found; + int match; + + assert(initialized); + assert(nChildLists > 0); /* Are we initialized? */ + NodeKey_Init(&key, child); + found = childFind(&key); + if(found){ + List_Remove((List_Links*)found); + xfree(found); + match = plaxtonMatchBits(key.key, myNodeKey); + childrenCountA[match]--; + childrenCountTot--; + assert(childrenCountA[match] >= 0); + assert(childrenCountTot >= 0); + return; + } + return; +} + + +/* + *------------------------------------------------------------------ + * + * plaxtonCheckIfChild -- + * + * Return nonzero if the specified node is listed + * as a child. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.9.1998------ + */ +int +plaxtonCheckIfChild(struct sockaddr_in *child) +{ + NodeKey key, *found; + + assert(initialized); + assert(nChildLists > 0); /* Are we initialized? */ + NodeKey_Init(&key, child); + found = childFind(&key); + if(found != NULL){ + return 1; + } + return 0; +} + + + + + +/* + *------------------------------------------------------------------ + * + * plaxtonChildQIndex -- + * + * Return the queue for which messages about the specified + * object should be stored. + * + * The rule is simple -- when I send a message to children about + * an object, I send it to all children who match that object's + * key in fewer bits than I do. (Unless I am the root + * for that object, in which case I send info to all + * nodes that think of me as a parent.) + * + * So, store messages for an object that matches me in i bits + * in queue i. Later, we will send it to all children + * that match me in i or fewer bits. (Store messages + * for objects for which I am root in queue nChildLists-1). + * + * As an optimization, if I have no children that match + * me in i or fewer bits, set *amILeaf to nozero. In that + * case, the caller may choose not to enqueue any message. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------5.1.1998------ + */ +int +plaxtonChildQIndex (URLKey *key, int iAmRoot, int *amILeaf) +{ + int match; + int leafCheck; + + assert(initialized); + assert(nChildLists > 0); /* Are we initialized? */ + + + if(iAmRoot){ + if(/*childrenCountTot > 0*/ 1){ /* XXX */ + *amILeaf = 0; + return nChildLists - 1; + } + else{ + *amILeaf = 1; + return -999; + } + } + + match = plaxtonMatchBits(key->key, myNodeKey); + /* + * Check to see if I am a leaf for this object by looking + * at all childlists for children that match me in fewer bits + * than this object. If all such lists are empty, than I am + * a leaf. This is less inefficient than it seems. The expected + * number of queues I have to check is less than (1 + .5 + .25 + ...) + * (since I match in 0 bits half the time, ... ) + */ + *amILeaf = 1; + for(leafCheck = match - 1; leafCheck >= 0; leafCheck--){ + if(!List_IsEmpty(&childrenListA[leafCheck])){ + *amILeaf = 0; + break; + } + } + return match; +} + +/* + *------------------------------------------------------------------ + * + * plaxtonNChildrenQs -- + * + * Return the number of queues of messages for children + * caller should allocate. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------4.10.1998--- + */ +int plaxtonNChildrenQs (void) +{ + assert(initialized); + assert(nChildLists > 0); /* Are we initialized? */ + return nChildLists; +} + + +/* + *------------------------------------------------------------------ + * + * plaxtonGetChildAddrs -- + * + * Return the number of children for the specified message + * queue, allocate a buffer for that many addresses, + * and fill in that buffer with the children's addresses. + * + * Notice -- the question being asked is "to whom should + * I send messages for objects that match me in k bits?" + * The answer is all nodes that match in k *or fewer* bits. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * Allocates a vector for the addresses. Caller must free + * this vector when done. + * + * Note: the vector may be out of date by the time ther caller + * uses it. This is inherent problem -- even if caller sent + * all messages instantaneously, by the time they arrive + * at destination, the child could have left the system + * or a new child could have joined. + * + *----------------------------------------------------4.10.1998------- + */ +int +plaxtonGetChildAddrs (int qIndex, struct sockaddr_in **retAddrAP) +{ + int count; + int imatch; + NodeKey *current; + int check; + + assert(initialized); + for(imatch = 0, count = 0; + imatch <= qIndex; + imatch++){ + count += childrenCountA[imatch]; + } + if(!count){ + *retAddrAP = NULL; + return 0; + } + *retAddrAP=(struct sockaddr_in *)xmalloc(count * sizeof(struct sockaddr_in)); + for(imatch = 0, count = 0; + imatch <= qIndex; + imatch++){ + check = 0; + LIST_FORALL2(&childrenListA[imatch], NodeKey *, current){ + check++; + (*retAddrAP)[count] = NodeKey_GetAddr(current); + count++; + debug(HCPLAX_DEBUG, 2, "plaxtonGetChildAddrs: sending msg to child %s\n", + inet_ntoa((*retAddrAP)[count].sin_addr)); + } + assert(check == childrenCountA[imatch]); + } + return count; +} + + +/* + *------------------------------------------------------------------ + * + * nParentQs -- + * + * Return the number of parent q's to allocate. + * We need to account for both the normal case + * when we find a parent that matches an object + * in more bits than we do, and the root + * case when we match in k bits and so do some + * other nodes, but no one matches in more than + * k bits; in that case, we may not send + * to the closest node that matches us in k bits. + * + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.15.1998------- + */ +int +plaxtonNParentQs (void) +{ + int normalParentCount, rootCaseCount; + + assert(initialized); + assert(parentListA != NULL); + normalParentCount = nParentLevels * Plaxton_bucketsPerLevel; + rootCaseCount = BITS_PER_KEY; + return normalParentCount + rootCaseCount; +} + + +/* + *------------------------------------------------------------------ + * + * plaxtonParentQIndex -- + * + * Return the index of the parent queue to use + * for messages about the specified URLKey or + * set amIRoot to true if I have no parent + * for that object. + * + * I am trying to find someone who matches + * the object in more bits than I do. To + * reduce the number of levels of hierarchy + * I try to match bitsPerLevel bits at a + * time. + * + * If I match in at least l*bitsPerLevel bits but + * not in (l+1)*bitsPerLevel bits, then + * the parent is the node that matches in + * the most bits between (l+1)*bPL and (l)*bPL + * + * If there are multiple potential parents + * that match in the same number of bits, choose + * the closest one (all parent lists are maintained + * in sorted order) unless I also match the + * object in that many bits; in the latter case + * we need to decide on a root from among + * several equally qualified candidates. We + * always choose the node with the highest + * NodeKey in that case. + * + * Note the key invariant for avoiding loops: + * my parent always matches an object in more + * bits than I do or matches in as many bits + * as I do and has a higher ID. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.19.1998--------- + */ +int +plaxtonParentQIndex(URLKey *key, int *amIRoot) +{ + int match; + int nextLevelMatch; + int found, pindex; + + assert(initialized); + /* XXX */ + match = plaxtonMatchBits(key->key, myNodeKey); + if(match == nParentLevels * bitsPerLevel){ + /* + * Match all bits. I am root! + */ + *amIRoot = 1; + return -9999999; + } + + /* + * Try for full or partial match at next level. + */ + nextLevelMatch = ((match + bitsPerLevel) / bitsPerLevel) * bitsPerLevel; + found = scanForParentMatch(nextLevelMatch, match+1, key, + &pindex, amIRoot); + if(found){ + assert(!*amIRoot); + return normalParentIndex(pindex); + } + + + /* + * I match in match bits, and didn't find anyone that matches + * in more. But, there may be other nodes that match the object + * in the same number of bits (but differ from me in some higher bit). + * We all need to agree on whom among us is the root. We need + * to do so in a way that avoids loops (and hopefully which + * gets everyone to agree on the same root.) Solution: if + * an object matches me in k bits and I know of no node + * that matches the object in more than k bits, choose as + * the root for that object, the node that matches the + * object (and me) in k bits and which has the highest + * known NodeKey. + */ + assert(!List_IsEmpty(&highestMatchA[match])); + if(((NodeKey *)List_First(&highestMatchA[match]))->key == myNodeKey){ + *amIRoot = 1; + return -999999; + } + else{ + *amIRoot = 0; + return highestMatchIndex(match); + } +} + + +/* + *------------------------------------------------------------------ + * + * scanForParentMatch -- + * + * Find the bucket index in parentsListA for + * a node matching between maxTry and minTry + * bits (inclusive) for the specified key. + * Return true if parent found and set + * parentIndexRet and amIRootRet. + * + * As we look at fewer and fewer bits we + * need to try all combinations of the + * high-order bits we are masking off. + * We currently do this by probing different + * entries in the table. It would be more + * efficient to maintain auxiliary data + * structures with all eligible nodes, + * but the data structures here are already + * more complicated than I like; Make it + * work first, then optimize performance later. + * + * Return the node that matches in the most + * possible bits. If more than one node + * matches in a specific # of bits, return the + * closest. + * + * Arguments: + * None. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------ + */ +static int +scanForParentMatch(int maxTry, int minTry, const URLKey *key, + int *parentIndexRet, int *amIRootRet) +{ + GenericKey tryKey; + int matchBits, permuteBits, permuteMax, ipermute; + int tryIndex; + int found = 0, bestIndex = -999999; + struct sockaddr_in bestSin, trySin; + + /* + * This code assumes that we are looking to match + * a "level" in the tree. + */ + assert(maxTry % bitsPerLevel == 0); + assert(maxTry - minTry < bitsPerLevel); + + for(matchBits = maxTry; matchBits >= minTry; matchBits--){ + /* + * Split the address into two parts, a set of low-order bits + * that must match the target and a set of high-order + * bits that are "don't care" for which we'll try all + * permutations. + */ + permuteBits = maxTry - matchBits; + permuteMax = (1 << permuteBits) - 1; + for(ipermute = 0; ipermute <= permuteMax; ipermute++){ + tryKey = makeMatchKey(key, matchBits, ipermute); + tryIndex = fullMatchIndex(tryKey); + if(!List_IsEmpty(&parentListA[tryIndex])){ + if(!found){ + found = 1; + *amIRootRet = 0; + bestSin = NodeKey_GetAddr((NodeKey *) + List_First(&parentListA[tryIndex])); + bestIndex = tryIndex; + } + else{ + trySin = NodeKey_GetAddr((NodeKey *) + List_First(&parentListA[tryIndex])); + if(HCHier_compareDistanceFromMe(bestSin.sin_addr, + trySin.sin_addr) > 0){ + bestSin = trySin; + bestIndex = tryIndex; + assert(*amIRootRet == 0); + } + } + } + } + if(found){ + assert(*amIRootRet == 0); + *parentIndexRet = bestIndex; + return 1; + } + } /* for matchBits */ + assert(!found); + return 0; +} + + +/* + *------------------------------------------------------------------ + * + * makeMatchKey -- + * + * Return a key that matches the specified key in the + * bottom bits, and add the prefix + * permuteVal to the front. Remaining bits are all 0. + * For example, if key is 110101101, matchBits is 4, + * and permuteVal is 3 (11), then return + * 000111101 (zero-fill: 000 prefix: 11 match: 1101). + * + * Arguments: + * None. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------ + */ +static GenericKey +makeMatchKey(const URLKey *key, int matchBits, int permuteVal) +{ + GenericKey tryKey, matchMask; + + matchMask = (((GenericKey)1) << matchBits) - 1; + tryKey = (key->key & matchMask) | (permuteVal << matchBits); + return tryKey; +} + + +/* + *------------------------------------------------------------------ + * + * highestMatchIndex -- + * + * Return the network buffer index that we should + * use for messages to the root node for objects + * that match us and some other node in + * bits and match no node in more than + * bits. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------4.15.1998----- + */ +static int +highestMatchIndex(int match) +{ + return maxParentBucket + 1 + match; +} + +/* + *------------------------------------------------------------------ + * + * normalParentIndex -- + * + * Return the network buffer index that we should + * use for parents that match in the specified bucket + * (where bucket is calculated as matchLevel * 2^bitsPerLevel + * + key%2^bitsPerLevel). + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------4.15.1998---- + */ +static int +normalParentIndex(int bucket) +{ + assert(bucket <= maxParentBucket); + return bucket; +} + +/* + *------------------------------------------------------------------ + * + * firstLevelBucketForMatch -- + * + * Return the index of the first bucket for the set + * of buckets for the first non-matching level for + * a key that we match in matchBits. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.19.1998------- + */ +static int +firstLevelBucketForMatch(int match) +{ + int level; + + assert(match <= BITS_PER_KEY); + level = (match/bitsPerLevel) * Plaxton_bucketsPerLevel; + return level; +} + + + +/* + *------------------------------------------------------------------ + * + * plaxtonGetParentAddr -- + * + * Return the address to whom to send messages + * from the specified parent queue in *retAddr. + * Return nonzero on error. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.16.1998-------- + */ +int +plaxtonGetParentAddr (int index, struct sockaddr_in *retAddr) +{ + assert(initialized); + assert(index >= 0); + assert(parentListA != NULL); + if(index <= maxParentBucket){ + if(!List_IsEmpty(&parentListA[index])){ + *retAddr = ((NodeKey *)List_First(&parentListA[index]))->addr; + debug(HCPLAX_DEBUG, 2, + "plaxtonGetParentAddr: Sending msg to parent %s\n", + inet_ntoa(retAddr->sin_addr)); + return 0; + } + else{ + return 1; + } + } + else{ + /* XXX index = index - maxParentBucket; */ + index = index - maxParentBucket - 1; + assert(index < BITS_PER_KEY); + assert(index >= 0); + /* + * List always contains at least me + */ + assert(!List_IsEmpty(&highestMatchA[index])); + if(((NodeKey *)List_First(&highestMatchA[index]))->key == myNodeKey){ + return 1; + } + else{ + *retAddr = ((NodeKey *)List_First(&highestMatchA[index]))->addr; + debug(HCPLAX_DEBUG, 2, + "plaxtonGetParentAddr: Sending msg to parent %s\n", + inet_ntoa(retAddr->sin_addr)); + return 0; + } + } +} + +/* + *------------------------------------------------------------------ + * + * HCPLax_addNode -- + * + * Add a node to the lists of parents. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-----------------------------------------------------4.16.1998-------- + */ +void +plaxtonAddNode(struct sockaddr_in *newNode) +{ + assert(initialized); + assert(newNode); +#if 1 /* XXX! - disabling parent&child lists */ + addNodeParentList(newNode); +#endif + addNodeHighestMatch(newNode); + return; +} + +/* + *------------------------------------------------------------------ + * + * plaxtonRemoveNode -- + * + * Remove thenode from the lists of parents. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-----------------------------------------------------4.16.1998------ + */ +void +plaxtonRemoveNode(struct sockaddr_in *goneNode) +{ + assert(initialized); + assert(goneNode); + removeNodeParentList(goneNode); + removeNodeHighestMatch(goneNode); + return; +} + +/* + *------------------------------------------------------------------ + * + * plaxtonChangeDistance -- + * + * Change the distance (and perhaps the rank in sorted list) + * of the specified node. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-----------------------------------------------------4.16.1998------- + */ +void +plaxtonchangeDistance(struct sockaddr_in *changeDistNode) +{ + assert(initialized); + assert(changeDistNode); + changeDistanceParentList(changeDistNode); + /* + * Don't need to tell HighestMatch list since that one is + * not sorted by distance. + */ + return; +} + +/* + *------------------------------------------------------------------ + * + * addNodeParentList -- + * + * See definition of parentListA where it is declared. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.16.1998----- + */ +static void +addNodeParentList(struct sockaddr_in *addNode) +{ + NodeKey *n; + int index; + NodeKey *current; + struct sockaddr_in s1, s2; + + n = (NodeKey *)xmalloc(sizeof(NodeKey)); + assert(n); + NodeKey_Init(n, addNode); + + index = parentListIndex(n); + + + if(List_IsEmpty(&parentListA[index])){ + List_Insert((List_Links *)n, LIST_ATFRONT(&parentListA[index])); + return; + } + else{ + LIST_FORALL2(&parentListA[index], NodeKey *, current){ + if(HCHier_compareDistanceFromMe(NodeKey_GetAddr(current).sin_addr, + addNode->sin_addr) > 0){ + List_Insert((List_Links *)n, LIST_BEFORE(current)); + return; + } + /* + * Check invariant that list is sorted by distance from me. + */ + if((List_Links *)current != LIST_ATREAR(&parentListA[index])){ + s1 = NodeKey_GetAddr(current); + s2 = NodeKey_GetAddr((NodeKey *)List_Next((List_Links *)current)); + assert( HCHier_compareDistanceFromMe(s1.sin_addr, s2.sin_addr) <= 0); + } + } + } + List_Insert((List_Links *)n, LIST_ATREAR(&parentListA[index])); + return; +} + +/* + *------------------------------------------------------------------ + * + * removeNodeParentList -- + * + * Remove the specified node from the parentList + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------4.16.1998--------- + */ +static void +removeNodeParentList(struct sockaddr_in *goneNode) +{ + int index; + NodeKey n, *current; + struct sockaddr_in s1, s2; + + NodeKey_Init(&n, goneNode); + + index = parentListIndex(&n); + + + LIST_FORALL2(&parentListA[index], NodeKey *, current){ + if(!NodeKey_Compare(&n, current)){ + List_Remove((List_Links *)current); + xfree(current); + return; + } + /* + * Check invariant that list is sorted by distance from me. + */ + if((List_Links *)current != LIST_ATREAR(&parentListA[index])){ + s1 = NodeKey_GetAddr(current); + s2 = NodeKey_GetAddr((NodeKey *)List_Next((List_Links *)current)); + assert(HCHier_compareDistanceFromMe(s1.sin_addr, s2.sin_addr) <= 0); + } + } +} + +/* + *------------------------------------------------------------------ + * + * changeDistanceParentList -- + * + * Since we need to keep this list sorted by distance + * from me, we need to reorder it if the distance + * to some node changes. + * + * Note: right now we do the simple thing -- remove + * and add the node. Faster implementations are possible. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------4.16.1998-------- + */ +static void +changeDistanceParentList(struct sockaddr_in *changeDistNode) +{ + removeNodeParentList(changeDistNode); + addNodeParentList(changeDistNode); +} + + +/* + *------------------------------------------------------------------ + * + * parentListIndex -- + * + * Return the index of the parent list for the element + * with the specified nodeKey. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.19.1998------ + */ +static int +parentListIndex(NodeKey *n) +{ + return fullMatchIndex(n->key); +} + +/* + *------------------------------------------------------------------ + * + * fullMatchIndex -- + * + * Return the highest legal index into the parent list + * for object/node with the specified key. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-----------------------------------------------------4.19.1998------ + */ +static int +fullMatchIndex(GenericKey k) +{ + int bits, levelsThatMatch, indexInBucket, index; + + bits = plaxtonMatchBits(k, myNodeKey); + if(bits == nParentLevels * bitsPerLevel){ + /* + * I am root! want to match in all bits in last level + * This will force system to get right indexInBucket in + * last level. + */ + bits--; + } + levelsThatMatch = bits / bitsPerLevel; + indexInBucket = (k >> (levelsThatMatch * bitsPerLevel)) % Plaxton_bucketsPerLevel; + index = firstLevelBucketForMatch(bits) + indexInBucket; + return index; +} + + +/* + *------------------------------------------------------------------ + * + * addNodeHighestMatch -- + * + * Add the specified node to all highestMatch lists k + * for for which the node matches me in at least the + * bottom k bits. Keep each list sorted by key with + * the head of the list containing the highest key. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.16.1998------ + */ +static void +addNodeHighestMatch(struct sockaddr_in *addNode) +{ + NodeKey n; + int match, imatch; + + NodeKey_Init(&n, addNode); + + match = plaxtonMatchBits(n.key, myNodeKey); + if(match == BITS_PER_KEY){ + match--; + } + for(imatch = 0; imatch <= match; imatch++){ + matchInsert(&highestMatchA[imatch], addNode); + } + return; +} + +/* + *------------------------------------------------------------------ + * + * matchInsert -- + * + * Insert new node key onto list sorted by node key. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------4.16.1998---- + */ +static void +matchInsert(List_Links *l, struct sockaddr_in *a) +{ + NodeKey *current, *n; + + + n = (NodeKey *)xmalloc(sizeof(NodeKey)); + assert(n); + NodeKey_Init(n, a); + + /* + * Each list always contains at least me. + */ + assert(!List_IsEmpty(l)); + + LIST_FORALL2(l, NodeKey *, current){ + assert(current->key != n->key); + if(current->key < n->key){ + List_Insert((List_Links *)n, LIST_BEFORE((List_Links *)current)); + return; + } + } + assert(((NodeKey *)List_Last(l))->key >= n->key); + List_Insert((List_Links *)n, LIST_ATREAR((l))); + return; +} + +/* + *------------------------------------------------------------------ + * + * removeNodeHighestMatch -- + * + * Remove node from all lists k where node's key + * matches my key in at least k bits. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------4.16.1998---------- + */ +static void +removeNodeHighestMatch(struct sockaddr_in *goneNode) +{ + NodeKey n; + int match, imatch; + + NodeKey_Init(&n, goneNode); + + match = plaxtonMatchBits(n.key, myNodeKey); + if(match == BITS_PER_KEY){ + match --; + } + for(imatch = 0; imatch <= match; imatch++){ + matchRemove(&highestMatchA[imatch], goneNode); + } + return; +} + +/* + *------------------------------------------------------------------ + * + * matchRemove -- + * + * Remove specified element from list. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------4.16.1998----------- + */ +static void +matchRemove(List_Links *l, struct sockaddr_in *a) +{ + NodeKey *current, n; + + NodeKey_Init(&n, a); + /* + * Each list always contains at least me. + */ + assert(!List_IsEmpty(l)); + + LIST_FORALL2(l, NodeKey *, current){ + if(!NodeKey_Compare(current, &n)){ + List_Remove((List_Links *)current); + xfree(current); + return; + } + } + return; +} + +/* + *------------------------------------------------------------------ + * + * plaxtonCheckIfParent -- + * + * Return true if the specified node is, in fact, our + * current parent for objects with the specified URL. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------ + */ +int +plaxtonCheckIfParent(URLKey key, struct sockaddr_in *candidate) +{ + int index, amIRoot; + struct sockaddr_in real; + + assert(initialized); + index = plaxtonParentQIndex(&key, &amIRoot); + if(amIRoot){ + return 0; + } + if(plaxtonGetParentAddr(index, &real)){ + return 0; + } + if(candidate->sin_addr.s_addr == real.sin_addr.s_addr){ + return 1; + } + return 0; +} + + + + +/* + *------------------------------------------------------------------ + * + * childAdd -- + * + * Add a child record to the appropriate child list. + * Does NOT check to see if child is already on list -- + * caller should already have done it. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------4.9.1998---------- + */ +static void +childAdd(NodeKey *key) +{ + int l; + + l = plaxtonMatchBits(key->key, myNodeKey); + assert(l <= nChildLists); + List_Insert((List_Links*)key, LIST_ATFRONT(&childrenListA[l])); + childrenCountA[l]++; + childrenCountTot++; + assert(childFind(key)); + return; +} + +/* + *------------------------------------------------------------------ + * + * childFind -- + * + * Find the specified child if on the list. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------4.9.1998--------- + */ +static NodeKey * +childFind(NodeKey *key) +{ + int l; + NodeKey *current; + + l = plaxtonMatchBits(key->key, myNodeKey); + assert(l <= nChildLists); + LIST_FORALL2(&childrenListA[l], NodeKey *, current){ + if(!NodeKey_Compare(current, key)){ + return current; + } + } + return NULL; +} + + +/* + *------------------------------------------------------------------ + * + * matchBits -- + * + * Return the number of bits that match between the two + * keys. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-----------------------------------------------------4.10.1998------- + */ +int +plaxtonMatchBits(GenericKey k1, GenericKey k2) +{ + int ibit; + GenericKey mask; + + for(ibit = 0, mask = 0x1; + ibit < nChildLists - 1; + ibit++, mask = mask << 1){ + if((k1 & mask) != (k2 & mask)){ + return ibit; + } + } + return ibit; +} + + +/* + *------------------------------------------------------------------ + * + * plaxtonmyNodeKey -- + * + * description. + * + * Arguments: + * None. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------ + */ +GenericKey +plaxtonmyNodeKey(void) +{ + return myNodeKey; +} + + +#ifdef DOTEST +/* + *------------------------------------------------------------------ + * + * plaxtonSelfTest -- + * + * description. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.10.1998------ + */ + +void +plaxtonSelfTest() +{ + printf("plaxtonSelfTest..."); fflush(stdout); + testMakeMatchKey(); + neighborsOutgoing = (HCNet *)xmalloc(sizeof(HCNet)); + assert(neighborsOutgoing); + printf("."); fflush(stdout); + HCNet_Init(neighborsOutgoing); + HCNet_SetTestMode(neighborsOutgoing); + selfTestChildren(); + printf("."); fflush(stdout); + selfTestParents(); + printf("."); fflush(stdout); + HCNet_Destroy(neighborsOutgoing); + xfree(neighborsOutgoing); + printf(".Done.\n"); fflush(stdout); + return; +} + +/* + *------------------------------------------------------------------ + * + * selfTestChildren -- + * + * description. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------ + */ +static void +selfTestChildren() +{ + int iaddr; + struct sockaddr_in n0; + static int CHECK_ADDR_BEGIN = 1000; + static int CHECK_ADDR_END = 2000; + static int CHECK_NOT_THERE_END = 3000; + static URLKey urlMatchAll, urlMatchNone, urlMatchOne, urlMatchEight; + int amILeaf = -1; + int childCount, allChildren; + struct sockaddr_in *childrenA = NULL; + + assert(CHECK_ADDR_END > CHECK_ADDR_BEGIN); + assert(CHECK_ADDR_END < CHECK_NOT_THERE_END); + + + + allChildren = CHECK_ADDR_END - CHECK_ADDR_BEGIN; + plaxtonInit(8); + + assert(sizeof(URLKey) == sizeof(GenericKey)); /* bypassing normal init */ + urlMatchAll.key = myNodeKey; + urlMatchNone.key = ~myNodeKey; + urlMatchOne.key = (~myNodeKey) ^ 0x1; + urlMatchEight.key = (~myNodeKey) ^ 0xFF; + + assert(plaxtonNChildrenQs() == sizeof(GenericKey) * 8 + 1); + amILeaf = -1; + assert(plaxtonChildQIndex(&urlMatchAll, 0, &amILeaf) == sizeof(GenericKey) * 8); + assert(amILeaf == 1); + amILeaf = -1; + assert(plaxtonChildQIndex(&urlMatchNone, 0, &amILeaf) == 0); + assert(amILeaf == 1); + amILeaf = -1; + assert(plaxtonChildQIndex(&urlMatchOne, 0, &amILeaf) == 1); + assert(amILeaf == 1); + amILeaf = -1; + assert(plaxtonChildQIndex(&urlMatchEight, 0, &amILeaf) == 8); + assert(amILeaf == 1); + childrenA = (struct sockaddr_in *)0x888888; + childCount = plaxtonGetChildAddrs(15, &childrenA); + assert(childCount == 0); + assert(childrenA == NULL); + + for(iaddr = CHECK_ADDR_BEGIN; iaddr < CHECK_ADDR_END; iaddr++){ + n0.sin_addr.s_addr = iaddr; n0.sin_port = 0; + plaxtonAddChild(&n0); + } + + childCount = plaxtonGetChildAddrs(sizeof(GenericKey) * 8, &childrenA); + assert(childCount == allChildren); + assert(childrenA != NULL); + assert(selfTestFindAddr(CHECK_ADDR_BEGIN, childrenA, childCount)); + assert(selfTestFindAddr((CHECK_ADDR_BEGIN + CHECK_ADDR_END) / 2, + childrenA, childCount)); + assert(selfTestFindAddr(CHECK_ADDR_END - 1, childrenA, childCount)); + assert(!selfTestFindAddr(CHECK_ADDR_END, childrenA, childCount)); + xfree(childrenA); + + /* + * The nodes we inserted as children were random. Assuming + * we inserted a reasonable number of them, it would be + * surprizing if more than, say, 60% matched us in the + * lowest bit or if fewer than, say, 40% match us in the + * lowest bit + */ + if(allChildren >= 1000){ + childCount = plaxtonGetChildAddrs(0, &childrenA); + if(allChildren * 0.6 < childCount){ + printf("WARNING WARNING WARNING WARNING\n"); + printf("WARNING: Plaxton saw unlikely distribution of random children.\n"); + printf("WARNING: (too many). This is very nearly an assertion failure.\n"); + } + if(allChildren * 0.4 > childCount){ + printf("WARNING WARNING WARNING WARNING\n"); + printf("WARNING: Plaxton saw unlikely distribution of random children.\n"); + printf("WARNING: (too few). This is very nearly an assertion failure.\n"); + } + xfree(childrenA); + } + else{ + printf("\nWARNING: Plaxton selftest skipping random match check (too few samples\n"); + } + + printf(".");fflush(stdout); + for(iaddr = CHECK_ADDR_BEGIN; iaddr < CHECK_ADDR_END; iaddr++){ + n0.sin_addr.s_addr = iaddr; n0.sin_port = 0; + assert(plaxtonCheckIfChild(&n0)); + plaxtonRemoveChild(&n0); + } + printf(".");fflush(stdout); + for(iaddr = CHECK_ADDR_BEGIN; iaddr < CHECK_NOT_THERE_END; iaddr++){ + n0.sin_addr.s_addr = iaddr; n0.sin_port = 0; + assert(!plaxtonCheckIfChild(&n0)); + } + plaxtonDestroy(); + printf(".OK\n"); +} + + + +/* + *------------------------------------------------------------------ + * + * selfTestFindAddr -- + * + * Return nonzero if the specified value is in + * the array. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-----------------------------------------------------4.10.1998------- + */ +static int +selfTestFindAddr(int addr, struct sockaddr_in *childrenA, int childCount) +{ + struct sockaddr_in n0; + int ii; + + assert(childrenA != NULL); + n0.sin_addr.s_addr = addr; n0.sin_port = 0; + for(ii = 0; ii < childCount; ii++){ + assert(childrenA[ii].sin_port == 0); + if(childrenA[ii].sin_addr.s_addr == n0.sin_addr.s_addr){ + return 1; + } + } + return 0; +} + + +/* + *------------------------------------------------------------------ + * + * selfTestParents -- + * + * description. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-----------------------------------------------------4.19.1998-------- + */ +static void +selfTestParents(void) +{ + testParentsBinaryTree(); + testParentsMultiRootCase(); + testParentsInternals(); + testParentsInternals2(); + stressTestParents(); +} + +/* + *------------------------------------------------------------------ + * + * testParentsBinaryTree -- + * + * Test the following simple case: four nodes + * with low-order bits: 00 01 10 11 + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.19.1998-------- + */ +static void +testParentsBinaryTree(void) +{ + int myId, targetId, caseCount; + struct sockaddr_in parents[4], retAddr; + int ii, hops, qIndex, iAmRoot; + NodeKey key; + URLKey targetURLKey; + + /* XXX Make sure that entire code merge happens. Funny little bug in + * tools.h that we fixed. + */ + parents[0].sin_addr.s_addr = 10; + parents[0].sin_port = 0; + parents[1].sin_addr.s_addr = 11; + parents[1].sin_port = 0; + assert(SINCMP(&parents[0], &parents[1])); /* if this fails, bug in SINCMP */ + + /* + * Try this test from every point of view + */ + for(myId = 0; myId < 4; myId++){ + plaxtonInit(1); + assert(plaxtonNParentQs() == 192); + + + /* + * Cheat to force my key to do right thing + */ + myNodeKey = (GenericKey)myId; + for(ii = 0; ii < BITS_PER_KEY; ii++){ + assert(!List_IsEmpty(&highestMatchA[ii])); + ((NodeKey *)List_First(&highestMatchA[ii]))->key = myNodeKey; + } + + for(caseCount = 1; caseCount <= 3; caseCount++){ + /* + * Do all combinations of bottom two bits except my own + */ + targetId = myId ^ caseCount; + /* + * Search for a node that matches desired bits + */ + for(ii = 0; ; ii++){ + parents[caseCount].sin_addr.s_addr = myId * 1000 + ii; + parents[caseCount].sin_port = 0; + NodeKey_Init(&key, &parents[caseCount]); + if((key.key & 0x3) == targetId){ + if(caseCount == 1){ + assert((key.key & 0x2) == (myNodeKey & 0x2)); + assert((key.key & 0x1) != (myNodeKey & 0x1)); + hops = 1; + } + else{ + assert((key.key & 0x2) != (myNodeKey & 0x2)); + hops = 2; + } + HCHier_newNode(&parents[caseCount], hops, 999); + + break; + } + } + } + + + /* + * Case 0: same in all bits + */ + targetURLKey.key = myId; + qIndex = plaxtonParentQIndex(&targetURLKey, &iAmRoot); + assert(iAmRoot); + + + /* + * Case 1: differ in bit 0 --> queue is at level 0 + * and the (key & 0x1)th queue within level 0 + */ + targetURLKey.key = myId ^ 0x1; + qIndex = plaxtonParentQIndex(&targetURLKey, &iAmRoot); + assert(!iAmRoot); + assert(qIndex == ((unsigned)targetURLKey.key & 0x1)); + assert(plaxtonGetParentAddr(qIndex, &retAddr) == 0); + assert(retAddr.sin_addr.s_addr == parents[1].sin_addr.s_addr); + assert(plaxtonCheckIfParent(targetURLKey, &parents[1])); + assert(!plaxtonCheckIfParent(targetURLKey, &parents[2])); + assert(!plaxtonCheckIfParent(targetURLKey, &parents[3])); + + + /* + * Case 2: differ in bit 1; same bit 0 --> queue is + * at level 1 and the (key & 0x1)the queue within level 1 + */ + targetURLKey.key = myId ^ 0x2; + qIndex = plaxtonParentQIndex(&targetURLKey, &iAmRoot); + assert(!iAmRoot); + assert(qIndex == 2 + (((unsigned)targetURLKey.key & 0x2)>>1)); + assert(plaxtonGetParentAddr(qIndex, &retAddr) == 0); + assert(retAddr.sin_addr.s_addr == parents[2].sin_addr.s_addr); + assert(!plaxtonCheckIfParent(targetURLKey, &parents[1])); + assert(plaxtonCheckIfParent(targetURLKey, &parents[2])); + assert(!plaxtonCheckIfParent(targetURLKey, &parents[3])); + + /* + * Case 3: differ in bits 0 and 1; should be same as case 1 + */ + targetURLKey.key = myId ^ 0x3; + qIndex = plaxtonParentQIndex(&targetURLKey, &iAmRoot); + assert(!iAmRoot); + assert(qIndex == ((unsigned)targetURLKey.key & 0x1)); + assert(plaxtonGetParentAddr(qIndex, &retAddr) == 0); + assert(retAddr.sin_addr.s_addr == parents[1].sin_addr.s_addr); + assert(plaxtonCheckIfParent(targetURLKey, &parents[1])); + assert(!plaxtonCheckIfParent(targetURLKey, &parents[2])); + assert(!plaxtonCheckIfParent(targetURLKey, &parents[3])); + + plaxtonDestroy(); + } +} + +/* + *------------------------------------------------------------------ + * + * testParentsMultiRootCase -- + * + * Test the case where several nodes match + * an object in k bits, and no nodes match in + * more than k bits. Answer should be that the + * highest key is the winner. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------4.19.1998-------- + */ +static void +testParentsMultiRootCase(void) +{ + int eligibleFound, ii; + struct sockaddr_in sin, maxAddr, retAddr; + static unsigned int nodeMask = 0xFF; + static unsigned int objMask = 0x3F; + GenericKey maxKey; + URLKey targetURLKey; + int qIndex, iAmRoot; + NodeKey anotherNodeKey; + + plaxtonInit(2); + + assert(objMask < nodeMask); + assert((objMask & nodeMask) == objMask); + + /* + * Create a bunch of nodes that are eligible to be the root for + * for an object that matches them (and us) in objMask bits + * and mismatches in the next bit + */ + /* XXX targetURLKey.key = (myNodeKey & objMask) ^ (nodeMask ^ objMask);*/ + targetURLKey.key = (myNodeKey & nodeMask) ^ (nodeMask ^ objMask); + + qIndex = plaxtonParentQIndex(&targetURLKey, &iAmRoot); + assert(iAmRoot); + + maxKey = myNodeKey; + maxAddr = Config.Sockaddr.http->s; + ii = 0; + eligibleFound = 0; + while(eligibleFound < 40){ + sin.sin_addr.s_addr = 10000 + ii; + sin.sin_port = 0; + NodeKey_Init(&anotherNodeKey, &sin); + if((anotherNodeKey.key & nodeMask) == (myNodeKey & nodeMask)){ + eligibleFound++; + HCHier_newNode(&sin, 1000, 999); + if(anotherNodeKey.key > maxKey){ + /* + * Found new root for object + */ + maxKey = anotherNodeKey.key; + maxAddr = sin; + } + } + qIndex = plaxtonParentQIndex(&targetURLKey, &iAmRoot); + if(maxKey == myNodeKey){ + assert(iAmRoot); + } + if(iAmRoot){ + assert(maxKey == myNodeKey); + } + else{ + assert(plaxtonGetParentAddr(qIndex, &retAddr) == 0); + assert(qIndex >= nParentLevels * plaxtonbucketsPerLevel); + assert(retAddr.sin_addr.s_addr == maxAddr.sin_addr.s_addr); + } + ii++; + } + plaxtonDestroy(); + +} + + +/* + *------------------------------------------------------------------ + * + * stressTestParents -- + * + * Add and remove a few bizillion nodes and elements. I don't + * know what the right answers should be, but + * I want to test to try to trigger assertion failures. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-----------------------------------------------------4.19.1998-------- + */ +static void +stressTestParents(void) +{ + int ii; + URLKey targetURLKey; + int qIndex, iamRoot; + static int BIG = 100; /* should be much bigger for good test */ + struct sockaddr_in sin, retAddr; + + plaxtonInit(4); + + for(ii = 0; ii < BIG; ii++){ + sin.sin_addr.s_addr = 100000 + ii; + sin.sin_port = 0; + HCHier_newNode(&sin, 1000, 999); + } + + for(ii = 0; ii < BIG; ii++){ + sin.sin_addr.s_addr = 100000 + ii; + sin.sin_port = 0; + plaxtonchangeDistance(&sin); + } + + + for(ii = 0; ii < BIG; ii++){ + targetURLKey.key = ii; + qIndex = plaxtonParentQIndex(&targetURLKey, &iamRoot); + if(!iamRoot){ + assert(!plaxtonGetParentAddr(qIndex, &retAddr)); + } + } + + for(ii = 0; ii < BIG; ii++){ + sin.sin_addr.s_addr = 100000 + ii; + sin.sin_port = 0; + HCHier_delNode(&sin, 1000, 1999); + } + + for(ii = 0; ii < BIG; ii++){ + targetURLKey.key = ii; + qIndex = plaxtonParentQIndex(&targetURLKey, &iamRoot); + if(!iamRoot){ + assert(!plaxtonGetParentAddr(qIndex, &retAddr)); + } + } + + plaxtonDestroy(); +} + + +/* + *------------------------------------------------------------------ + * + * testParentsInternals -- + * + * Test the internal data structures of the parents + * for the non-multi-root case. + * Set the local key address to 0 then + * generate a quand-ary tree with all possible + * values of node keys from 1..15, and make + * sure that the nodes land in the right buckets + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------ + */ +static void +testParentsInternals(void) +{ + int ii, targetKey; + NodeKey anotherNodeKey, *current, *next; + GenericKey k; + struct sockaddr_in sin, nodes[16]; + int iAmRoot; + URLKey urlKey; + + plaxtonInit(2); + myNodeKey = 0; /* Fake it out to make tests easier to write and read */ + + targetKey = 1; ii = 0; + while(targetKey < 16){ + /* + * Note: netdb only keeps distance info on granularity of + * networks; we want to know what distances there are between + * nodes, so make nodes be on different (hopfully unused) subnets + */ + sin.sin_addr.s_addr = 15000 + ii*255; + sin.sin_port = 0; + if(netdbHintHops(sin.sin_addr)) != 0){ + ii++; + continue; /* Someone already used this address and gave it a distance */ + } + NodeKey_Init(&anotherNodeKey, &sin); + if((anotherNodeKey.key & 15) == targetKey){ + nodes[targetKey] = sin; + HCHier_newNode(&sin, targetKey*1000, 999); + targetKey++; + } + ii++; + } + + /* + * The level-0 lists should be in sorted order + */ + for(ii = 1; ii <= 3; ii++){ + LIST_FORALL2(&parentListA[ii], NodeKey *, current){ + if(current != (NodeKey *)List_Last(&parentListA[ii])){ + next = (NodeKey *)List_Next((List_Links *)current); + assert(!List_IsAtEnd(&parentListA[ii], (List_Links *)next)); + assert(HCHier_compareDistanceFromMe(NodeKey_GetAddr(current).sin_addr, + NodeKey_GetAddr(next).sin_addr) + <= 0); + } + } + } + + /* + * The level-0 lists XX01, XX10, and XX11 should each have + * four elements in the following order: 00--, 01--, 10--, + * and 11-- (where "--" is the index of the list) + */ + for(ii = 1; ii <= 3; ii++){ + assert((&parentListA[ii])->prevPtr + == (&parentListA[ii])->nextPtr->nextPtr->nextPtr->nextPtr); + + k = ((NodeKey *)((&parentListA[ii])->nextPtr))->key; + assert((k & 3) == ii); + assert(((k & 15) >> 2) == 0); /* 00 */ + + k = ((NodeKey *)((&parentListA[ii])->nextPtr->nextPtr))->key; + assert((k & 3) == ii); + assert(((k & 15) >> 2) == 1); /* 01 */ + + k = ((NodeKey *)((&parentListA[ii])->nextPtr->nextPtr->nextPtr))->key; + assert((k & 3) == ii); + assert(((k & 15) >> 2) == 2); /* 10 */ + + k = ((NodeKey *)((&parentListA[ii])->nextPtr->nextPtr->nextPtr->nextPtr))->key; + assert((k & 3) == ii); + assert(((k & 15) >> 2) == 3); /* 11 */ + + urlKey.key = 0xFFAA3400 | ii; + assert(plaxtonCheckIfParent(urlKey, &nodes[ii])); + assert(plaxtonParentQIndex(&urlKey, &iAmRoot) == ii); + assert(!iAmRoot); + } + /* + * The level-1 lists 0100, 1000, 1100 should each + * have one element + */ + /* 00 00 is list 4 and is empty */ + assert(List_IsEmpty(&parentListA[4])); + + /* 01 00 is list 5 and has one element "xxx0100" = 4 */ + assert(parentListA[5].nextPtr->nextPtr == &parentListA[5]); + k = ((NodeKey *)((&parentListA[5])->nextPtr))->key; + assert((k & 15) == 4); + urlKey.key = 0xFFAA1204; + assert(plaxtonCheckIfParent(urlKey, &nodes[4])); + assert(plaxtonParentQIndex(&urlKey, &iAmRoot) == 5); + assert(!iAmRoot); + + /* 10 00 is list 6 and has one element "xxx1000" = 8 */ + assert(parentListA[6].nextPtr->nextPtr == &parentListA[6]); + k = ((NodeKey *)((&parentListA[6])->nextPtr))->key; + assert((k & 15) == 8); + urlKey.key = 0xFFAA1208; + assert(plaxtonCheckIfParent(urlKey, &nodes[8])); + assert(plaxtonParentQIndex(&urlKey, &iAmRoot) == 6); + assert(!iAmRoot); + + /* 11 00 is list 7 and has one element "xxx1100" = 12 */ + assert(parentListA[7].nextPtr->nextPtr == &parentListA[7]); + k = ((NodeKey *)((&parentListA[7])->nextPtr))->key; + assert((k & 15) == 12); + urlKey.key = 0xFFAA120C; + assert(plaxtonCheckIfParent(urlKey, &nodes[12])); + assert(plaxtonParentQIndex(&urlKey, &iAmRoot) == 7); + assert(!iAmRoot); + + plaxtonDestroy(); +} + + +/* + *------------------------------------------------------------------ + * + * testParentsInternals2 -- + * + * Test finding parent when we can't match + * entire next level. + * + * Arguments: + * type1 arg1 -- description. + * + * Results: + * None. + * + * Side effects: + * None. + * + *--------------------------------------------------------5.4.1998---- + */ +static void +testParentsInternals2(void) +{ + int ii; + struct sockaddr_in sin; + NodeKey anotherNodeKey; + URLKey urlKey; + int iAmRoot; + + plaxtonInit(4); + myNodeKey = 0; /* Fake it out to make tests easier to write and read */ + + ii = 0; + while(1){ + /* + * Note: netdb only keeps distance info on granularity of + * networks; so make nodes be on different (hopfully unused) subnets + */ + sin.sin_addr.s_addr = 15000 + ii*255; + sin.sin_port = 0; + if(netdbHintHops(sin.sin_addr) != 0){ + ii++; + continue; /* Someone already used this address and gave it a distance */ + } + NodeKey_Init(&anotherNodeKey, &sin); + /* + * Want 0110 0000 + */ + if((anotherNodeKey.key & 0xFF) == 0x60){ + HCHier_newNode(&sin, 1000, 999); + break; + } + ii++; + } + assert((anotherNodeKey.key & 0xFF) == 0x60); + urlKey.key = 0x20; + assert(plaxtonCheckIfParent(urlKey, &sin)); + assert(plaxtonParentQIndex(&urlKey, &iAmRoot) == 22); + assert(!iAmRoot); + plaxtonDestroy(); +} + +/* + *------------------------------------------------------------------ + * + * testMakeMatchKey -- + * + * description. + * + * Arguments: + * None. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------5.4.1998------ + */ +static void +testMakeMatchKey(void) +{ + URLKey key; + GenericKey ret; + + key.key = 0xFFAABBCC; + ret = makeMatchKey(&key, 4, 0); + assert(ret == 0xC); + ret = makeMatchKey(&key, 4, 0xD); + assert(ret = 0xDC); + ret = makeMatchKey(&key, 0, 0xD); + assert(ret = 0xD); +} + +#endif /* DOTEST */ +