///|
/// Splits data codewords into RS blocks, appends parity, and interleaves both.
///
/// 将数据码字拆分为 RS 块,追加校验字节,并交错排列。
fn interleave_blocks(
data : Array[Byte],
version : Int32,
ec : EcLevel,
) -> Array[Byte] {
let nblocks = block_count(version, ec)
let ecc_len = ecc_codewords(version, ec)
let raw_len = data.length()
let short_len = raw_len / nblocks
let num_short = nblocks - raw_len % nblocks
let blocks : Array[Array[Byte]] = []
let eccs : Array[Array[Byte]] = []
let mut offset = 0
for i in 0.. QrCode {
{
version_: Version(version),
ec_level_: ec,
mask_: mask,
size_: m.size,
modules_: m.modules.copy(),
}
}
///|
fn encode_at_version(
segments : Array[Segment],
version : Int32,
ec : EcLevel,
mask? : Mask,
) -> QrCode raise QrError {
let capacity_bytes = data_codewords(version, ec)
let buf = BitBuffer::new()
for seg in segments {
append_segment(buf, seg, version)
}
buf.pad_to(capacity_bytes)
let codewords = interleave_blocks(buf.to_bytes(), version, ec)
let base = build_matrix(version, codewords)
match mask {
Some(m) => make_qr(version, ec, m, apply_mask(base, m, ec))
None => {
let (best, best_mask) = choose_best_mask(base, ec)
make_qr(version, ec, best_mask, best)
}
}
}
///|
/// Encodes `text` as a QR Model 2 symbol.
///
/// 将 `text` 编码为 QR Model 2 符号。
///
/// `ec_level` defaults to M, version range to 1..40, mode is chosen
/// automatically, and the mask is selected by minimizing the Annex J penalty.
///
/// `ec_level` 默认为 M,版本范围为 1..40,模式自动选择,
/// 掩码通过最小化 Annex J 惩罚分选出。
///
/// # Example
/// ```mbt check
/// test "encode hello world" {
/// let qr = @core.encode("HELLO WORLD") // V1-M
/// assert_eq(qr.size(), 21)
/// assert_eq(qr.version().value(), 1)
/// }
/// ```
pub fn encode(
text : String,
ec_level~ : EcLevel = EcLevel::M,
min_version~ : Int32 = 1,
max_version~ : Int32 = 40,
mode? : Mode,
mask? : Mask,
) -> QrCode raise QrError {
if text.is_empty() {
raise QrError::EmptyInput
}
match mode {
Some(m) => {
let segments = split_segments(text, mode=m)
let version = choose_version(segments, ec_level, min_version, max_version)
encode_at_version(segments, version, ec_level, mask?)
}
None => {
let (version, segments) = choose_version_optimal(
text, ec_level, min_version, max_version,
)
encode_at_version(segments, version, ec_level, mask?)
}
}
}
///|
/// Encodes arbitrary bytes as one Byte segment. Unlike `encode`, no UTF-8
/// interpretation is applied, so binary payloads are preserved exactly.
///
/// 将任意字节编码为单个 Byte 段。与 `encode` 不同,这里不做 UTF-8 解释,
/// 因此二进制载荷会被精确保留。
///
/// # Example
/// ```mbt check
/// test "encode raw bytes" {
/// let qr = @core.encode_bytes(b"\xff\x00\x80")
/// assert_eq(qr.version().value(), 1)
/// }
/// ```
pub fn encode_bytes(
data : Bytes,
ec_level~ : EcLevel = EcLevel::M,
min_version~ : Int32 = 1,
max_version~ : Int32 = 40,
mask? : Mask,
) -> QrCode raise QrError {
if data.is_empty() {
raise QrError::EmptyInput
}
encode_segments(
[Segment::from_bytes(data)],
ec_level~,
min_version~,
max_version~,
mask?,
)
}
///|
/// Encodes an explicit sequence of segments, preserving the caller's mode and
/// ordering choices. Empty segments are dropped. Numeric and alphanumeric
/// segments are validated against their repertoires.
///
/// 编码显式给出的段序列,保留调用方的模式与顺序选择。空段会被丢弃;
/// 数字与字母数字段会按其字符集校验。
///
/// # Example
/// ```mbt check
/// test "encode custom segments" {
/// let segments = [
/// @core.Segment::new(@core.Mode::Numeric, "123"),
/// @core.Segment::new(@core.Mode::Byte, "abc"),
/// ]
/// let qr = @core.encode_segments(segments)
/// assert_eq(qr.version().value(), 1)
/// }
/// ```
pub fn encode_segments(
segments : Array[Segment],
ec_level~ : EcLevel = EcLevel::M,
min_version~ : Int32 = 1,
max_version~ : Int32 = 40,
mask? : Mask,
) -> QrCode raise QrError {
let normalized : Array[Segment] = []
for seg in segments {
if seg.char_count() > 0 {
match seg.mode_ {
Numeric => validate(seg.data_, Numeric)
Alphanumeric => validate(seg.data_, Alphanumeric)
Byte => ()
}
normalized.push(seg)
}
}
if normalized.length() == 0 {
raise QrError::EmptyInput
}
let version = choose_version(normalized, ec_level, min_version, max_version)
encode_at_version(normalized, version, ec_level, mask?)
}