Viewing is open to everyone. Downloading and starring need a GitHub sign-in. Sign in with GitHub

api_codec.inc

v0.1.0-build.85 Latest Commit ede38f5

Size
23 KB
Lines
315
Language
cpp
SHA-256
7a15987af1ae…
View on GitHub

Sign in with GitHub to download. Each download is recorded with your GitHub username.

1// Public C ABI: codec functions. Included after core_internal.inc.
2
3extern "C" {
4
5uint32_t ckey_get_abi_version(void) { return CKEY_ABI_VERSION; }
6
7const char *ckey_get_software_version(void) { return CKEY_SOFTWARE_VERSION; }
8
9ckey_status ckey_encode_locator(const uint8_t *payload, size_t payload_len,
10 ckey_symbol *out, ckey_diagnostics *diag) {
11 if (!payload || !out) { set_status(diag, CKEY_INVALID_ARGUMENT, "payload and output are required"); return CKEY_INVALID_ARGUMENT; }
12 if (payload_len != CKEY_LOCATOR_PAYLOAD_BYTES) { set_status(diag, CKEY_INVALID_ARGUMENT, "locator payload must be exactly 16 bytes"); return CKEY_INVALID_ARGUMENT; }
13
14 std::array<uint8_t, kCodewordBytes> cw{};
15 // Frame: magic C7 4B, current format, profile 01, length u16be, 16 payload
16 // bytes, CRC-32C over the first 22 bytes, followed by 51 RS parity bytes.
17 cw[0] = 0xc7; cw[1] = 0x4b; cw[2] = CKEY_FORMAT_VERSION; cw[3] = 1; cw[4] = 0; cw[5] = 16;
18 std::memcpy(cw.data() + 6, payload, 16);
19 const uint32_t crc = crc32c(cw.data(), 22);
20 cw[22] = static_cast<uint8_t>(crc >> 24); cw[23] = static_cast<uint8_t>(crc >> 16);
21 cw[24] = static_cast<uint8_t>(crc >> 8); cw[25] = static_cast<uint8_t>(crc);
22 const auto gen = rs_generator();
23 std::array<uint8_t, kCodewordBytes> work = cw;
24 for (size_t i = 0; i < kDataBytes; ++i) {
25 const uint8_t factor = work[i];
26 for (size_t j = 0; j <= kParityBytes; ++j) work[i + j] ^= gf.mul(factor, gen[j]);
27 }
28 std::memcpy(cw.data() + kDataBytes, work.data() + kDataBytes, kParityBytes);
29 std::memset(out, 0, sizeof(*out)); out->abi_version = CKEY_ABI_VERSION; out->side = kSide;
30 std::memcpy(out->codeword, cw.data(), cw.size());
31 set_status(diag, CKEY_OK, "encoded Profile 1 locator symbol");
32 return CKEY_OK;
33}
34
35ckey_status ckey_decode_locator(const uint8_t *codeword, size_t codeword_len,
36 uint8_t *payload, size_t out_capacity,
37 ckey_diagnostics *diag) {
38 if (!codeword || !payload) { set_status(diag, CKEY_INVALID_ARGUMENT, "codeword and payload output are required"); return CKEY_INVALID_ARGUMENT; }
39 if (codeword_len != kCodewordBytes || out_capacity < CKEY_LOCATOR_PAYLOAD_BYTES) { set_status(diag, CKEY_INVALID_ARGUMENT, "locator decode requires 77 bytes and a 16-byte output buffer"); return CKEY_INVALID_ARGUMENT; }
40 std::array<uint8_t,kCodewordBytes> cw{}; std::memcpy(cw.data(), codeword, cw.size());
41 uint32_t corrected = 0;
42 if (!rs_correct(cw, corrected)) { set_status(diag, CKEY_UNCORRECTABLE, "Reed-Solomon correction failed"); return CKEY_UNCORRECTABLE; }
43 // Formats 8 and 11--13 remain readable for historic camera fixtures.
44 // Encoder output is the current format; the newer inset-finder geometry
45 // is unambiguously declared by format 11.
46 if (cw[0] != 0xc7 || cw[1] != 0x4b || (cw[2] != 8u && cw[2] != 11u && cw[2] != 12u && cw[2] != 13u && cw[2] != CKEY_FORMAT_VERSION) || cw[3] != 1 || cw[4] != 0 || cw[5] != 16) {
47 set_status(diag, CKEY_INTEGRITY_FAILURE, "corrected frame has invalid magic, version, profile, or length"); return CKEY_INTEGRITY_FAILURE;
48 }
49 const uint32_t expected = (static_cast<uint32_t>(cw[22]) << 24) | (static_cast<uint32_t>(cw[23]) << 16) | (static_cast<uint32_t>(cw[24]) << 8) | cw[25];
50 if (crc32c(cw.data(), 22) != expected) { set_status(diag, CKEY_INTEGRITY_FAILURE, "CRC-32C validation failed"); return CKEY_INTEGRITY_FAILURE; }
51 std::memcpy(payload, cw.data() + 6, CKEY_LOCATOR_PAYLOAD_BYTES);
52 set_status(diag, CKEY_OK, corrected ? "decoded locator; Reed-Solomon symbols corrected" : "decoded locator; no correction needed");
53 if (diag) diag->corrected_symbols = corrected;
54 return CKEY_OK;
55}
56
57ckey_status ckey_encode_text(const uint8_t *payload, size_t payload_len,
58 ckey_text_symbol *out, ckey_diagnostics *diag) {
59 if (!payload || !out) { set_status(diag, CKEY_INVALID_ARGUMENT, "text payload and output are required"); return CKEY_INVALID_ARGUMENT; }
60 if (payload_len > CKEY_TEXT_PAYLOAD_MAX_BYTES) { set_status(diag, CKEY_INVALID_ARGUMENT, "text profile payload exceeds 200 bytes"); return CKEY_INVALID_ARGUMENT; }
61 std::memset(out, 0, sizeof(*out));
62 out->abi_version = CKEY_ABI_VERSION; out->side = kSide; out->payload_length = static_cast<uint32_t>(payload_len);
63 // Frame: magic, current format, profile 2, u16be payload length, payload,
64 // CRC-32C, then deterministic SplitMix32 visual fill. All 210 data bytes
65 // remain protected by the full RS(255,210) code.
66 std::vector<uint8_t> codeword(kTextCodewordBytes, 0);
67 codeword[0] = 0xc7; codeword[1] = 0x4b; codeword[2] = CKEY_TEXT_FORMAT_VERSION; codeword[3] = 2;
68 codeword[4] = static_cast<uint8_t>(payload_len >> 8); codeword[5] = static_cast<uint8_t>(payload_len);
69 std::memcpy(codeword.data() + 6, payload, payload_len);
70 const uint32_t crc = crc32c(codeword.data(), 6 + payload_len);
71 const size_t crc_index = 6 + payload_len;
72 codeword[crc_index] = static_cast<uint8_t>(crc >> 24); codeword[crc_index + 1] = static_cast<uint8_t>(crc >> 16);
73 codeword[crc_index + 2] = static_cast<uint8_t>(crc >> 8); codeword[crc_index + 3] = static_cast<uint8_t>(crc);
74 fill_padding(codeword.data(), crc_index + 4, kTextDataBytes, crc_index + 4);
75 rs_encode_generic(codeword, kTextDataBytes, kTextParityBytes);
76 std::memcpy(out->codeword, codeword.data(), codeword.size());
77 set_status(diag, CKEY_OK, "encoded Profile 2 text symbol");
78 return CKEY_OK;
79}
80
81ckey_status ckey_decode_text(const uint8_t *codeword, size_t codeword_len,
82 uint8_t *payload, size_t out_capacity, size_t *out_payload_len,
83 ckey_diagnostics *diag) {
84 if (!codeword || !payload || !out_payload_len || codeword_len != kTextCodewordBytes) {
85 set_status(diag, CKEY_INVALID_ARGUMENT, "text decode requires a 255-byte codeword and output length"); return CKEY_INVALID_ARGUMENT;
86 }
87 std::vector<uint8_t> corrected(codeword, codeword + codeword_len);
88 uint32_t corrections = 0;
89 if (!rs_correct_generic(corrected, kTextParityBytes, corrections)) { set_status(diag, CKEY_UNCORRECTABLE, "text Reed-Solomon correction failed"); return CKEY_UNCORRECTABLE; }
90 if (corrected[0] != 0xc7 || corrected[1] != 0x4b ||
91 !is_text_format(corrected[2]) || corrected[3] != 2) {
92 set_status(diag, CKEY_INTEGRITY_FAILURE, "text frame has invalid magic, format, or profile"); return CKEY_INTEGRITY_FAILURE;
93 }
94 const size_t length = (static_cast<size_t>(corrected[4]) << 8) | corrected[5];
95 if (length > CKEY_TEXT_PAYLOAD_MAX_BYTES || 6 + length + 4 > kTextDataBytes) {
96 set_status(diag, CKEY_INTEGRITY_FAILURE, "text frame declares a length beyond the profile"); return CKEY_INTEGRITY_FAILURE;
97 }
98 if (out_capacity < length) {
99 set_status(diag, CKEY_INSUFFICIENT_BUFFER, "text payload exceeds output capacity"); return CKEY_INSUFFICIENT_BUFFER;
100 }
101 const size_t crc_index = 6 + length;
102 const uint32_t expected = (static_cast<uint32_t>(corrected[crc_index]) << 24) |
103 (static_cast<uint32_t>(corrected[crc_index + 1]) << 16) |
104 (static_cast<uint32_t>(corrected[crc_index + 2]) << 8) | corrected[crc_index + 3];
105 if (crc32c(corrected.data(), crc_index) != expected) { set_status(diag, CKEY_INTEGRITY_FAILURE, "text CRC-32C validation failed"); return CKEY_INTEGRITY_FAILURE; }
106 // Formats 0.23 and later are checked against the normative SplitMix32 fill.
107 if ((corrected[2] == 23u || corrected[2] == CKEY_TEXT_FORMAT_VERSION) &&
108 !padding_matches(corrected.data(), crc_index + 4, kTextDataBytes, crc_index + 4)) {
109 set_status(diag, CKEY_INTEGRITY_FAILURE, "text visual-fill validation failed"); return CKEY_INTEGRITY_FAILURE;
110 }
111 std::memcpy(payload, corrected.data() + 6, length); *out_payload_len = length;
112 set_status(diag, CKEY_OK, corrections ? "decoded text profile; Reed-Solomon symbols corrected" : "decoded text profile; no correction needed");
113 if (diag) diag->corrected_symbols = corrections;
114 return CKEY_OK;
115}
116
117ckey_status ckey_encode_extended(const uint8_t *payload, size_t payload_len,
118 ckey_extended_symbol *out, ckey_diagnostics *diag) {
119 return ckey_encode_extended_tiered(payload, payload_len, CKEY_EXTENDED_TIER_AUTO, out, diag);
120}
121
122ckey_status ckey_encode_extended_tiered(const uint8_t *payload, size_t payload_len,
123 ckey_extended_tier requested_tier,
124 ckey_extended_symbol *out, ckey_diagnostics *diag) {
125 if (!payload || !out) { set_status(diag, CKEY_INVALID_ARGUMENT, "extended payload and output are required"); return CKEY_INVALID_ARGUMENT; }
126 if (payload_len == 0 || payload_len > CKEY_EXTENDED_PAYLOAD_MAX_BYTES) { set_status(diag, CKEY_INVALID_ARGUMENT, "extended profile payload must contain 1 to 2048 bytes"); return CKEY_INVALID_ARGUMENT; }
127 const size_t tier_index = extended_tier_index(requested_tier, payload_len);
128 if (tier_index == kExtendedTierCapacities.size() || payload_len > kExtendedTierCapacities[tier_index]) {
129 set_status(diag, CKEY_INVALID_ARGUMENT, "payload does not fit the selected extended capacity tier"); return CKEY_INVALID_ARGUMENT;
130 }
131 const size_t data_bytes = (11 + kExtendedTierCapacities[tier_index] + kExtendedBlocks - 1) / kExtendedBlocks;
132 const size_t parity_bytes = kExtendedBlockBytes - data_bytes;
133 std::vector<uint8_t> framed(kExtendedBlocks * data_bytes, 0);
134 framed[0]=0xc7; framed[1]=0x4b; framed[2]=CKEY_EXTENDED_FORMAT_VERSION; framed[3]=3;
135 framed[4]=static_cast<uint8_t>(tier_index + 1);
136 framed[5]=static_cast<uint8_t>(payload_len >> 8); framed[6]=static_cast<uint8_t>(payload_len);
137 std::memcpy(framed.data()+7,payload,payload_len);
138 const size_t crc_index=7+payload_len; const uint32_t crc=crc32c(framed.data(),crc_index);
139 framed[crc_index]=static_cast<uint8_t>(crc>>24); framed[crc_index+1]=static_cast<uint8_t>(crc>>16);
140 framed[crc_index+2]=static_cast<uint8_t>(crc>>8); framed[crc_index+3]=static_cast<uint8_t>(crc);
141 fill_padding(framed.data(), crc_index + 4, framed.size(), crc_index + 4);
142 std::array<std::array<uint8_t,kExtendedBlockBytes>,kExtendedBlocks> blocks{};
143 for (size_t block=0;block<kExtendedBlocks;++block) {
144 std::copy_n(framed.data()+block*data_bytes,data_bytes,blocks[block].begin());
145 std::vector<uint8_t> work(blocks[block].begin(),blocks[block].end());
146 rs_encode_generic(work,data_bytes,parity_bytes);
147 std::copy(work.begin(),work.end(),blocks[block].begin());
148 }
149 std::memset(out,0,sizeof(*out)); out->abi_version=CKEY_ABI_VERSION; out->side=kExtendedSide; out->payload_length=static_cast<uint32_t>(payload_len);
150 // Byte interleaving maps local optical damage across all independent blocks.
151 for (size_t byte=0;byte<kExtendedBlockBytes;++byte) for (size_t block=0;block<kExtendedBlocks;++block)
152 out->codeword[byte*kExtendedBlocks+block]=blocks[block][byte];
153 char message[96]; std::snprintf(message, sizeof(message), "encoded profile 3 tier %zu; RS(255,%zu) x 10", kExtendedTierCapacities[tier_index], data_bytes);
154 set_status(diag,CKEY_OK,message); return CKEY_OK;
155}
156
157ckey_status ckey_decode_extended(const uint8_t *codeword, size_t codeword_len,
158 uint8_t *out_payload, size_t out_capacity, size_t *out_payload_len,
159 ckey_diagnostics *diag) {
160 if (!codeword || !out_payload || !out_payload_len || codeword_len != kExtendedCodewordBytes) { set_status(diag,CKEY_INVALID_ARGUMENT,"extended decode requires a 2550-byte codeword and output length"); return CKEY_INVALID_ARGUMENT; }
161 const auto attempt = [&](size_t data_bytes, size_t parity_bytes, bool tiered) -> ckey_status {
162 std::vector<uint8_t> framed(kExtendedBlocks * data_bytes, 0);
163 uint32_t corrections = 0;
164 for (size_t block=0; block<kExtendedBlocks; ++block) {
165 std::vector<uint8_t> work(kExtendedBlockBytes);
166 for (size_t byte=0; byte<kExtendedBlockBytes; ++byte)
167 work[byte] = codeword[byte*kExtendedBlocks+block];
168 uint32_t fixed=0;
169 if (!rs_correct_generic(work, parity_bytes, fixed)) return CKEY_UNCORRECTABLE;
170 corrections += fixed;
171 std::copy_n(work.begin(), data_bytes, framed.data()+block*data_bytes);
172 }
173 if (framed[0]!=0xc7 || framed[1]!=0x4b || framed[3]!=3) return CKEY_INTEGRITY_FAILURE;
174 size_t length=0, payload_offset=0, crc_index=0;
175 if (tiered) {
176 if (!is_tiered_extended_format(framed[2]) || framed[4] == 0 || framed[4] > kExtendedTierCapacities.size()) return CKEY_INTEGRITY_FAILURE;
177 const size_t selected = framed[4]-1;
178 if (data_bytes != (11+kExtendedTierCapacities[selected]+kExtendedBlocks-1)/kExtendedBlocks) return CKEY_INTEGRITY_FAILURE;
179 length=(static_cast<size_t>(framed[5])<<8)|framed[6]; payload_offset=7; crc_index=payload_offset+length;
180 if (length==0 || length>kExtendedTierCapacities[selected] || crc_index+4>framed.size()) return CKEY_INTEGRITY_FAILURE;
181 } else {
182 if (framed[2]!=10u && framed[2]!=16u) return CKEY_INTEGRITY_FAILURE;
183 length=(static_cast<size_t>(framed[4])<<8)|framed[5]; payload_offset=6; crc_index=payload_offset+length;
184 if (length==0 || length>CKEY_EXTENDED_PAYLOAD_MAX_BYTES || crc_index+4>framed.size()) return CKEY_INTEGRITY_FAILURE;
185 }
186 const uint32_t expected=(static_cast<uint32_t>(framed[crc_index])<<24)|(static_cast<uint32_t>(framed[crc_index+1])<<16)|(static_cast<uint32_t>(framed[crc_index+2])<<8)|framed[crc_index+3];
187 if (crc32c(framed.data(),crc_index)!=expected) return CKEY_INTEGRITY_FAILURE;
188 if (tiered && !padding_matches(framed.data(),crc_index+4,framed.size(),crc_index+4)) return CKEY_INTEGRITY_FAILURE;
189 if (out_capacity<length) {
190 set_status(diag,CKEY_INSUFFICIENT_BUFFER,"extended output buffer is smaller than decoded payload");
191 return CKEY_INSUFFICIENT_BUFFER;
192 }
193 std::memcpy(out_payload,framed.data()+payload_offset,length); *out_payload_len=length;
194 set_status(diag,CKEY_OK,corrections ? "decoded extended profile; Reed-Solomon symbols corrected" : "decoded extended profile; no correction needed");
195 if (diag) diag->corrected_symbols=corrections;
196 return CKEY_OK;
197 };
198
199 // Preserve the original Profile 3 RS(255,223) frame reader. The format and
200 // profile bytes are the third and fourth data bytes in block zero. Checking
201 // them before trying every new parity split avoids doing eight full RS
202 // decodes for an unrelated square candidate in the live camera path.
203 const bool tier_hint_valid = codeword[40] > 0 && codeword[40] <= kExtendedTierCapacities.size();
204 const bool likely_tiered = is_tiered_extended_format(codeword[20]) || (codeword[30]==3 && tier_hint_valid);
205 if (!is_tiered_extended_format(codeword[20])) {
206 const ckey_status legacy=attempt(kExtendedDataBytes,kExtendedParityBytes,false);
207 if (legacy==CKEY_OK || legacy==CKEY_INSUFFICIENT_BUFFER) return legacy;
208 }
209 if (!likely_tiered) { set_status(diag,CKEY_UNCORRECTABLE,"sampled frame does not identify a supported Profile 3 format"); return CKEY_UNCORRECTABLE; }
210
211 // New tiers share the same 2,550-byte optical codeword. The selected tier
212 // changes data/parity split per block, trading capacity for ECC while
213 // keeping the full visual lattice populated. Try the indicated tier first.
214 std::array<size_t, CKEY_EXTENDED_TIER_COUNT> order{};
215 for(size_t i=0;i<order.size();++i) order[i]=i;
216 const size_t hinted = codeword[40] > 0 && codeword[40] <= order.size() ? codeword[40]-1 : order.size();
217 if(hinted<order.size()) { order[0]=hinted; size_t next=1; for(size_t i=0;i<order.size();++i) if(i!=hinted) order[next++]=i; }
218 for(size_t tier:order) {
219 const size_t data_bytes=(11+kExtendedTierCapacities[tier]+kExtendedBlocks-1)/kExtendedBlocks;
220 const ckey_status result=attempt(data_bytes,kExtendedBlockBytes-data_bytes,true);
221 if(result==CKEY_OK || result==CKEY_INSUFFICIENT_BUFFER) return result;
222 }
223 set_status(diag,CKEY_UNCORRECTABLE,"no Profile 3 capacity tier passed Reed-Solomon, frame, padding, and CRC checks"); return CKEY_UNCORRECTABLE;
224}
225
226uint32_t ckey_scalable_modules_across(uint32_t version) {
227 return version >= 1 && version <= CKEY_SCALABLE_VERSION_COUNT ? static_cast<uint32_t>(kScalableGrid[version - 1]) : 0u;
228}
229
230size_t ckey_scalable_capacity(uint32_t version, ckey_ecc_level level, ckey_scalable_layout layout) {
231 return scalable_capacity(static_cast<int>(version), static_cast<int>(level), static_cast<int>(layout));
232}
233
234ckey_status ckey_encode_scalable(const uint8_t *payload, size_t payload_len, uint32_t version, ckey_ecc_level level,
235 ckey_scalable_layout layout, ckey_scalable_symbol *out, ckey_diagnostics *diag) {
236 const int family = static_cast<int>(layout);
237 if ((!payload && payload_len) || !out || static_cast<int>(level) < 0 || static_cast<int>(level) > 3 || version > CKEY_SCALABLE_VERSION_COUNT ||
238 family < 0 || family >= kScalableFamilies) {
239 set_status(diag, CKEY_INVALID_ARGUMENT, "invalid Profile 0 payload, version, level, or output"); return CKEY_INVALID_ARGUMENT;
240 }
241 int v = static_cast<int>(version);
242 if (v == 0) { for (v = 1; v <= kScalableVersions && scalable_capacity(v, level, family) < payload_len; ++v) {} }
243 if (v > kScalableVersions || scalable_capacity(v, level, family) < payload_len) {
244 set_status(diag, CKEY_INVALID_ARGUMENT, "payload exceeds the selected Profile 0 version and level"); return CKEY_INVALID_ARGUMENT;
245 }
246 const ScalableLayout &L = scalable_layout(v, family);
247 if (L.codeword_bytes > CKEY_SCALABLE_MAX_CODEWORD_BYTES) { set_status(diag, CKEY_INTERNAL_ERROR, "Profile 0 layout exceeds codeword storage"); return CKEY_INTERNAL_ERROR; }
248 const auto blocks = scalable_blocks(L.codeword_bytes, level);
249 std::vector<uint8_t> framed(scalable_data_bytes(L.codeword_bytes, level), 0);
250 framed[0] = 0xc7; framed[1] = 0x4b; framed[2] = L.format; framed[3] = 0;
251 framed[4] = static_cast<uint8_t>(v); framed[5] = static_cast<uint8_t>(level);
252 framed[6] = static_cast<uint8_t>(payload_len >> 8); framed[7] = static_cast<uint8_t>(payload_len);
253 if (payload_len) std::memcpy(framed.data() + kScalableHeaderBytes, payload, payload_len);
254 const size_t crc_index = kScalableHeaderBytes + payload_len; const uint32_t crc = crc32c(framed.data(), crc_index);
255 for (int b = 0; b < 4; ++b) framed[crc_index + b] = static_cast<uint8_t>(crc >> (24 - 8 * b));
256 fill_padding(framed.data(), crc_index + 4, framed.size(), crc_index + 4);
257 std::vector<std::vector<uint8_t>> coded; size_t offset = 0;
258 for (const auto &b : blocks) {
259 std::vector<uint8_t> work(b.n, 0);
260 std::copy_n(framed.begin() + static_cast<std::ptrdiff_t>(offset), b.k, work.begin()); offset += b.k;
261 rs_encode_generic(work, b.k, b.n - b.k);
262 coded.push_back(std::move(work));
263 }
264 std::memset(out, 0, sizeof(*out));
265 out->abi_version = CKEY_ABI_VERSION; out->version = static_cast<uint32_t>(v); out->ecc_level = static_cast<uint32_t>(level);
266 out->layout = static_cast<uint32_t>(family);
267 out->payload_length = static_cast<uint32_t>(payload_len); out->codeword_length = static_cast<uint32_t>(L.codeword_bytes);
268 // Byte-interleave the blocks so local damage spreads across all of them.
269 size_t pos = 0;
270 for (size_t j = 0; j < blocks.front().n; ++j) for (size_t b = 0; b < blocks.size(); ++b) if (j < blocks[b].n) out->codeword[pos++] = coded[b][j];
271 char message[96]; std::snprintf(message, sizeof(message), "encoded Profile 0%s v%d level %c; %zu RS blocks", family ? ".1 (print)" : "", v, "LMQH"[level], blocks.size());
272 set_status(diag, CKEY_OK, message);
273 return CKEY_OK;
274}
275
276ckey_status ckey_decode_scalable(const uint8_t *codeword, size_t codeword_len, uint32_t version, ckey_scalable_layout layout,
277 uint8_t *out_payload, size_t out_capacity, size_t *out_payload_len, ckey_diagnostics *diag) {
278 const int family = static_cast<int>(layout);
279 if (!codeword || !out_payload_len || version < 1 || version > CKEY_SCALABLE_VERSION_COUNT || family < 0 || family >= kScalableFamilies) {
280 set_status(diag, CKEY_INVALID_ARGUMENT, "invalid Profile 0 codeword, version, or layout"); return CKEY_INVALID_ARGUMENT;
281 }
282 const ScalableLayout &L = scalable_layout(static_cast<int>(version), family);
283 if (codeword_len != L.codeword_bytes) { set_status(diag, CKEY_INVALID_ARGUMENT, "codeword length does not match the Profile 0 version"); return CKEY_INVALID_ARGUMENT; }
284 for (int level = 0; level < 4; ++level) {
285 const auto blocks = scalable_blocks(L.codeword_bytes, level);
286 std::vector<std::vector<uint8_t>> work(blocks.size());
287 for (size_t b = 0; b < blocks.size(); ++b) work[b].resize(blocks[b].n);
288 size_t pos = 0;
289 for (size_t j = 0; j < blocks.front().n; ++j) for (size_t b = 0; b < blocks.size(); ++b) if (j < blocks[b].n) work[b][j] = codeword[pos++];
290 std::vector<uint8_t> framed; uint32_t corrections = 0; bool ok = true;
291 for (size_t b = 0; b < blocks.size() && ok; ++b) {
292 uint32_t fixed = 0;
293 ok = rs_correct_generic(work[b], blocks[b].n - blocks[b].k, fixed);
294 corrections += fixed;
295 framed.insert(framed.end(), work[b].begin(), work[b].begin() + static_cast<std::ptrdiff_t>(blocks[b].k));
296 }
297 if (!ok || framed.size() < kScalableOverheadBytes) continue;
298 if (framed[0] != 0xc7 || framed[1] != 0x4b || framed[2] != L.format || framed[3] != 0 ||
299 framed[4] != version || framed[5] != level) continue;
300 const size_t length = (static_cast<size_t>(framed[6]) << 8) | framed[7];
301 if (kScalableOverheadBytes + length > framed.size()) continue;
302 const size_t crc_index = kScalableHeaderBytes + length;
303 uint32_t expected = 0; for (int b = 0; b < 4; ++b) expected = (expected << 8) | framed[crc_index + b];
304 if (crc32c(framed.data(), crc_index) != expected || !padding_matches(framed.data(), crc_index + 4, framed.size(), crc_index + 4)) continue;
305 if (out_capacity < length || (!out_payload && length)) { set_status(diag, CKEY_INSUFFICIENT_BUFFER, "Profile 0 payload exceeds output capacity"); return CKEY_INSUFFICIENT_BUFFER; }
306 if (length) std::memcpy(out_payload, framed.data() + kScalableHeaderBytes, length);
307 *out_payload_len = length;
308 char message[96]; std::snprintf(message, sizeof(message), "decoded Profile 0%s v%u level %c", family ? ".1 (print)" : "", version, "LMQH"[level]);
309 set_status(diag, CKEY_OK, message); if (diag) diag->corrected_symbols = corrections;
310 return CKEY_OK;
311 }
312 set_status(diag, CKEY_UNCORRECTABLE, "no Profile 0 level passed Reed-Solomon, frame, and CRC checks"); return CKEY_UNCORRECTABLE;
313}
314
315} // extern "C"