-- Copyright © 2026 Lingenic LLC. All rights reserved.
-- Licensed under the Lingenic Source-Available License v2.3.
-- Production use requires a separate license from Licensor.
-- See LICENSE.md and COPYRIGHT in the project root.
--
-------------------------------------------------------------------------------
-- Lingenic-Text
-- Formally Verified Unicode Text Processing Library
--
-- UTF-8 encoding and decoding per RFC 3629.
--
-- Postconditions reference the ghost spec in Lingenic_Text.UTF8_Spec.
-- The ghost spec encodes the RFC rules as expression functions; the prover
-- unfolds them and verifies the implementation matches.
-------------------------------------------------------------------------------
with Lingenic_Text.UTF8_Spec;
package Lingenic_Text.UTF8
with SPARK_Mode, Pure
is
---------------------------------------------------------------------------
-- Decode one UTF-8 sequence starting at Source (Pos).
--
-- Completeness: Valid = Well_Formed_At (bidirectional, not just =>)
-- Round-trip: Source bytes are the encoding of the decoded CP
-- Scalar: Decoded CP is always a scalar value (surrogates excluded)
---------------------------------------------------------------------------
procedure Decode
(Source : Byte_Array;
Pos : Positive;
CP : out Codepoint;
Length : out Positive;
Valid : out Boolean)
with Pre => Pos in Source'Range
and then Source'Last < Positive'Last,
Post => Length in 1 .. 4
and then Valid = UTF8_Spec.Well_Formed_At (Source, Pos)
and then
(if Valid then
Length = UTF8_Spec.Lead_Length (Source (Pos))
and then CP = UTF8_Spec.Decoded_At (Source, Pos)
and then Is_Scalar_Value (CP)
and then UTF8_Spec.Encoded_At (Source, Pos, CP)
else
CP = 0 and then Length = 1);
---------------------------------------------------------------------------
-- Encode one codepoint as a UTF-8 sequence into Target starting at Pos.
--
-- Round-trip: Encoded bytes are well-formed and decode back to CP
-- Frame: Bytes outside the written range are unchanged
---------------------------------------------------------------------------
procedure Encode
(CP : Codepoint;
Target : in out Byte_Array;
Pos : Positive;
Length : out Positive)
with Pre => Is_Scalar_Value (CP)
and then Pos in Target'Range
and then Target'Last < Positive'Last
and then Target'Last - Pos >= UTF8_Spec.Encoded_Length (CP) - 1,
Post => Length = UTF8_Spec.Encoded_Length (CP)
and then UTF8_Spec.Encoded_At (Target, Pos, CP)
and then UTF8_Spec.Well_Formed_At (Target, Pos)
and then UTF8_Spec.Decoded_At (Target, Pos) = CP
and then (for all I in Target'Range =>
(if I < Pos or else I - Pos >= Length then
Target (I) = Target'Old (I)));
end Lingenic_Text.UTF8;