static inline int encodeForm(struct EncodingScheme_Form* in, uint64_t* data, int bits) { *data |= ((uint64_t)in->prefixLen & Bits_maxBits64(5)) << bits; bits += 5; *data |= ((uint64_t)in->bitCount & Bits_maxBits64(5)) << bits; bits += 5; *data |= ((uint64_t)in->prefix & Bits_maxBits64(in->prefixLen)) << bits; return 5 + 5 + in->prefixLen; }
/** * Decode a form from it's binary representation. * Can only use a maximum of 41 bits. * * @param out the output which will be populated with the encoding form data. * @param data the binary data in host order. * @return the number of bits of data which were consumed by the decoding. * If the content is definitely not an encoding form, 0 is returned. */ static inline int decodeForm(struct EncodingScheme_Form* out, uint64_t d) { out->prefixLen = d & Bits_maxBits64(5); d >>= 5; int bitCount = d & Bits_maxBits64(5); if (bitCount < 1) { return 0; } out->bitCount = bitCount; d >>= 5; out->prefix = d & Bits_maxBits64(out->prefixLen); return 5 + 5 + out->prefixLen; }
bool EncodingScheme_isSane(struct EncodingScheme* scheme) { // Check for obviously insane encoding. if (scheme->count == 0) { // No encoding schemes return false; } if (scheme->count > 31) { // impossible, each form must have a different bitCount and bitCount // can only be expressed in 5 bits limiting it to 31 bits max and a form // using zero bits is not allowed so there are only 31 max possibilities. return false; } if (scheme->count == 1) { // Fixed width encoding, prefix is not allowed and bitcount must be non-zero if (scheme->forms[0].prefixLen != 0 || scheme->forms[0].prefix != 0) { // prefixLen must be 0 return false; } if (scheme->forms[0].bitCount == 0 || scheme->forms[0].bitCount > 31) { // bitcount must be non-zero and can't overflow the number return false; } return true; } // Variable width encoding. for (int i = 0; i < scheme->count; i++) { struct EncodingScheme_Form* form = &scheme->forms[i]; if (form->prefixLen == 0 || form->prefixLen > 31) { // Prefix must exist in order to distinguish between forms return false; } if (form->bitCount == 0 || form->bitCount > 31) { // Bitcount must be non-zero return false; } if (EncodingScheme_formSize(form) > 59) { // cannot be represented in the usable space in a label return false; } if (i > 0 && form->bitCount <= scheme->forms[i-1].bitCount) { // Forms must be in ascending order. return false; } for (int j = 0; j < scheme->count; j++) { // Forms must be distinguishable by their prefixes. if (j != i && (scheme->forms[j].prefix & Bits_maxBits64(form->prefixLen)) == form->prefix) { return false; } } } return true; }
uint64_t EncodingScheme_convertLabel(struct EncodingScheme* scheme, uint64_t routeLabel, int convertTo) { int formNum = EncodingScheme_getFormNum(scheme, routeLabel); if (formNum == EncodingScheme_getFormNum_INVALID) { return EncodingScheme_convertLabel_INVALID; } struct EncodingScheme_Form* currentForm = &scheme->forms[formNum]; if (scheme->count == 1 || (routeLabel & Bits_maxBits64(currentForm->prefixLen + currentForm->bitCount)) == 1) { // fixed width encoding or it's a self label, this is easy switch (convertTo) { case 0: case EncodingScheme_convertLabel_convertTo_CANNONICAL: return routeLabel; default: return EncodingScheme_convertLabel_INVALID; } } routeLabel >>= currentForm->prefixLen; uint64_t director = routeLabel & Bits_maxBits64(currentForm->bitCount); routeLabel >>= currentForm->bitCount; // ACKTUNG: Magic afoot! // Conversions are necessary for two reasons. // #1 ensure 0001 always references interface 1, the self interface. // #2 reuse interface the binary encoding for interface 1 in other EncodingForms // because interface 1 cannot be expressed as anything other than 0001 if ((currentForm->prefix & Bits_maxBits64(currentForm->prefixLen)) == 1) { // Swap 0 and 1 if the prefix is 1, this makes 0001 alias to 1 // because 0 can never show up in the wild, we reuse it for 1. Assert_true(director != 0); if (director == 1) { director--; } } else if (director) { // Reuse the number 1 for 2 and 2 for 3 etc. to gain an extra slot in all other encodings. director++; } if (convertTo == EncodingScheme_convertLabel_convertTo_CANNONICAL) { // Take into account the fact that if the destination form does not have a 1 prefix, // an extra number will be available. int minBitsA = Bits_log2x64(director) + 1; int minBitsB = Bits_log2x64(director-1) + 1; for (int i = 0; i < scheme->count; i++) { struct EncodingScheme_Form* form = &scheme->forms[i]; int minBits = ((form->prefix & Bits_maxBits64(form->prefixLen)) == 1) ? minBitsA : minBitsB; if (form->bitCount >= minBits) { convertTo = i; break; } } } if (convertTo < 0 || convertTo >= scheme->count) { // convertTo value is insane return EncodingScheme_convertLabel_INVALID; } struct EncodingScheme_Form* nextForm = &scheme->forms[convertTo]; if ((nextForm->prefix & Bits_maxBits64(nextForm->prefixLen)) == 1) { // Swap 1 and 0 back if necessary. if (director == 0) { director++; } } else if (director) { // Or move the numbers down by one. director--; } if ((Bits_log2x64(director) + 1) > nextForm->bitCount) { // won't fit in requested form return EncodingScheme_convertLabel_INVALID; } if (Bits_log2x64(routeLabel) + EncodingScheme_formSize(nextForm) > 59) { return EncodingScheme_convertLabel_INVALID; } routeLabel <<= nextForm->bitCount; routeLabel |= director; routeLabel <<= nextForm->prefixLen; routeLabel |= nextForm->prefix; if ((routeLabel & Bits_maxBits64(nextForm->prefixLen + nextForm->bitCount)) == 1) { // looks like a self-route return EncodingScheme_convertLabel_INVALID; } return routeLabel; }
static void numberCompressions_generic( uint32_t nInterfaces, uint32_t (*bitsUsedForLabel)(const uint64_t label), uint32_t (*bitsUsedForNumber)(const uint32_t number), uint64_t (*getCompressed)(const uint32_t number, const uint32_t bitsUsed), uint32_t (*getDecompressed)(const uint64_t label, const uint32_t bitsUsed), struct EncodingScheme* (* defineScheme)(struct Allocator* alloc) ) { uint8_t bitWidths[64] = { 0 }; for (uint32_t i = 0; i < nInterfaces; ++i) { bitWidths[bitsUsedForNumber(i)] = 1; } for (uint32_t bits = 0; bits < 64; ++bits) { if (!bitWidths[bits]) { continue; } for (uint32_t i = 0; i < nInterfaces; ++i) { /* only check for greater-or-equal bit widths */ if (bits < bitsUsedForNumber(i)) { continue; } uint64_t label = getCompressed(i, bits); if (1 == i) { Assert_true(1 == label); continue; } Assert_true(bits == bitsUsedForLabel(label)); Assert_true(i == getDecompressed(label, bits)); } } for (uint64_t label = 0; label < 0x10000u; ++label) { uint32_t bits = bitsUsedForLabel(label); Assert_true(1 == bitWidths[bits]); if (1 == (label & Bits_maxBits64(bits))) { //Assert_true(4 == bits); Assert_true(1 == getDecompressed(label, bits)); } else { uint32_t i = getDecompressed(label, bits); Assert_true(i < nInterfaces); } } struct Allocator* alloc = MallocAllocator_new(20000); struct EncodingScheme* scheme = defineScheme(alloc); for (uint32_t i = 0; i < nInterfaces; i++) { for (int j = 0; j < scheme->count; j++) { int bits = scheme->forms[j].prefixLen + scheme->forms[j].bitCount; if ((int)bitsUsedForNumber(i) > bits) { continue; } uint64_t label = getCompressed(i, bits); for (int k = j; k < scheme->count; k++) { uint64_t labelB = EncodingScheme_convertLabel(scheme, label, k); if (1 == i && k != 0) { Assert_true(1 == label); Assert_true(EncodingScheme_convertLabel_INVALID == labelB); continue; } int bitsB = bitsUsedForLabel(labelB); Assert_true(bitsB == scheme->forms[k].prefixLen + scheme->forms[k].bitCount || (i == 1 && bitsB == 4)); Assert_true(i == getDecompressed(labelB, bitsB)); uint64_t labelC = EncodingScheme_convertLabel(scheme, labelB, j); Assert_true(labelC == label); } } } Allocator_free(alloc); }