// Content Compliance oracle: Unicode 17.0.0 default uppercase. // // This component *declares* conformance cases through the host bridge and // owns its own memory — no impl imports, no shared linear memory, no // failure_* exports. The host drives the implementation under test (a QIP // module, a CLI over stdin/stdout, or a host library), compares against the // declared expectation, and does all reporting. There are no case names in // the ABI: case identity is the declaration ordinal, and deterministic // replay means "case 987" is always the same bytes — a failure reference is // just (component hash, seed, ordinal). Human-readable context lives in the // generated fixtures source, not on the wire. // // Cases come from two sources: // 1. Curated inputs (generated by tools/casegen from UCD 17.0.0). // 2. Deterministic fuzz cases from a xorshift32 PRNG. The seed defaults to // 17 and hosts may vary it via the optional `uniform_set_seed` uniform — // same seed, same corpus, on any host. // Expected outputs are computed by the embedded UCD-derived oracle (full // uppercase mappings + invalid-UTF-8 passthrough; Unicode default uppercase // has no context rules), so this component is independent of any // implementation it checks. const tables = @import("unicode-17-uppercase-tables.zig"); const fixtures = @import("unicode-17-uppercase-fixtures.zig"); // The qip bridge. Every call carries the case ordinal from the component's // own counter, so both sides continuously verify they agree on "which case // is this?". Ordinals are u64: deterministic fuzz campaigns can run past // 2^32 cases. Two kinds of case: // - Requirements the host checks: must_render_exactly (exact bytes) and // must_trap (negative phase, for validator contracts; uppercase // has no rejecting inputs so it is unused here). // - must_render_into cases the component judges: must_render_into opens one case, // copies output into this component's memory, optional // must_render_into_emit_error calls attach diagnostics, and // must_render_into_finish closes it with the error count. extern "qip" fn must_render_exactly( ordinal: u64, input_ptr: u32, input_len: u32, expected_ptr: u32, expected_len: u32, ) i32; extern "qip" fn must_render_into( ordinal: u64, input_ptr: u32, input_len: u32, out_ptr: u32, out_cap: u32, ) i32; extern "qip" fn must_render_into_emit_error(ordinal: u64, message_ptr: u32, message_len: u32) i32; extern "qip" fn must_render_into_finish(ordinal: u64, error_count: u32) i32; const render_into_failed_message = "render failed or output did not fit"; const property_failed_message = "rendered output failed property check"; const FUZZ_CASES: u32 = 64; const IDEMPOTENCE_CASES: u32 = 12; const ASCII_CASES: u32 = 8; const MAX_INPUT: usize = 256; // Worst-case expansion is 3x code points (e.g. U+0390 -> U+0399 U+0308 U+0301). const MAX_EXPECTED: usize = MAX_INPUT * 3; var input_buf: [MAX_INPUT]u8 = undefined; var expected_buf: [MAX_EXPECTED]u8 = undefined; var out1_buf: [MAX_EXPECTED]u8 = undefined; var out2_buf: [MAX_EXPECTED]u8 = undefined; var seed: u32 = 17; var ordinal: u64 = 0; export fn uniform_set_seed(value: i32) void { seed = @bitCast(value); } const Decoded = struct { cp: u32, size: usize, valid: bool, }; // Strict UTF-8 decode with Go utf8.DecodeRune acceptance: rejects overlong // forms, surrogates, and values above U+10FFFF; an invalid sequence consumes // exactly one byte. fn decode(bytes: []const u8) Decoded { const b0 = bytes[0]; if (b0 < 0x80) return .{ .cp = b0, .size = 1, .valid = true }; if (b0 < 0xC2) return .{ .cp = 0, .size = 1, .valid = false }; if (b0 < 0xE0) { if (bytes.len < 2) return .{ .cp = 0, .size = 1, .valid = false }; const b1 = bytes[1]; if (b1 < 0x80 or b1 > 0xBF) return .{ .cp = 0, .size = 1, .valid = false }; return .{ .cp = (@as(u32, b0 & 0x1F) << 6) | @as(u32, b1 & 0x3F), .size = 2, .valid = true }; } if (b0 < 0xF0) { if (bytes.len < 3) return .{ .cp = 0, .size = 1, .valid = false }; const b1 = bytes[1]; const lo: u8 = if (b0 == 0xE0) 0xA0 else 0x80; const hi: u8 = if (b0 == 0xED) 0x9F else 0xBF; if (b1 < lo or b1 > hi) return .{ .cp = 0, .size = 1, .valid = false }; const b2 = bytes[2]; if (b2 < 0x80 or b2 > 0xBF) return .{ .cp = 0, .size = 1, .valid = false }; const cp = (@as(u32, b0 & 0x0F) << 12) | (@as(u32, b1 & 0x3F) << 6) | @as(u32, b2 & 0x3F); return .{ .cp = cp, .size = 3, .valid = true }; } if (b0 < 0xF5) { if (bytes.len < 4) return .{ .cp = 0, .size = 1, .valid = false }; const b1 = bytes[1]; const lo: u8 = if (b0 == 0xF0) 0x90 else 0x80; const hi: u8 = if (b0 == 0xF4) 0x8F else 0xBF; if (b1 < lo or b1 > hi) return .{ .cp = 0, .size = 1, .valid = false }; const b2 = bytes[2]; if (b2 < 0x80 or b2 > 0xBF) return .{ .cp = 0, .size = 1, .valid = false }; const b3 = bytes[3]; if (b3 < 0x80 or b3 > 0xBF) return .{ .cp = 0, .size = 1, .valid = false }; const cp = (@as(u32, b0 & 0x07) << 18) | (@as(u32, b1 & 0x3F) << 12) | (@as(u32, b2 & 0x3F) << 6) | @as(u32, b3 & 0x3F); return .{ .cp = cp, .size = 4, .valid = true }; } return .{ .cp = 0, .size = 1, .valid = false }; } fn encodeUtf8(cp: u32, out: []u8) usize { if (cp < 0x80) { out[0] = @intCast(cp); return 1; } if (cp < 0x800) { out[0] = @intCast(0xC0 | (cp >> 6)); out[1] = @intCast(0x80 | (cp & 0x3F)); return 2; } if (cp < 0x10000) { out[0] = @intCast(0xE0 | (cp >> 12)); out[1] = @intCast(0x80 | ((cp >> 6) & 0x3F)); out[2] = @intCast(0x80 | (cp & 0x3F)); return 3; } out[0] = @intCast(0xF0 | (cp >> 18)); out[1] = @intCast(0x80 | ((cp >> 12) & 0x3F)); out[2] = @intCast(0x80 | ((cp >> 6) & 0x3F)); out[3] = @intCast(0x80 | (cp & 0x3F)); return 4; } // Returns the mapped UTF-8 bytes, or null when cp uppercases to itself. fn lookupUpper(cp: u32) ?[]const u8 { var lo: usize = 0; var hi: usize = tables.map_keys.len; while (lo < hi) { const mid = lo + (hi - lo) / 2; if (tables.map_keys[mid] < cp) { lo = mid + 1; } else { hi = mid; } } if (lo >= tables.map_keys.len or tables.map_keys[lo] != cp) return null; const packed_val = tables.map_packed[lo]; const off = packed_val >> 8; const len = packed_val & 0xFF; return tables.map_blob[off .. off + len]; } // The embedded oracle covers the valid UTF-8 input domain. fn oracleUpper(input: []const u8, out: []u8) usize { var out_len: usize = 0; var i: usize = 0; while (i < input.len) { const d = decode(input[i..]); if (!d.valid) @trap(); if (lookupUpper(d.cp)) |upper| { for (upper) |b| { out[out_len] = b; out_len += 1; } } else { for (input[i .. i + d.size]) |b| { out[out_len] = b; out_len += 1; } } i += d.size; } return out_len; } fn declare(input: []const u8) i32 { const expected_len = oracleUpper(input, expected_buf[0..]); const status = must_render_exactly( ordinal, @intCast(@intFromPtr(input.ptr)), @intCast(input.len), @intCast(@intFromPtr(&expected_buf)), @intCast(expected_len), ); ordinal += 1; return status; } fn renderInto(input: []const u8, out: []u8) i32 { return must_render_into( ordinal, @intCast(@intFromPtr(input.ptr)), @intCast(input.len), @intCast(@intFromPtr(out.ptr)), @intCast(out.len), ); } fn renderIntoVerdict(ok: bool) void { if (ok) { _ = must_render_into_finish(ordinal, 0); } else { _ = must_render_into_emit_error(ordinal, @intCast(@intFromPtr(property_failed_message.ptr)), property_failed_message.len); _ = must_render_into_finish(ordinal, 1); } ordinal += 1; } fn nextRandom(state: *u32) u32 { var x = state.*; x ^= x << 13; x ^= x >> 17; x ^= x << 5; state.* = x; return x; } fn buildFuzzInput(rng: *u32) []const u8 { const target = nextRandom(rng) % 48; var len: usize = 0; var unit: usize = 0; while (unit < target and len + 4 <= MAX_INPUT) : (unit += 1) { switch (nextRandom(rng) % 4) { 0 => { input_buf[len] = @intCast(0x20 + (nextRandom(rng) % 0x5F)); len += 1; }, 1 => { input_buf[len] = @intCast(0x20 + (nextRandom(rng) % 0x5F)); len += 1; }, 2 => { const key = tables.map_keys[nextRandom(rng) % tables.map_keys.len]; len += encodeUtf8(key, input_buf[len..]); }, else => { var cp = nextRandom(rng) % 0x2000; if (cp >= 0xD800 and cp <= 0xDFFF) cp = 0x3B1; len += encodeUtf8(cp, input_buf[len..]); }, } } return input_buf[0..len]; } export fn comply() i32 { ordinal = 0; for (fixtures.cases) |case| { _ = declare(case.input); } var rng: u32 = if (seed == 0) 0x9E3779B9 else seed; var i: u32 = 0; while (i < FUZZ_CASES) : (i += 1) { const input = buildFuzzInput(&rng); _ = declare(input); } // Property: uppercase is idempotent — upper(upper(x)) == upper(x). // Inspects real impl output via render_output instead of comparing // against the oracle, so it holds against any conformant data version. i = 0; while (i < IDEMPOTENCE_CASES) : (i += 1) { const input = buildFuzzInput(&rng); const n1 = renderInto(input, out1_buf[0..]); if (n1 < 0) { _ = must_render_into_emit_error(ordinal, @intCast(@intFromPtr(render_into_failed_message.ptr)), render_into_failed_message.len); _ = must_render_into_finish(ordinal, 1); ordinal += 1; continue; } const len1: usize = @intCast(n1); _ = must_render_into_finish(ordinal, 0); ordinal += 1; _ = must_render_exactly(ordinal, @intCast(@intFromPtr(&out1_buf)), @intCast(len1), @intCast(@intFromPtr(&out1_buf)), @intCast(len1)); ordinal += 1; } // Property: ASCII input must produce ASCII output. i = 0; while (i < ASCII_CASES) : (i += 1) { const target = nextRandom(&rng) % 40; var len: usize = 0; while (len < target) : (len += 1) { input_buf[len] = @intCast(0x20 + (nextRandom(&rng) % 0x5F)); } const n = renderInto(input_buf[0..len], out1_buf[0..]); var ok = n >= 0; if (ok) { for (out1_buf[0..@intCast(n)]) |b| { if (b >= 0x80) { ok = false; break; } } } renderIntoVerdict(ok); } return @intCast(ordinal & 0x7FFFFFFF); }