123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- /* vim: set expandtab ts=4 sw=4: */
- /*
- * You may redistribute this program and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation,
- * either version 3 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, see <http://www.gnu.org/licenses/>.
- */
- #include "benc/String.h"
- #include "crypto/random/Random.h"
- #include "switch/EncodingScheme.h"
- #include "memory/Allocator.h"
- #include "memory/MallocAllocator.h"
- #include "util/Bits.h"
- #define Order_TYPE struct EncodingScheme_Form
- #define Order_NAME OfEncodingForms
- #define Order_COMPARE compareEncodingForms
- #include "util/Order.h"
- static inline int compareEncodingForms(const struct EncodingScheme_Form* a,
- const struct EncodingScheme_Form* b)
- {
- int diff = a->bitCount;
- diff -= b->bitCount;
- return diff == 0 ? 0 : diff > 0 ? 1 : -1;
- }
- static void randomForm(struct EncodingScheme_Form* form, struct Random* rand)
- {
- for (;;) {
- Random_bytes(rand, (uint8_t*)form, sizeof(struct EncodingScheme_Form));
- //Bits_memset(form, 0xff, sizeof(struct EncodingScheme_Form));
- form->bitCount &= ((1<<5)-1);
- if (!form->bitCount) {
- form->bitCount++;
- }
- form->prefixLen &= ((1<<5)-1);
- if (!form->prefixLen) {
- form->prefixLen++;
- }
- if (EncodingScheme_formSize(form) > 59) { continue; }
- if (form->prefixLen > 3 && (form->prefix & 0xf) == 1) { continue; }
- break;
- }
- form->prefix &= ((((uint64_t)1)<<form->prefixLen)-1);
- }
- static struct EncodingScheme* randomScheme(struct Random* rand, struct Allocator* alloc)
- {
- struct EncodingScheme* out =
- Allocator_malloc(alloc, sizeof(struct EncodingScheme));
- do {
- out->count = Random_uint32(rand) % 32;
- } while (out->count < 2);
- out->forms = Allocator_malloc(alloc, sizeof(struct EncodingScheme_Form) * out->count);
- for (int i = 0; i < (int)out->count; i++) {
- randomForm(&out->forms[i], rand);
- for (int j = 0; j < i; j++) {
- if (out->forms[i].bitCount == out->forms[j].bitCount) {
- i--;
- break;
- }
- int minPfx = (out->forms[i].prefixLen < out->forms[j].prefixLen)
- ? out->forms[i].prefixLen : out->forms[j].prefixLen;
- if (((out->forms[j].prefix ^ out->forms[i].prefix) & ((1<<minPfx)-1)) == 0) {
- // collision, destroy both entries and try again.
- if (j != i-1) {
- Bits_memcpyConst(&out->forms[j],
- &out->forms[i-1],
- sizeof(struct EncodingScheme_Form));
- }
- i -= 2;
- break;
- }
- }
- }
- Order_OfEncodingForms_qsort(out->forms, out->count);
- return out;
- }
- static void assertEqual(struct EncodingScheme* a,
- struct EncodingScheme* b)
- {
- Assert_always(b);
- Assert_always(a->count == b->count);
- Assert_always(
- !Bits_memcmp(a->forms, b->forms, sizeof(struct EncodingScheme_Form) * a->count));
- }
- static void randomTest(struct Allocator* parent, struct Random* rand)
- {
- struct Allocator* alloc = Allocator_child(parent);
- struct EncodingScheme* control = randomScheme(rand, alloc);
- String* data = EncodingScheme_serialize(control, alloc);
- struct EncodingScheme* test = EncodingScheme_deserialize(data, alloc);
- assertEqual(control, test);
- Allocator_free(alloc);
- }
- // Just make sure random crap doesn't crash it.
- static void fuzzTest(struct Allocator* parent, struct Random* rand)
- {
- struct Allocator* alloc = Allocator_child(parent);
- String* data = String_newBinary(NULL, Random_uint32(rand) % 1024, alloc);
- Random_bytes(rand, (uint8_t*)data->bytes, data->len);
- EncodingScheme_deserialize(data, alloc);
- Allocator_free(alloc);
- }
- /** Greatest possible number using x bits, all are set. */
- #define Bits_maxBits64(x) ((((uint64_t)1)<<(x))-1)
- static void encoding(struct Allocator* parent)
- {
- struct EncodingScheme_Form forms[3] = {
- {
- .prefixLen = 15,
- .bitCount = 2,
- .prefix = ((1<<15)-1) ^ (1<<1),
- }, {
- .prefixLen = 20,
- .bitCount = 4,
- .prefix = ((1<<20)-1) ^ (1<<2),
- }, {
- .prefixLen = 18,
- .bitCount = 8,
- .prefix = ((1<<18)-1) ^ (1<<3),
- }
- };
- // should encode as prefixLen, bitCount, prefix
- // 111111111111101 00010 01111 15/2/~0 -> 01001111 11110100 11111111 remainder 1
- // 11111111111111111011 00100 10100 20/4/~0 -> 00101001 11011001 11111111 remainder 7f
- // 111111111111110111 01000 10010 18/8/~0 -> 01111111 10001001 11101110 11111111 remainder 7
- // 00000111 <-- appliation of remainder
- //
- // In bytes:
- // 01001111 11110100 11111111 00101001 11011001 11111111
- // 01111111 10001001 11101110 11111111 00000111
- //
- // or \x4f\xf4\xff\x29\xd9\xff\x7f\x89\xee\xff\x07
- struct EncodingScheme list = {
- .count = 3,
- .forms = forms
- };
- struct Allocator* alloc = Allocator_child(parent);
- String* data = EncodingScheme_serialize(&list, alloc);
- Assert_always(data->len == 11);
- Assert_always(!Bits_memcmp(data->bytes, "\x4f\xf4\xff\x29\xd9\xff\x7f\x89\xee\xff\x07", 11));
- Allocator_free(alloc);
- }
- #define convertLabel_SELF_ROUTE 1
- #define convertLabel_TOO_BIG 2
- static int convertLabel(struct EncodingScheme_Form* iform,
- struct EncodingScheme_Form* oform,
- uint64_t label)
- {
- struct {
- struct EncodingScheme scheme;
- struct EncodingScheme_Form forms[2];
- } s;
- s.scheme.count = 2;
- s.scheme.forms = s.forms;
- Bits_memcpyConst(&s.forms[0], iform, sizeof(struct EncodingScheme_Form));
- Bits_memcpyConst(&s.forms[1], oform, sizeof(struct EncodingScheme_Form));
- int iformNum = 0;
- int oformNum = 1;
- struct EncodingScheme* scheme = &s.scheme;
- Assert_always(EncodingScheme_getFormNum(scheme, label) == iformNum);
- uint64_t label2 = EncodingScheme_convertLabel(scheme, label, oformNum);
- if ((label & Bits_maxBits64(s.forms[0].prefixLen + s.forms[0].bitCount)) == 1) {
- Assert_always(label2 == EncodingScheme_convertLabel_INVALID);
- return convertLabel_SELF_ROUTE;
- }
- if (Bits_log2x64(label) + EncodingScheme_formSize(oform) -
- EncodingScheme_formSize(iform) > 59)
- {
- Assert_always(label2 == EncodingScheme_convertLabel_INVALID);
- return convertLabel_TOO_BIG;
- }
- uint64_t additional = label >> s.forms[0].prefixLen;
- uint64_t director = additional & Bits_maxBits64(s.forms[0].bitCount);
- additional = additional >> s.forms[0].bitCount;
- // 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 ((s.forms[0].prefix & Bits_maxBits64(s.forms[0].prefixLen)) == 1) {
- // Swap 0 and 1 because zero is represented as 1 and 1 is handled specially
- 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 ((s.forms[1].prefix & Bits_maxBits64(s.forms[1].prefixLen)) == 1) {
- // Swap 1 and 0 back if necessary.
- if (director == 0) { director++; }
- } else if (director) {
- // Or move the numbers down by one.
- director--;
- }
- uint64_t converted = (additional << s.forms[1].bitCount) | director;
- converted = (converted << s.forms[1].prefixLen) | s.forms[1].prefix;
- if ((converted & Bits_maxBits64(s.forms[1].prefixLen + s.forms[1].bitCount)) == 1) {
- // looks like a self-route
- Assert_always(label2 == EncodingScheme_convertLabel_INVALID);
- return convertLabel_SELF_ROUTE;
- }
- Assert_always(label2 == converted);
- uint64_t label3 = EncodingScheme_convertLabel(scheme, label2, iformNum);
- Assert_always(label3 == label);
- return 0;
- }
- static void convertLabelRand(struct Random* rand, struct EncodingScheme* scheme)
- {
- for (int i = 0; i < 100; i++) {
- int oformNum;
- do {
- oformNum = Random_uint8(rand) % scheme->count;
- } while (!oformNum);
- int iformNum = Random_uint8(rand) % oformNum;
- uint64_t label = Random_uint64(rand);
- label &= (UINT64_MAX >> (Random_uint8(rand) % 63));
- for (;;) {
- // make sure the label has the right prefix
- label <<= scheme->forms[iformNum].prefixLen;
- label |= scheme->forms[iformNum].prefix;
- Assert_always(EncodingScheme_getFormNum(scheme, label) == iformNum);
- if (Bits_log2x64(label) > 59) {
- // fall through
- } else {
- int ret = convertLabel(&scheme->forms[iformNum], &scheme->forms[oformNum], label);
- if (ret == convertLabel_SELF_ROUTE) {
- i--;
- break;
- } else if (ret == convertLabel_TOO_BIG) {
- // fall through
- } else if (!ret) {
- // success
- break;
- } else {
- Assert_always(0);
- }
- }
- label >>= scheme->forms[iformNum].prefixLen + 1;
- }
- }
- }
- int main()
- {
- struct Allocator* alloc = MallocAllocator_new(20000000);
- struct Random* rand = Random_new(alloc, NULL, NULL);
- encoding(alloc);
- for (int i = 0; i < 1000; i++) {
- randomTest(alloc, rand);
- }
- for (int i = 0; i < 1000; i++) {
- fuzzTest(alloc, rand);
- }
- // for testing individual conversions in isolation.
- /*convertLabel(
- &(struct EncodingScheme_Form){.bitCount = 15, .prefixLen = 20, .prefix = 792003},
- &(struct EncodingScheme_Form){.bitCount = 31, .prefixLen = 30, .prefix = 385462496},
- 11331704259
- );*/
- for (int i = 0; i < 10; i++) {
- struct Allocator* tempAlloc = Allocator_child(alloc);
- struct EncodingScheme* scheme = randomScheme(rand, tempAlloc);
- convertLabelRand(rand, scheme);
- Allocator_free(tempAlloc);
- }
- return 0;
- }
|