Viewing is open to everyone. Downloading and starring need a GitHub sign-in.
Sign in with GitHub
core_tests.cpp
v0.1.0-build.85
Latest
Commit ede38f5
Sign in with GitHub to download. Each download is recorded with your GitHub username.
| 1 | "ckey/ckey.h" |
| 2 | <array> |
| 3 | "check.h" |
| 4 | <cstdint> |
| 5 | <cstring> |
| 6 | <iostream> |
| 7 | <string> |
| 8 | <vector> |
| 9 | |
| 10 | static size_t occurrences(const std::string &s, const std::string &needle) { |
| 11 | size_t count=0, pos=0; |
| 12 | while ((pos=s.find(needle,pos))!=std::string::npos) { ++count; pos+=needle.size(); } |
| 13 | return count; |
| 14 | } |
| 15 | |
| 16 | int main() { |
| 17 | CKEY_CHECK(ckey_get_abi_version() == 4); |
| 18 | std::array<uint8_t,16> payload{}; |
| 19 | for (size_t i=0;i<payload.size();++i) payload[i]=static_cast<uint8_t>(i); |
| 20 | ckey_symbol a{}, b{}; ckey_diagnostics d{}; |
| 21 | CKEY_CHECK(ckey_encode_locator(payload.data(), payload.size(), &a, &d) == CKEY_OK); |
| 22 | CKEY_CHECK(d.status == CKEY_OK && a.side == 29 && a.abi_version == 4); |
| 23 | CKEY_CHECK(a.codeword[0]==0xc7 && a.codeword[1]==0x4b && a.codeword[2]==CKEY_FORMAT_VERSION && a.codeword[3]==1); |
| 24 | CKEY_CHECK(a.codeword[4]==0 && a.codeword[5]==16); |
| 25 | for (size_t i=0;i<16;++i) CKEY_CHECK(a.codeword[6+i]==i); |
| 26 | // CRC-32C("C7 4B 0E 01 00 10 00..0F") is a fixed interoperability check. |
| 27 | CKEY_CHECK(a.codeword[22]==0x94 && a.codeword[23]==0xb0 && a.codeword[24]==0xcb && a.codeword[25]==0x29); |
| 28 | constexpr uint8_t expected_parity[51]={0x45,0xa4,0xf1,0x66,0x5e,0x75,0x71,0x9e,0x8f,0xc5,0xed,0xfb,0xe2,0x4f,0xc1,0x88,0x93,0x17,0x08,0x46,0x9a,0x90,0x2f,0x84,0xb1,0x67,0xcc,0x40,0x4c,0x2f,0xce,0x04,0xdd,0xf4,0xc4,0xb7,0xda,0x58,0xd2,0xa8,0x56,0x37,0x2d,0x2a,0x28,0x67,0x2b,0x08,0x48,0x2c,0x0b}; |
| 29 | for (size_t i=0;i<51;++i) CKEY_CHECK(a.codeword[26+i]==expected_parity[i]); |
| 30 | CKEY_CHECK(std::strcmp(ckey_get_software_version(), CKEY_SOFTWARE_VERSION)==0); |
| 31 | std::array<uint8_t,16> decoded{}; |
| 32 | CKEY_CHECK(ckey_decode_locator(a.codeword, sizeof(a.codeword), decoded.data(), decoded.size(), &d)==CKEY_OK); |
| 33 | CKEY_CHECK(decoded==payload && d.corrected_symbols==0); |
| 34 | // Exercise every correctable error weight and the full unknown-error bound. |
| 35 | for (uint32_t pattern=0; pattern<4; ++pattern) for (size_t error_count=1; error_count<=25; ++error_count) { |
| 36 | auto damaged=a; |
| 37 | for (size_t e=0;e<error_count;++e) { |
| 38 | const size_t pos=(e*5 + pattern*3) % 77; |
| 39 | damaged.codeword[pos] ^= static_cast<uint8_t>(1 + ((pattern + e*29) % 255)); |
| 40 | } |
| 41 | decoded.fill(0); |
| 42 | CKEY_CHECK(ckey_decode_locator(damaged.codeword, sizeof(damaged.codeword), decoded.data(), decoded.size(), &d)==CKEY_OK); |
| 43 | CKEY_CHECK(decoded==payload && d.corrected_symbols==error_count); |
| 44 | } |
| 45 | for (uint32_t pattern=0; pattern<32; ++pattern) { |
| 46 | auto damaged=a; |
| 47 | for (size_t e=0;e<25;++e) { |
| 48 | const size_t pos=(e*5 + pattern*3) % 77; |
| 49 | damaged.codeword[pos] ^= static_cast<uint8_t>(1 + ((pattern + e*29) % 255)); |
| 50 | } |
| 51 | decoded.fill(0); |
| 52 | CKEY_CHECK(ckey_decode_locator(damaged.codeword, sizeof(damaged.codeword), decoded.data(), decoded.size(), &d)==CKEY_OK); |
| 53 | CKEY_CHECK(decoded==payload && d.corrected_symbols==25); |
| 54 | } |
| 55 | auto beyond_bound=a; |
| 56 | for (size_t e=0;e<26;++e) beyond_bound.codeword[(e*3)%77] ^= static_cast<uint8_t>(0x41u + e); |
| 57 | decoded.fill(0); |
| 58 | CKEY_CHECK(ckey_decode_locator(beyond_bound.codeword, sizeof(beyond_bound.codeword), decoded.data(), decoded.size(), &d)!=CKEY_OK); |
| 59 | CKEY_CHECK(ckey_encode_locator(payload.data(), payload.size(), &b, &d)==CKEY_OK); |
| 60 | CKEY_CHECK(std::memcmp(a.codeword,b.codeword,sizeof(a.codeword))==0); |
| 61 | CKEY_CHECK(ckey_encode_locator(payload.data(), 15, &b, &d)==CKEY_INVALID_ARGUMENT); |
| 62 | CKEY_CHECK(d.status==CKEY_INVALID_ARGUMENT); |
| 63 | |
| 64 | std::array<uint8_t, CKEY_TEXT_PAYLOAD_MAX_BYTES> text_payload{}; |
| 65 | for (size_t i = 0; i < text_payload.size(); ++i) text_payload[i] = static_cast<uint8_t>((i * 37 + 11) & 0xff); |
| 66 | ckey_text_symbol text_symbol{}; |
| 67 | CKEY_CHECK(ckey_encode_text(text_payload.data(), text_payload.size(), &text_symbol, &d) == CKEY_OK); |
| 68 | CKEY_CHECK(text_symbol.codeword[0] == 0xc7 && text_symbol.codeword[1] == 0x4b && text_symbol.codeword[2] == CKEY_TEXT_FORMAT_VERSION); |
| 69 | std::array<uint8_t, CKEY_TEXT_PAYLOAD_MAX_BYTES> text_decoded{}; size_t text_length = 0; |
| 70 | CKEY_CHECK(ckey_decode_text(text_symbol.codeword, sizeof(text_symbol.codeword), text_decoded.data(), text_decoded.size(), &text_length, &d) == CKEY_OK); |
| 71 | CKEY_CHECK(text_length == text_payload.size() && text_decoded == text_payload); |
| 72 | ckey_text_symbol repeated_text{}; |
| 73 | CKEY_CHECK(ckey_encode_text(text_payload.data(), 31, &repeated_text, &d) == CKEY_OK); |
| 74 | CKEY_CHECK(ckey_decode_text(repeated_text.codeword, sizeof(repeated_text.codeword), text_decoded.data(), text_decoded.size(), &text_length, &d) == CKEY_OK); |
| 75 | CKEY_CHECK(text_length == 31 && std::equal(text_payload.begin(), text_payload.begin()+31, text_decoded.begin())); |
| 76 | auto damaged_text = text_symbol; |
| 77 | for (size_t i = 0; i < 22; ++i) damaged_text.codeword[(i * 11 + 7) % CKEY_TEXT_CODEWORD_BYTES] ^= static_cast<uint8_t>(0x31 + i); |
| 78 | text_decoded.fill(0); text_length = 0; |
| 79 | CKEY_CHECK(ckey_decode_text(damaged_text.codeword, sizeof(damaged_text.codeword), text_decoded.data(), text_decoded.size(), &text_length, &d) == CKEY_OK); |
| 80 | CKEY_CHECK(text_length == text_payload.size() && text_decoded == text_payload && d.corrected_symbols == 22); |
| 81 | size_t text_needed = 0; |
| 82 | CKEY_CHECK(ckey_render_text_svg(&text_symbol, 16, nullptr, 0, &text_needed) == CKEY_INSUFFICIENT_BUFFER); |
| 83 | std::string text_svg(text_needed, '\0'); |
| 84 | CKEY_CHECK(ckey_render_text_svg(&text_symbol, 16, text_svg.data(), text_svg.size(), &text_needed) == CKEY_OK); |
| 85 | CKEY_CHECK(text_svg.find("CKey text symbol profile 2") != std::string::npos); |
| 86 | CKEY_CHECK(text_svg.find("stroke-width=\"0.2000\" stroke-linecap=\"round\"") != std::string::npos); |
| 87 | CKEY_CHECK(occurrences(text_svg,"stroke-width=\"0.8000\"/>")==4); |
| 88 | CKEY_CHECK(text_svg.find("M 1.7000 4.0000 A 2.3000 2.3000") != std::string::npos); |
| 89 | |
| 90 | std::array<uint8_t, CKEY_EXTENDED_PAYLOAD_MAX_BYTES> extended_payload{}; |
| 91 | for (size_t i=0;i<extended_payload.size();++i) extended_payload[i]=static_cast<uint8_t>((i*73+19)&0xff); |
| 92 | ckey_extended_symbol extended{}; |
| 93 | CKEY_CHECK(ckey_encode_extended(extended_payload.data(),extended_payload.size(),&extended,&d)==CKEY_OK); |
| 94 | // Profile 3's public stream is byte-interleaved: the header's bytes 0, 1, |
| 95 | // and 2 from block zero therefore occur at offsets 0, 10, and 20. |
| 96 | CKEY_CHECK(extended.side==116 && extended.codeword[0]==0xc7 && extended.codeword[10]==0x4b && extended.codeword[20]==CKEY_EXTENDED_FORMAT_VERSION); |
| 97 | std::array<uint8_t, CKEY_EXTENDED_PAYLOAD_MAX_BYTES> extended_decoded{}; size_t extended_length=0; |
| 98 | CKEY_CHECK(ckey_decode_extended(extended.codeword,sizeof(extended.codeword),extended_decoded.data(),extended_decoded.size(),&extended_length,&d)==CKEY_OK); |
| 99 | CKEY_CHECK(extended_length==extended_payload.size() && extended_decoded==extended_payload); |
| 100 | const std::string short_extended_text="CKey renderer test"; |
| 101 | ckey_extended_symbol short_extended{}, short_extended_repeat{}; |
| 102 | CKEY_CHECK(ckey_encode_extended(reinterpret_cast<const uint8_t *>(short_extended_text.data()),short_extended_text.size(),&short_extended,&d)==CKEY_OK); |
| 103 | CKEY_CHECK(short_extended.codeword[20]==CKEY_EXTENDED_FORMAT_VERSION && short_extended.codeword[30]==3 && short_extended.codeword[40]==1); |
| 104 | CKEY_CHECK(ckey_encode_extended(reinterpret_cast<const uint8_t *>(short_extended_text.data()),short_extended_text.size(),&short_extended_repeat,&d)==CKEY_OK); |
| 105 | CKEY_CHECK(std::memcmp(short_extended.codeword,short_extended_repeat.codeword,sizeof(short_extended.codeword))==0); |
| 106 | std::array<uint8_t,64> short_extended_decoded{}; size_t short_extended_length=0; |
| 107 | CKEY_CHECK(ckey_decode_extended(short_extended.codeword,sizeof(short_extended.codeword),short_extended_decoded.data(),short_extended_decoded.size(),&short_extended_length,&d)==CKEY_OK); |
| 108 | CKEY_CHECK(short_extended_length==short_extended_text.size() && std::equal(short_extended_text.begin(),short_extended_text.end(),short_extended_decoded.begin())); |
| 109 | ckey_extended_symbol short_extended_high_ecc{}; |
| 110 | CKEY_CHECK(ckey_encode_extended_tiered(reinterpret_cast<const uint8_t *>(short_extended_text.data()),short_extended_text.size(),CKEY_EXTENDED_TIER_512,&short_extended_high_ecc,&d)==CKEY_OK); |
| 111 | CKEY_CHECK(short_extended_high_ecc.codeword[40]==3); |
| 112 | constexpr std::array<ckey_extended_tier,7> tiers={CKEY_EXTENDED_TIER_256,CKEY_EXTENDED_TIER_384,CKEY_EXTENDED_TIER_512,CKEY_EXTENDED_TIER_768,CKEY_EXTENDED_TIER_1024,CKEY_EXTENDED_TIER_1536,CKEY_EXTENDED_TIER_2048}; |
| 113 | constexpr std::array<size_t,7> capacities={256,384,512,768,1024,1536,2048}; |
| 114 | for (size_t tier=0;tier<tiers.size();++tier) { |
| 115 | const size_t length=capacities[tier]-3; |
| 116 | std::vector<uint8_t> payload(length); |
| 117 | for(size_t i=0;i<length;++i) payload[i]=static_cast<uint8_t>((i*41+tier*17+3)&0xff); |
| 118 | ckey_extended_symbol symbol{}; |
| 119 | CKEY_CHECK(ckey_encode_extended_tiered(payload.data(),payload.size(),tiers[tier],&symbol,&d)==CKEY_OK); |
| 120 | std::vector<uint8_t> recovered(CKEY_EXTENDED_PAYLOAD_MAX_BYTES); size_t recovered_length=0; |
| 121 | CKEY_CHECK(ckey_decode_extended(symbol.codeword,sizeof(symbol.codeword),recovered.data(),recovered.size(),&recovered_length,&d)==CKEY_OK); |
| 122 | CKEY_CHECK(recovered_length==length && std::equal(payload.begin(),payload.end(),recovered.begin())); |
| 123 | auto damaged=symbol; |
| 124 | constexpr size_t errors_per_block=6; |
| 125 | for(size_t block=0;block<CKEY_EXTENDED_BLOCK_COUNT;++block) for(size_t error=0;error<errors_per_block;++error) |
| 126 | damaged.codeword[(error*19+5)*CKEY_EXTENDED_BLOCK_COUNT+block]^=static_cast<uint8_t>(0x51+error+block); |
| 127 | recovered_length=0; |
| 128 | CKEY_CHECK(ckey_decode_extended(damaged.codeword,sizeof(damaged.codeword),recovered.data(),recovered.size(),&recovered_length,&d)==CKEY_OK); |
| 129 | CKEY_CHECK(recovered_length==length && std::equal(payload.begin(),payload.end(),recovered.begin())); |
| 130 | CKEY_CHECK(d.corrected_symbols==errors_per_block*CKEY_EXTENDED_BLOCK_COUNT); |
| 131 | } |
| 132 | CKEY_CHECK(ckey_encode_extended_tiered(extended_payload.data(),300,CKEY_EXTENDED_TIER_256,&extended,&d)==CKEY_INVALID_ARGUMENT); |
| 133 | // The automatic 2,048-byte tier is RS(255,206), correcting 24 unknown |
| 134 | // byte errors per block. Interleaving distributes local loss across blocks. |
| 135 | auto damaged_extended=extended; |
| 136 | for (size_t block=0;block<CKEY_EXTENDED_BLOCK_COUNT;++block) for (size_t i=0;i<24;++i) |
| 137 | damaged_extended.codeword[(i*13%CKEY_EXTENDED_CODEWORD_BYTES_PER_BLOCK)*CKEY_EXTENDED_BLOCK_COUNT+block]^=static_cast<uint8_t>(0x31+i+block); |
| 138 | extended_decoded.fill(0); extended_length=0; |
| 139 | CKEY_CHECK(ckey_decode_extended(damaged_extended.codeword,sizeof(damaged_extended.codeword),extended_decoded.data(),extended_decoded.size(),&extended_length,&d)==CKEY_OK); |
| 140 | CKEY_CHECK(extended_length==extended_payload.size() && extended_decoded==extended_payload && d.corrected_symbols==240); |
| 141 | size_t extended_needed=0; |
| 142 | CKEY_CHECK(ckey_render_extended_svg(&extended,16,nullptr,0,&extended_needed)==CKEY_INSUFFICIENT_BUFFER); |
| 143 | std::string extended_svg(extended_needed,'\0'); |
| 144 | CKEY_CHECK(ckey_render_extended_svg(&extended,16,extended_svg.data(),extended_svg.size(),&extended_needed)==CKEY_OK); |
| 145 | CKEY_CHECK(extended_svg.find("CKey extended symbol profile 3")!=std::string::npos); |
| 146 | CKEY_CHECK(extended_svg.find("stroke-width=\"0.2800\"")!=std::string::npos); |
| 147 | CKEY_CHECK(extended_svg.find("viewBox=\"-4 -4 124 124\"")!=std::string::npos); |
| 148 | CKEY_CHECK(occurrences(extended_svg,"stroke-width=\"3.2000\"/>")==4); |
| 149 | CKEY_CHECK(extended_svg.find("M 6.8000 16.0000 A 9.2000 9.2000")!=std::string::npos); |
| 150 | |
| 151 | size_t needed=0; |
| 152 | CKEY_CHECK(ckey_render_svg(&a, 16, nullptr, 0, &needed)==CKEY_INSUFFICIENT_BUFFER); |
| 153 | CKEY_CHECK(needed>1000); |
| 154 | std::string svg(needed,'\0'); |
| 155 | CKEY_CHECK(ckey_render_svg(&a, 16, svg.data(), svg.size(), &needed)==CKEY_OK); |
| 156 | size_t ignored=0; |
| 157 | CKEY_CHECK(ckey_render_svg(&a, 0, nullptr, 0, &ignored)==CKEY_INVALID_ARGUMENT); |
| 158 | CKEY_CHECK(ckey_render_svg(&a, CKEY_MAX_RENDER_SCALE + 1, nullptr, 0, &ignored)==CKEY_INVALID_ARGUMENT); |
| 159 | CKEY_CHECK(svg.find("viewBox=\"-1 -1 31 31\"")!=std::string::npos); |
| 160 | CKEY_CHECK(svg.find("fill=\"#ffffff\"")!=std::string::npos); |
| 161 | CKEY_CHECK(svg.find("A 2.3000 2.3000")!=std::string::npos); |
| 162 | CKEY_CHECK(occurrences(svg,"r=\"0.1500\"")==occurrences(svg,"<circle ")); |
| 163 | CKEY_CHECK(occurrences(svg,"<circle ")>3); |
| 164 | CKEY_CHECK(svg.find("stroke-width=\"0.3000\" stroke-linecap=\"round\"/>")!=std::string::npos); |
| 165 | CKEY_CHECK(occurrences(svg,"stroke-width=\"0.8000\"/>")==4); |
| 166 | CKEY_CHECK(svg.find("M 1.7000 4.0000 A 2.3000 2.3000")!=std::string::npos); |
| 167 | CKEY_CHECK(occurrences(svg,"stroke-width=\"0.15\" stroke-linecap=\"round\"/>")==69); |
| 168 | CKEY_CHECK(svg.find("x=\"2.1300\" y=\"11.1300\"")!=std::string::npos); |
| 169 | CKEY_CHECK(svg.find("CKey locator symbol")!=std::string::npos); |
| 170 | std::cout << "CKey core tests passed\n"; |
| 171 | } |