Skip to main content

qrcode_generator/encode/
mod.rs

1#[cfg(feature = "qr")]
2use alloc::collections::BTreeMap;
3#[cfg(feature = "qr")]
4use alloc::vec;
5use alloc::{string::String, vec::Vec};
6#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
7use core::ops::RangeInclusive;
8
9mod bits;
10#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
11mod reed_solomon;
12
13#[cfg(feature = "micro-qr")]
14mod micro;
15#[cfg(feature = "qr")]
16mod model2;
17#[cfg(any(feature = "qr", feature = "rmqr"))]
18mod optimizer;
19#[cfg(feature = "rmqr")]
20mod rmqr;
21
22use bits::BitBuffer;
23
24use crate::EncodeError;
25
26#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
27#[inline]
28const fn numeric_character_capacity(capacity_bits: usize, overhead_bits: usize) -> usize {
29    let payload_bits = capacity_bits.saturating_sub(overhead_bits);
30
31    payload_bits / 10 * 3
32        + match payload_bits % 10 {
33            4..=6 => 1,
34            7..=9 => 2,
35            _ => 0,
36        }
37}
38
39#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
40#[inline]
41const fn ensure_input_length(
42    length: usize,
43    per_symbol_capacity: usize,
44    capacity_bits: usize,
45    symbol_count: usize,
46) -> Result<(), EncodeError> {
47    if length > per_symbol_capacity.saturating_mul(symbol_count) {
48        Err(EncodeError::DataTooLong {
49            required_bits: None,
50            capacity_bits: capacity_bits.saturating_mul(symbol_count),
51        })
52    } else {
53        Ok(())
54    }
55}
56
57/// The error correction level written to an encoded symbol.
58///
59/// The available variants depend on the enabled symbol family features.
60#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
61#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
62#[non_exhaustive]
63pub enum SymbolErrorCorrection {
64    /// Detects errors in a Micro QR M1 symbol without correcting them.
65    #[cfg(feature = "micro-qr")]
66    DetectionOnly,
67    /// Uses the Low error correction level.
68    Low,
69    /// Uses the Medium error correction level.
70    Medium,
71    /// Uses the Quartile error correction level.
72    Quartile,
73    /// Uses the High error correction level.
74    #[cfg(any(feature = "qr", feature = "rmqr"))]
75    High,
76}
77
78/// An error correction level for a Model 2 QR Code.
79#[cfg(feature = "qr")]
80#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
81pub enum QrErrorCorrection {
82    /// Uses the Low error correction level.
83    Low,
84    /// Uses the Medium error correction level.
85    Medium,
86    /// Uses the Quartile error correction level.
87    Quartile,
88    /// Uses the High error correction level.
89    High,
90}
91
92#[cfg(feature = "qr")]
93impl QrErrorCorrection {
94    #[inline]
95    pub(crate) const fn qr_ordinal(self) -> usize {
96        match self {
97            Self::Low => 0,
98            Self::Medium => 1,
99            Self::Quartile => 2,
100            Self::High => 3,
101        }
102    }
103
104    #[inline]
105    pub(crate) const fn qr_format_bits(self) -> u8 {
106        match self {
107            Self::Low => 1,
108            Self::Medium => 0,
109            Self::Quartile => 3,
110            Self::High => 2,
111        }
112    }
113}
114
115#[cfg(feature = "qr")]
116impl From<QrErrorCorrection> for SymbolErrorCorrection {
117    #[inline]
118    fn from(value: QrErrorCorrection) -> Self {
119        match value {
120            QrErrorCorrection::Low => Self::Low,
121            QrErrorCorrection::Medium => Self::Medium,
122            QrErrorCorrection::Quartile => Self::Quartile,
123            QrErrorCorrection::High => Self::High,
124        }
125    }
126}
127
128/// An error correction level for a Micro QR Code.
129#[cfg(feature = "micro-qr")]
130#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
131pub enum MicroErrorCorrection {
132    /// Detects errors in an M1 symbol without correcting them.
133    DetectionOnly,
134    /// Uses the Low error correction level.
135    Low,
136    /// Uses the Medium error correction level.
137    Medium,
138    /// Uses the Quartile error correction level.
139    Quartile,
140}
141
142#[cfg(feature = "micro-qr")]
143impl From<MicroErrorCorrection> for SymbolErrorCorrection {
144    #[inline]
145    fn from(value: MicroErrorCorrection) -> Self {
146        match value {
147            MicroErrorCorrection::DetectionOnly => Self::DetectionOnly,
148            MicroErrorCorrection::Low => Self::Low,
149            MicroErrorCorrection::Medium => Self::Medium,
150            MicroErrorCorrection::Quartile => Self::Quartile,
151        }
152    }
153}
154
155/// An error correction level for a Rectangular Micro QR Code.
156#[cfg(feature = "rmqr")]
157#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
158pub enum RmqrErrorCorrection {
159    /// Uses the Medium error correction level.
160    Medium,
161    /// Uses the High error correction level.
162    High,
163}
164
165#[cfg(feature = "rmqr")]
166impl From<RmqrErrorCorrection> for SymbolErrorCorrection {
167    #[inline]
168    fn from(value: RmqrErrorCorrection) -> Self {
169        match value {
170            RmqrErrorCorrection::Medium => Self::Medium,
171            RmqrErrorCorrection::High => Self::High,
172        }
173    }
174}
175
176/// A validated Model 2 QR Code version.
177#[cfg(feature = "qr")]
178#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
179pub struct QrVersion(u8);
180
181#[cfg(feature = "qr")]
182impl QrVersion {
183    /// The largest Model 2 QR Code version.
184    pub const MAX: Self = Self(40);
185    /// The smallest Model 2 QR Code version.
186    pub const MIN: Self = Self(1);
187
188    /// Creates a Model 2 version from a value in `1..=40`.
189    #[inline]
190    pub const fn new(value: u8) -> Result<Self, EncodeError> {
191        if value >= 1 && value <= 40 {
192            Ok(Self(value))
193        } else {
194            Err(EncodeError::InvalidVersionRange)
195        }
196    }
197
198    /// Returns the numeric version value.
199    #[inline]
200    pub const fn value(self) -> u8 {
201        self.0
202    }
203}
204
205/// A Micro QR Code version.
206#[cfg(feature = "micro-qr")]
207#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
208pub enum MicroVersion {
209    /// An 11 by 11 Micro QR Code symbol.
210    M1,
211    /// A 13 by 13 Micro QR Code symbol.
212    M2,
213    /// A 15 by 15 Micro QR Code symbol.
214    M3,
215    /// A 17 by 17 Micro QR Code symbol.
216    M4,
217}
218
219#[cfg(feature = "micro-qr")]
220impl MicroVersion {
221    /// Returns the number of modules on each side.
222    #[inline]
223    pub const fn size(self) -> usize {
224        match self {
225            Self::M1 => 11,
226            Self::M2 => 13,
227            Self::M3 => 15,
228            Self::M4 => 17,
229        }
230    }
231}
232
233/// A Rectangular Micro QR Code version.
234#[cfg(feature = "rmqr")]
235#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
236#[repr(u8)]
237pub enum RmqrVersion {
238    /// A 7 by 43 rMQR symbol.
239    R7x43,
240    /// A 7 by 59 rMQR symbol.
241    R7x59,
242    /// A 7 by 77 rMQR symbol.
243    R7x77,
244    /// A 7 by 99 rMQR symbol.
245    R7x99,
246    /// A 7 by 139 rMQR symbol.
247    R7x139,
248    /// A 9 by 43 rMQR symbol.
249    R9x43,
250    /// A 9 by 59 rMQR symbol.
251    R9x59,
252    /// A 9 by 77 rMQR symbol.
253    R9x77,
254    /// A 9 by 99 rMQR symbol.
255    R9x99,
256    /// A 9 by 139 rMQR symbol.
257    R9x139,
258    /// An 11 by 27 rMQR symbol.
259    R11x27,
260    /// An 11 by 43 rMQR symbol.
261    R11x43,
262    /// An 11 by 59 rMQR symbol.
263    R11x59,
264    /// An 11 by 77 rMQR symbol.
265    R11x77,
266    /// An 11 by 99 rMQR symbol.
267    R11x99,
268    /// An 11 by 139 rMQR symbol.
269    R11x139,
270    /// A 13 by 27 rMQR symbol.
271    R13x27,
272    /// A 13 by 43 rMQR symbol.
273    R13x43,
274    /// A 13 by 59 rMQR symbol.
275    R13x59,
276    /// A 13 by 77 rMQR symbol.
277    R13x77,
278    /// A 13 by 99 rMQR symbol.
279    R13x99,
280    /// A 13 by 139 rMQR symbol.
281    R13x139,
282    /// A 15 by 43 rMQR symbol.
283    R15x43,
284    /// A 15 by 59 rMQR symbol.
285    R15x59,
286    /// A 15 by 77 rMQR symbol.
287    R15x77,
288    /// A 15 by 99 rMQR symbol.
289    R15x99,
290    /// A 15 by 139 rMQR symbol.
291    R15x139,
292    /// A 17 by 43 rMQR symbol.
293    R17x43,
294    /// A 17 by 59 rMQR symbol.
295    R17x59,
296    /// A 17 by 77 rMQR symbol.
297    R17x77,
298    /// A 17 by 99 rMQR symbol.
299    R17x99,
300    /// A 17 by 139 rMQR symbol.
301    R17x139,
302}
303
304#[cfg(feature = "rmqr")]
305impl RmqrVersion {
306    pub(crate) const ALL: [Self; 32] = [
307        Self::R7x43,
308        Self::R7x59,
309        Self::R7x77,
310        Self::R7x99,
311        Self::R7x139,
312        Self::R9x43,
313        Self::R9x59,
314        Self::R9x77,
315        Self::R9x99,
316        Self::R9x139,
317        Self::R11x27,
318        Self::R11x43,
319        Self::R11x59,
320        Self::R11x77,
321        Self::R11x99,
322        Self::R11x139,
323        Self::R13x27,
324        Self::R13x43,
325        Self::R13x59,
326        Self::R13x77,
327        Self::R13x99,
328        Self::R13x139,
329        Self::R15x43,
330        Self::R15x59,
331        Self::R15x77,
332        Self::R15x99,
333        Self::R15x139,
334        Self::R17x43,
335        Self::R17x59,
336        Self::R17x77,
337        Self::R17x99,
338        Self::R17x139,
339    ];
340    // Versions ordered by area, then height, then width; sorted once at compile time for candidate search.
341    pub(crate) const ALL_BY_AREA: [Self; 32] = Self::sorted_by_area();
342    /// The last rMQR version in format indicator order.
343    pub const MAX: Self = Self::R17x139;
344    /// The first rMQR version in format indicator order.
345    pub const MIN: Self = Self::R7x43;
346
347    const fn sorted_by_area() -> [Self; 32] {
348        let mut versions = Self::ALL;
349        let length = versions.len();
350        let mut index = 0;
351
352        // A selection sort is enough for a const array of thirty-two versions.
353        while index < length {
354            let mut smallest = index;
355            let mut candidate = index + 1;
356
357            while candidate < length {
358                if versions[candidate].area_key() < versions[smallest].area_key() {
359                    smallest = candidate;
360                }
361
362                candidate += 1;
363            }
364
365            let swap = versions[index];
366            versions[index] = versions[smallest];
367            versions[smallest] = swap;
368            index += 1;
369        }
370
371        versions
372    }
373
374    // Packs area, height and width into one number that orders the same way as the (area, height, width) tuple.
375    const fn area_key(self) -> u64 {
376        let width = self.width() as u64;
377        let height = self.height() as u64;
378
379        (width * height) * 1_000_000 + height * 1_000 + width
380    }
381
382    /// Returns the five-bit version indicator value.
383    #[inline]
384    pub const fn value(self) -> u8 {
385        self as u8
386    }
387
388    /// Returns the symbol width in modules.
389    #[inline]
390    pub const fn width(self) -> usize {
391        match self {
392            Self::R11x27 | Self::R13x27 => 27,
393            Self::R7x43
394            | Self::R9x43
395            | Self::R11x43
396            | Self::R13x43
397            | Self::R15x43
398            | Self::R17x43 => 43,
399            Self::R7x59
400            | Self::R9x59
401            | Self::R11x59
402            | Self::R13x59
403            | Self::R15x59
404            | Self::R17x59 => 59,
405            Self::R7x77
406            | Self::R9x77
407            | Self::R11x77
408            | Self::R13x77
409            | Self::R15x77
410            | Self::R17x77 => 77,
411            Self::R7x99
412            | Self::R9x99
413            | Self::R11x99
414            | Self::R13x99
415            | Self::R15x99
416            | Self::R17x99 => 99,
417            Self::R7x139
418            | Self::R9x139
419            | Self::R11x139
420            | Self::R13x139
421            | Self::R15x139
422            | Self::R17x139 => 139,
423        }
424    }
425
426    /// Returns the symbol height in modules.
427    #[inline]
428    pub const fn height(self) -> usize {
429        match self {
430            Self::R7x43 | Self::R7x59 | Self::R7x77 | Self::R7x99 | Self::R7x139 => 7,
431            Self::R9x43 | Self::R9x59 | Self::R9x77 | Self::R9x99 | Self::R9x139 => 9,
432            Self::R11x27
433            | Self::R11x43
434            | Self::R11x59
435            | Self::R11x77
436            | Self::R11x99
437            | Self::R11x139 => 11,
438            Self::R13x27
439            | Self::R13x43
440            | Self::R13x59
441            | Self::R13x77
442            | Self::R13x99
443            | Self::R13x139 => 13,
444            Self::R15x43 | Self::R15x59 | Self::R15x77 | Self::R15x99 | Self::R15x139 => 15,
445            Self::R17x43 | Self::R17x59 | Self::R17x77 | Self::R17x99 | Self::R17x139 => 17,
446        }
447    }
448}
449
450/// The version of an encoded symbol.
451///
452/// The available variants depend on the enabled symbol family features.
453#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
454#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
455#[non_exhaustive]
456pub enum SymbolVersion {
457    /// A Model 2 QR Code version.
458    #[cfg(feature = "qr")]
459    Qr(QrVersion),
460    /// A Micro QR Code version.
461    #[cfg(feature = "micro-qr")]
462    Micro(MicroVersion),
463    /// A Rectangular Micro QR Code version.
464    #[cfg(feature = "rmqr")]
465    Rmqr(RmqrVersion),
466}
467
468/// A validated Model 2 mask number.
469#[cfg(feature = "qr")]
470#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
471pub struct QrMask(u8);
472
473#[cfg(feature = "qr")]
474impl QrMask {
475    /// Creates a Model 2 mask from a value in `0..=7`.
476    #[inline]
477    pub const fn new(value: u8) -> Result<Self, EncodeError> {
478        if value < 8 { Ok(Self(value)) } else { Err(EncodeError::InvalidMask) }
479    }
480
481    /// Returns the mask value.
482    #[inline]
483    pub const fn value(self) -> u8 {
484        self.0
485    }
486}
487
488/// A validated Micro QR Code mask number.
489#[cfg(feature = "micro-qr")]
490#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
491pub struct MicroMask(u8);
492
493#[cfg(feature = "micro-qr")]
494impl MicroMask {
495    /// Creates a Micro QR Code mask from a value in `0..=3`.
496    #[inline]
497    pub const fn new(value: u8) -> Result<Self, EncodeError> {
498        if value < 4 { Ok(Self(value)) } else { Err(EncodeError::InvalidMask) }
499    }
500
501    /// Returns the mask value.
502    #[inline]
503    pub const fn value(self) -> u8 {
504        self.0
505    }
506}
507
508/// An Extended Channel Interpretation assignment number.
509#[cfg(any(feature = "qr", feature = "rmqr"))]
510#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
511pub struct EciAssignment(u32);
512
513#[cfg(any(feature = "qr", feature = "rmqr"))]
514impl EciAssignment {
515    /// The default ISO-8859-1 character set assignment.
516    pub const ISO_8859_1: Self = Self(3);
517    /// The Shift JIS character set assignment.
518    pub const SHIFT_JIS: Self = Self(20);
519    /// The UTF-8 character set assignment.
520    pub const UTF_8: Self = Self(26);
521
522    /// Creates an ECI assignment from a value in `0..=999999`.
523    #[inline]
524    pub const fn new(value: u32) -> Result<Self, EncodeError> {
525        if value <= 999_999 {
526            Ok(Self(value))
527        } else {
528            Err(EncodeError::InvalidEciAssignment(value))
529        }
530    }
531
532    /// Returns the ECI assignment number.
533    #[inline]
534    pub const fn value(self) -> u32 {
535        self.0
536    }
537}
538
539/// A validated FNC1 second-position application indicator.
540#[cfg(any(feature = "qr", feature = "rmqr"))]
541#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
542pub struct ApplicationIndicator(u8);
543
544#[cfg(any(feature = "qr", feature = "rmqr"))]
545impl ApplicationIndicator {
546    /// Creates a numeric application indicator from a value in `0..=99`.
547    #[inline]
548    pub const fn numeric(value: u8) -> Result<Self, EncodeError> {
549        if value <= 99 { Ok(Self(value)) } else { Err(EncodeError::InvalidApplicationIndicator) }
550    }
551
552    /// Creates an application indicator from one ASCII letter.
553    #[inline]
554    pub const fn letter(value: char) -> Result<Self, EncodeError> {
555        if value.is_ascii_alphabetic() {
556            Ok(Self(value as u8 + 100))
557        } else {
558            Err(EncodeError::InvalidApplicationIndicator)
559        }
560    }
561
562    /// Returns the encoded application indicator byte.
563    #[inline]
564    pub const fn value(self) -> u8 {
565        self.0
566    }
567}
568
569/// The FNC1 header applied to a Model 2 or rMQR symbol.
570#[cfg(any(feature = "qr", feature = "rmqr"))]
571#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
572pub enum Fnc1 {
573    /// Marks the symbol as GS1 data using FNC1 in first position.
574    Gs1,
575    /// Marks the symbol as industry data using FNC1 in second position.
576    Industry(ApplicationIndicator),
577}
578
579/// Metadata carried by a Structured Append symbol.
580#[cfg(feature = "qr")]
581#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
582pub struct StructuredAppendInfo {
583    index:  u8,
584    total:  u8,
585    parity: u8,
586}
587
588#[cfg(feature = "qr")]
589impl StructuredAppendInfo {
590    /// Returns the zero-based symbol index.
591    #[inline]
592    pub const fn index(self) -> u8 {
593        self.index
594    }
595
596    /// Returns the total number of symbols.
597    #[inline]
598    pub const fn total(self) -> u8 {
599        self.total
600    }
601
602    /// Returns the parity byte shared by the symbol sequence.
603    #[inline]
604    pub const fn parity(self) -> u8 {
605        self.parity
606    }
607}
608
609#[derive(Clone, Copy, Debug, Eq, PartialEq)]
610pub(crate) enum Mode {
611    Numeric,
612    Alphanumeric,
613    Byte,
614    #[cfg_attr(not(feature = "kanji"), allow(dead_code))]
615    Kanji,
616    #[cfg_attr(not(any(feature = "qr", feature = "rmqr")), allow(dead_code))]
617    Eci,
618}
619
620impl Mode {
621    #[cfg(feature = "qr")]
622    #[inline]
623    const fn qr_bits(self) -> u8 {
624        match self {
625            Self::Numeric => 0b0001,
626            Self::Alphanumeric => 0b0010,
627            Self::Byte => 0b0100,
628            Self::Kanji => 0b1000,
629            Self::Eci => 0b0111,
630        }
631    }
632
633    #[cfg(feature = "qr")]
634    #[inline]
635    const fn cci_bits(self, version: QrVersion) -> u8 {
636        let group = version_group(version);
637
638        match self {
639            Self::Numeric => [10, 12, 14][group],
640            Self::Alphanumeric => [9, 11, 13][group],
641            Self::Byte => [8, 16, 16][group],
642            Self::Kanji => [8, 10, 12][group],
643            Self::Eci => 0,
644        }
645    }
646}
647
648/// An explicitly constructed QR Code data or control segment.
649#[derive(Clone, Debug, Eq, PartialEq)]
650pub struct Segment {
651    mode:            Mode,
652    character_count: usize,
653    bits:            BitBuffer,
654    source:          Vec<u8>,
655}
656
657impl Segment {
658    /// Creates a Numeric segment from ASCII digits.
659    pub fn numeric(data: impl AsRef<str>) -> Result<Self, EncodeError> {
660        let data = data.as_ref();
661        let mut bits = BitBuffer::with_capacity(data.len() * 10 / 3 + 4);
662        let bytes = data.as_bytes();
663
664        for (offset, byte) in bytes.iter().enumerate() {
665            if !byte.is_ascii_digit() {
666                return Err(EncodeError::InvalidData {
667                    mode:        "numeric",
668                    byte_offset: offset,
669                });
670            }
671        }
672
673        for chunk in bytes.chunks(3) {
674            let value = chunk.iter().fold(0, |value, byte| value * 10 + u32::from(byte - b'0'));
675            bits.append(value, [0, 4, 7, 10][chunk.len()]);
676        }
677
678        Ok(Self {
679            mode: Mode::Numeric,
680            character_count: bytes.len(),
681            bits,
682            source: bytes.to_vec(),
683        })
684    }
685
686    /// Creates an Alphanumeric segment from the standard 45-character set.
687    pub fn alphanumeric(data: impl AsRef<str>) -> Result<Self, EncodeError> {
688        let data = data.as_ref();
689        let mut values = Vec::with_capacity(data.len());
690
691        for (offset, byte) in data.bytes().enumerate() {
692            let Some(value) = alphanumeric_value(byte) else {
693                return Err(EncodeError::InvalidData {
694                    mode:        "alphanumeric",
695                    byte_offset: offset,
696                });
697            };
698            values.push(value);
699        }
700
701        let mut bits = BitBuffer::with_capacity(values.len() * 11 / 2 + 6);
702
703        for chunk in values.chunks(2) {
704            if chunk.len() == 2 {
705                bits.append(u32::from(chunk[0]) * 45 + u32::from(chunk[1]), 11);
706            } else {
707                bits.append(u32::from(chunk[0]), 6);
708            }
709        }
710
711        Ok(Self {
712            mode: Mode::Alphanumeric,
713            character_count: values.len(),
714            bits,
715            source: data.as_bytes().to_vec(),
716        })
717    }
718
719    #[cfg(any(feature = "qr", feature = "rmqr"))]
720    fn fnc1_alphanumeric(data: &[u8]) -> Result<Self, EncodeError> {
721        let mut values = Vec::with_capacity(data.len());
722
723        for (offset, &byte) in data.iter().enumerate() {
724            // A group separator becomes percent and a literal percent is escaped by doubling it.
725            if byte == 0x1D {
726                values.push(alphanumeric_value(b'%').expect("percent is alphanumeric"));
727            } else if byte == b'%' {
728                let value = alphanumeric_value(byte).expect("percent is alphanumeric");
729                values.extend_from_slice(&[value, value]);
730            } else if let Some(value) = alphanumeric_value(byte) {
731                values.push(value);
732            } else {
733                return Err(EncodeError::InvalidData {
734                    mode:        "FNC1 alphanumeric",
735                    byte_offset: offset,
736                });
737            }
738        }
739
740        let mut bits = BitBuffer::with_capacity(values.len() * 11 / 2 + 6);
741
742        for chunk in values.chunks(2) {
743            if chunk.len() == 2 {
744                bits.append(u32::from(chunk[0]) * 45 + u32::from(chunk[1]), 11);
745            } else {
746                bits.append(u32::from(chunk[0]), 6);
747            }
748        }
749
750        Ok(Self {
751            mode: Mode::Alphanumeric,
752            character_count: values.len(),
753            bits,
754            source: data.to_vec(),
755        })
756    }
757
758    /// Creates a Byte segment without adding an ECI header.
759    pub fn bytes(data: impl AsRef<[u8]>) -> Self {
760        let data = data.as_ref();
761
762        // A Byte segment stores its payload once in `bits`, so `source` stays empty and is read back from there.
763        Self {
764            mode:            Mode::Byte,
765            character_count: data.len(),
766            bits:            BitBuffer::from_bytes(data.to_vec()),
767            source:          Vec::new(),
768        }
769    }
770
771    // Returns the original bytes a segment was built from, reading a Byte segment back from its bit buffer.
772    #[cfg(any(feature = "qr", feature = "rmqr"))]
773    fn source_bytes(&self) -> &[u8] {
774        match self.mode {
775            Mode::Byte => self.bits.as_bytes(),
776            _ => &self.source,
777        }
778    }
779
780    /// Creates an ECI control segment.
781    #[cfg(any(feature = "qr", feature = "rmqr"))]
782    pub fn eci(assignment: EciAssignment) -> Self {
783        let mut bits = BitBuffer::with_capacity(24);
784        let value = assignment.0;
785
786        // Prefix bits select the shortest one, two or three-codeword ECI representation.
787        if value < 128 {
788            bits.append(value, 8);
789        } else if value < 16_384 {
790            bits.append(0b10, 2);
791            bits.append(value, 14);
792        } else {
793            bits.append(0b110, 3);
794            bits.append(value, 21);
795        }
796
797        Self {
798            mode: Mode::Eci,
799            character_count: 0,
800            bits,
801            source: Vec::new(),
802        }
803    }
804
805    #[cfg(feature = "kanji")]
806    /// Creates a Kanji segment from characters in the supported Shift JIS ranges.
807    pub fn kanji(data: impl AsRef<str>) -> Result<Self, EncodeError> {
808        let data = data.as_ref();
809        let mut bits = BitBuffer::with_capacity(data.chars().count() * 13);
810        let mut source = Vec::with_capacity(data.len() * 2);
811        let mut count = 0;
812
813        for (offset, character) in data.char_indices() {
814            let Some((value, encoded)) = kanji_encoding(character) else {
815                return Err(EncodeError::InvalidData {
816                    mode: "Kanji", byte_offset: offset
817                });
818            };
819
820            bits.append(u32::from(value), 13);
821            source.extend_from_slice(&encoded);
822            count += 1;
823        }
824        Ok(Self {
825            mode: Mode::Kanji,
826            character_count: count,
827            bits,
828            source,
829        })
830    }
831
832    /// Returns the character count stored in this segment.
833    #[inline]
834    pub const fn character_count(&self) -> usize {
835        self.character_count
836    }
837}
838
839#[cfg(feature = "kanji")]
840fn kanji_encoding(character: char) -> Option<(u16, [u8; 2])> {
841    let mut utf8 = [0; 4];
842    let text = character.encode_utf8(&mut utf8);
843    let (encoded, _, had_errors) = encoding_rs::SHIFT_JIS.encode(text);
844
845    if had_errors || encoded.len() != 2 {
846        return None;
847    }
848
849    let bytes = [encoded[0], encoded[1]];
850    let value = u16::from_be_bytes(bytes);
851
852    // The two valid Shift JIS ranges are shifted into one compact 13-bit index space.
853    let adjusted = match value {
854        0x8140..=0x9FFC => value - 0x8140,
855        0xE040..=0xEBBF => value - 0xC140,
856        _ => return None,
857    };
858
859    Some(((adjusted >> 8) * 0xC0 + (adjusted & 0xFF), bytes))
860}
861
862// Orders the modes used for deterministic tie-breaking in the segment optimizers.
863#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
864#[inline]
865const fn mode_rank(mode: Mode) -> u8 {
866    match mode {
867        Mode::Numeric => 0,
868        Mode::Alphanumeric => 1,
869        Mode::Kanji => 2,
870        Mode::Byte => 3,
871        Mode::Eci => 4,
872    }
873}
874
875#[inline]
876pub(crate) const fn alphanumeric_value(byte: u8) -> Option<u8> {
877    match byte {
878        b'0'..=b'9' => Some(byte - b'0'),
879        b'A'..=b'Z' => Some(byte - b'A' + 10),
880        b' ' => Some(36),
881        b'$' => Some(37),
882        b'%' => Some(38),
883        b'*' => Some(39),
884        b'+' => Some(40),
885        b'-' => Some(41),
886        b'.' => Some(42),
887        b'/' => Some(43),
888        b':' => Some(44),
889        _ => None,
890    }
891}
892
893// Computes the BCH remainder of the data polynomial times x^degree, reduced by the generator.
894// The generator is given without its leading term and degree is the number of parity bits.
895#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
896#[inline]
897pub(crate) const fn bch_remainder(data: u32, generator: u32, degree: u32) -> u32 {
898    let mut remainder = data;
899    let mut round = 0;
900
901    while round < degree {
902        remainder = (remainder << 1) ^ ((remainder >> (degree - 1)) * generator);
903        round += 1;
904    }
905
906    remainder
907}
908
909/// An immutable encoded QR Code, Micro QR Code or rMQR symbol.
910#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
911#[derive(Clone, Debug, Eq, PartialEq)]
912pub struct Symbol {
913    pub(crate) version:           SymbolVersion,
914    pub(crate) error_correction:  SymbolErrorCorrection,
915    pub(crate) mask:              u8,
916    pub(crate) modules:           Vec<bool>,
917    #[cfg(feature = "qr")]
918    pub(crate) structured_append: Option<StructuredAppendInfo>,
919}
920
921#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
922impl Symbol {
923    /// Returns the symbol width in modules.
924    ///
925    /// The square QR Code and Micro QR Code families use this as the height as well, but Rectangular Micro QR Code symbols are not square, so read [`width`](Self::width) and [`height`](Self::height) separately for them.
926    #[inline]
927    pub const fn size(&self) -> usize {
928        self.width()
929    }
930
931    /// Returns the symbol width in modules.
932    #[inline]
933    pub const fn width(&self) -> usize {
934        match self.version {
935            #[cfg(feature = "qr")]
936            SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
937            #[cfg(feature = "micro-qr")]
938            SymbolVersion::Micro(version) => version.size(),
939            #[cfg(feature = "rmqr")]
940            SymbolVersion::Rmqr(version) => version.width(),
941        }
942    }
943
944    /// Returns the symbol height in modules.
945    #[inline]
946    pub const fn height(&self) -> usize {
947        match self.version {
948            #[cfg(feature = "qr")]
949            SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
950            #[cfg(feature = "micro-qr")]
951            SymbolVersion::Micro(version) => version.size(),
952            #[cfg(feature = "rmqr")]
953            SymbolVersion::Rmqr(version) => version.height(),
954        }
955    }
956
957    /// Returns the encoded symbol version.
958    #[inline]
959    pub const fn version(&self) -> SymbolVersion {
960        self.version
961    }
962
963    /// Returns the error correction level written to the symbol.
964    #[inline]
965    pub const fn error_correction(&self) -> SymbolErrorCorrection {
966        self.error_correction
967    }
968
969    /// Returns the selected mask number.
970    #[inline]
971    pub const fn mask(&self) -> u8 {
972        self.mask
973    }
974
975    /// Returns Structured Append metadata when this symbol belongs to a sequence.
976    #[cfg(feature = "qr")]
977    #[inline]
978    pub const fn structured_append(&self) -> Option<StructuredAppendInfo> {
979        self.structured_append
980    }
981
982    /// Returns a module or `None` when the coordinates are outside the symbol.
983    #[inline]
984    pub fn module(&self, x: usize, y: usize) -> Option<bool> {
985        let width = self.width();
986        (x < width && y < self.height()).then(|| self.modules[y * width + x])
987    }
988
989    /// Returns every module as one row-major slice without copying.
990    ///
991    /// A dark module is `true`, and the module at `(x, y)` is at index `y * width + x`.
992    #[inline]
993    pub fn modules(&self) -> &[bool] {
994        &self.modules
995    }
996
997    /// Copies the symbol into a row-major Boolean matrix.
998    pub fn to_matrix(&self) -> Vec<Vec<bool>> {
999        self.modules.chunks(self.width()).map(<[bool]>::to_vec).collect()
1000    }
1001}
1002
1003/// Converts a value to text for QR Code encoding.
1004///
1005/// Implementations can use a shorter equivalent spelling when the value has more than one text representation.
1006/// The optional `url` feature implements this trait for `url::Url`.
1007pub trait ToQRText {
1008    /// Returns the text to encode.
1009    fn to_qr_text(&self) -> String;
1010}
1011
1012/// Configures and encodes Model 2 QR Code symbols.
1013#[cfg(feature = "qr")]
1014#[derive(Clone, Debug)]
1015pub struct QrEncoder {
1016    error_correction:       QrErrorCorrection,
1017    versions:               RangeInclusive<QrVersion>,
1018    mask:                   Option<QrMask>,
1019    boost_error_correction: bool,
1020    fnc1:                   Option<Fnc1>,
1021}
1022
1023#[cfg(feature = "qr")]
1024impl QrEncoder {
1025    /// Creates an encoder with automatic mask selection and Model 2 versions 1 through 40.
1026    #[inline]
1027    pub const fn new(error_correction: QrErrorCorrection) -> Self {
1028        Self {
1029            error_correction,
1030            versions: QrVersion::MIN..=QrVersion::MAX,
1031            mask: None,
1032            boost_error_correction: true,
1033            fnc1: None,
1034        }
1035    }
1036
1037    /// Selects one exact Model 2 QR Code version.
1038    #[must_use]
1039    #[inline]
1040    pub const fn version(mut self, version: QrVersion) -> Self {
1041        self.versions = version..=version;
1042        self
1043    }
1044
1045    /// Selects the inclusive Model 2 QR Code version range considered during encoding.
1046    #[must_use]
1047    #[inline]
1048    pub const fn version_range(mut self, versions: RangeInclusive<QrVersion>) -> Self {
1049        self.versions = versions;
1050        self
1051    }
1052
1053    /// Forces an exact mask instead of selecting one automatically.
1054    #[must_use]
1055    #[inline]
1056    pub const fn mask(mut self, mask: QrMask) -> Self {
1057        self.mask = Some(mask);
1058        self
1059    }
1060
1061    /// Enables or disables upgrading the error correction level when the selected version has room.
1062    #[must_use]
1063    #[inline]
1064    pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1065        self.boost_error_correction = boost;
1066        self
1067    }
1068
1069    /// Sets the FNC1 interpretation applied to the complete symbol.
1070    #[must_use]
1071    #[inline]
1072    pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1073        self.fnc1 = fnc1;
1074        self
1075    }
1076
1077    /// Encodes raw bytes with globally optimized Numeric, Alphanumeric and Byte segments.
1078    pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1079        let data = data.as_ref();
1080        let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1081
1082        ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1083
1084        self.encode_optimized_bytes(data, None)
1085    }
1086
1087    /// Encodes text with globally optimized character modes and ECI transitions.
1088    pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1089        let text = text.as_ref();
1090        let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1091
1092        ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1093
1094        self.encode_optimized_text(text, None, false)
1095    }
1096
1097    /// Encodes a value after converting it to its QR Code text representation.
1098    #[inline]
1099    pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1100        &self,
1101        value: &T,
1102    ) -> Result<Symbol, EncodeError> {
1103        let text = value.to_qr_text();
1104        self.encode_text(&text)
1105    }
1106
1107    /// Encodes explicit segments without changing their boundaries.
1108    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1109        self.encode_segments_with_header(segments, None)
1110    }
1111
1112    /// Encodes caller-selected byte parts as one Structured Append sequence.
1113    pub fn encode_structured_append_bytes(
1114        &self,
1115        parts: &[&[u8]],
1116    ) -> Result<Vec<Symbol>, EncodeError> {
1117        validate_part_count(parts.len())?;
1118        let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1119
1120        for part in parts {
1121            ensure_input_length(part.len(), capacity, capacity_bits, 1)?;
1122        }
1123
1124        let parity = parts
1125            .iter()
1126            .flat_map(|part| part.iter().copied())
1127            .fold(0, |parity, byte| parity ^ byte);
1128
1129        parts
1130            .iter()
1131            .enumerate()
1132            .map(|(index, part)| {
1133                self.encode_optimized_bytes(
1134                    part,
1135                    Some(StructuredAppendInfo {
1136                        index: index as u8,
1137                        total: parts.len() as u8,
1138                        parity,
1139                    }),
1140                )
1141            })
1142            .collect()
1143    }
1144
1145    /// Encodes caller-selected text parts as one Structured Append sequence.
1146    pub fn encode_structured_append_text(
1147        &self,
1148        parts: &[&str],
1149    ) -> Result<Vec<Symbol>, EncodeError> {
1150        validate_part_count(parts.len())?;
1151        let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1152
1153        for part in parts {
1154            ensure_input_length(part.chars().count(), capacity, capacity_bits, 1)?;
1155        }
1156
1157        let force_initial_eci = parts.iter().any(|part| requires_non_default_eci(part));
1158        let total = parts.len() as u8;
1159
1160        // Each part is optimized once, and both the shared parity and the final symbols reuse the result.
1161        let mut plans = Vec::with_capacity(parts.len());
1162        let mut parity = 0u8;
1163
1164        for part in parts {
1165            let (version, segments) = self.qr_structured_plan(part, force_initial_eci)?;
1166
1167            // Parity uses the byte representation selected by the optimizer, including Shift JIS or UTF-8 bytes.
1168            for segment in &segments {
1169                for &byte in segment.source_bytes() {
1170                    parity ^= byte;
1171                }
1172            }
1173
1174            plans.push((version, segments));
1175        }
1176
1177        plans
1178            .into_iter()
1179            .enumerate()
1180            .map(|(index, (version, segments))| {
1181                model2::encode(
1182                    &segments,
1183                    version,
1184                    self.error_correction,
1185                    self.mask,
1186                    self.boost_error_correction,
1187                    self.fnc1,
1188                    Some(StructuredAppendInfo {
1189                        index: index as u8,
1190                        total,
1191                        parity,
1192                    }),
1193                )
1194            })
1195            .collect()
1196    }
1197
1198    // Picks the smallest version that fits one Structured Append part and returns its optimized segments.
1199    fn qr_structured_plan(
1200        &self,
1201        text: &str,
1202        force_initial_eci: bool,
1203    ) -> Result<(QrVersion, Vec<Segment>), EncodeError> {
1204        if self.versions.start() > self.versions.end() {
1205            return Err(EncodeError::InvalidVersionRange);
1206        }
1207
1208        // Only the presence of the header matters for the fit check, so the metadata values are placeholders.
1209        let header = StructuredAppendInfo {
1210            index: 0, total: 16, parity: 0
1211        };
1212        let range = self.versions.clone();
1213        let mut cache = GroupCache::new(|version| {
1214            optimizer::text(
1215                text,
1216                optimizer::Profile::qr(version),
1217                self.fnc1.is_some(),
1218                force_initial_eci,
1219            )
1220        });
1221
1222        for value in range.start().0..=range.end().0 {
1223            let version = QrVersion(value);
1224            let segments = cache.get(version)?;
1225
1226            if model2::fits(segments, version, self.error_correction, self.fnc1, Some(header)) {
1227                return Ok((version, segments.to_vec()));
1228            }
1229        }
1230
1231        // No version fits, so the largest one is returned to reproduce the same capacity error later.
1232        let version = *range.end();
1233
1234        Ok((version, cache.take(version)?))
1235    }
1236
1237    /// Encodes caller-selected segment parts as one Structured Append sequence.
1238    pub fn encode_structured_append_segments(
1239        &self,
1240        parts: &[&[Segment]],
1241    ) -> Result<Vec<Symbol>, EncodeError> {
1242        validate_part_count(parts.len())?;
1243
1244        let parity = parts
1245            .iter()
1246            .flat_map(|part| part.iter())
1247            .flat_map(|segment| segment.source_bytes().iter().copied())
1248            .fold(0, |parity, byte| parity ^ byte);
1249
1250        parts
1251            .iter()
1252            .enumerate()
1253            .map(|(index, part)| {
1254                self.encode_segments_with_header(
1255                    part,
1256                    Some(StructuredAppendInfo {
1257                        index: index as u8,
1258                        total: parts.len() as u8,
1259                        parity,
1260                    }),
1261                )
1262            })
1263            .collect()
1264    }
1265
1266    /// Encodes bytes as one symbol or automatically splits them into at most 16 Structured Append symbols.
1267    ///
1268    /// When the data fits one symbol, that lone symbol is returned without Structured Append metadata, because a single symbol needs no sequence header.
1269    pub fn encode_bytes_with_structured_append(
1270        &self,
1271        data: impl AsRef<[u8]>,
1272    ) -> Result<Vec<Symbol>, EncodeError> {
1273        let data = data.as_ref();
1274
1275        match self.encode_bytes(data) {
1276            Ok(symbol) => return Ok(vec![symbol]),
1277            Err(EncodeError::DataTooLong {
1278                ..
1279            }) => {},
1280            Err(error) => return Err(error),
1281        }
1282
1283        let range = self.structured_append_range()?;
1284        let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1285
1286        ensure_input_length(data.len(), capacity, capacity_bits, 16)?;
1287
1288        // Partitioning failures replace internal placeholder errors with the full sequence capacity.
1289        let sequence_capacity_error = || EncodeError::DataTooLong {
1290            required_bits: None,
1291            capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1292        };
1293
1294        // Greedy maximum-size parts determine the minimum possible symbol count.
1295        let minimum_parts =
1296            self.partition_bytes(data, range.clone()).map_err(|_| sequence_capacity_error())?.len();
1297
1298        let mut selected = None;
1299
1300        for value in range.start().value()..=range.end().value() {
1301            let candidate = QrVersion(value);
1302
1303            if let Ok(parts) = self.partition_bytes(data, *range.start()..=candidate)
1304                && parts.len() == minimum_parts
1305            {
1306                selected = Some(
1307                    self.minimum_area_byte_partition(
1308                        data,
1309                        minimum_parts,
1310                        *range.start()..=candidate,
1311                    )
1312                    .map_err(|_| sequence_capacity_error())?,
1313                );
1314                break;
1315            }
1316        }
1317
1318        let parts = selected.ok_or_else(sequence_capacity_error)?;
1319
1320        let slices: Vec<&[u8]> = parts.into_iter().map(|(start, end)| &data[start..end]).collect();
1321
1322        self.encode_structured_append_bytes(&slices)
1323    }
1324
1325    /// Encodes text as one symbol or automatically splits it into at most 16 Structured Append symbols.
1326    ///
1327    /// When the text fits one symbol, that lone symbol is returned without Structured Append metadata, because a single symbol needs no sequence header.
1328    pub fn encode_text_with_structured_append(
1329        &self,
1330        text: impl AsRef<str>,
1331    ) -> Result<Vec<Symbol>, EncodeError> {
1332        let text = text.as_ref();
1333
1334        match self.encode_text(text) {
1335            Ok(symbol) => return Ok(vec![symbol]),
1336            Err(EncodeError::DataTooLong {
1337                ..
1338            }) => {},
1339            Err(error) => return Err(error),
1340        }
1341
1342        let range = self.structured_append_range()?;
1343        let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1344
1345        ensure_input_length(text.chars().count(), capacity, capacity_bits, 16)?;
1346
1347        let offsets = text_boundaries(text);
1348
1349        // Partitioning failures replace internal placeholder errors with the full sequence capacity.
1350        let sequence_capacity_error = || EncodeError::DataTooLong {
1351            required_bits: None,
1352            capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1353        };
1354
1355        // Text partitions use scalar boundaries so no UTF-8 character is split between symbols.
1356        let minimum_parts = self
1357            .partition_text(text, &offsets, range.clone())
1358            .map_err(|_| sequence_capacity_error())?
1359            .len();
1360
1361        let mut selected = None;
1362
1363        for value in range.start().value()..=range.end().value() {
1364            let candidate = QrVersion(value);
1365
1366            if let Ok(parts) = self.partition_text(text, &offsets, *range.start()..=candidate)
1367                && parts.len() == minimum_parts
1368            {
1369                selected = Some(
1370                    self.minimum_area_text_partition(
1371                        text,
1372                        &offsets,
1373                        minimum_parts,
1374                        *range.start()..=candidate,
1375                    )
1376                    .map_err(|_| sequence_capacity_error())?,
1377                );
1378                break;
1379            }
1380        }
1381
1382        let parts = selected.ok_or_else(sequence_capacity_error)?;
1383
1384        let slices: Vec<&str> =
1385            parts.into_iter().map(|(start, end)| &text[offsets[start]..offsets[end]]).collect();
1386        self.encode_structured_append_text(&slices)
1387    }
1388
1389    fn structured_append_range(&self) -> Result<RangeInclusive<QrVersion>, EncodeError> {
1390        if self.versions.start() > self.versions.end() {
1391            Err(EncodeError::InvalidVersionRange)
1392        } else {
1393            Ok(self.versions.clone())
1394        }
1395    }
1396
1397    fn input_capacity_upper_bound(
1398        &self,
1399        structured_append: bool,
1400    ) -> Result<(usize, usize), EncodeError> {
1401        if self.versions.start() > self.versions.end() {
1402            return Err(EncodeError::InvalidVersionRange);
1403        }
1404
1405        let version = *self.versions.end();
1406        let capacity_bits = model2::data_codewords(version, self.error_correction) * 8;
1407        let overhead_bits = 4
1408            + usize::from(Mode::Numeric.cci_bits(version))
1409            + usize::from(structured_append) * 20
1410            + match self.fnc1 {
1411                Some(Fnc1::Gs1) => 4,
1412                Some(Fnc1::Industry(_)) => 12,
1413                None => 0,
1414            };
1415
1416        Ok((numeric_character_capacity(capacity_bits, overhead_bits), capacity_bits))
1417    }
1418
1419    fn partition_bytes(
1420        &self,
1421        data: &[u8],
1422        range: RangeInclusive<QrVersion>,
1423    ) -> Result<Vec<(usize, usize)>, EncodeError> {
1424        let maximum_version = *range.end();
1425        let mut result = Vec::new();
1426        let mut start = 0;
1427
1428        while start < data.len() {
1429            if result.len() == 16 {
1430                return Err(EncodeError::DataTooLong {
1431                    required_bits: None, capacity_bits: 0
1432                });
1433            }
1434
1435            let mut low = start + 1;
1436
1437            // Numeric mode gives the largest possible character capacity for any input.
1438            let mut high = data
1439                .len()
1440                .min(start.saturating_add(self.qr_character_upper_bound(maximum_version)));
1441
1442            let mut fitting = None;
1443
1444            while low <= high {
1445                let middle = low + (high - low) / 2;
1446
1447                if self.bytes_fit(&data[start..middle], range.clone()) {
1448                    fitting = Some(middle);
1449                    low = middle + 1;
1450                } else {
1451                    high = middle - 1;
1452                }
1453            }
1454
1455            let end = fitting
1456                .ok_or(EncodeError::DataTooLong {
1457                    required_bits: None, capacity_bits: 0
1458                })?;
1459
1460            result.push((start, end));
1461            start = end;
1462        }
1463        Ok(result)
1464    }
1465
1466    fn partition_text(
1467        &self,
1468        text: &str,
1469        offsets: &[usize],
1470        range: RangeInclusive<QrVersion>,
1471    ) -> Result<Vec<(usize, usize)>, EncodeError> {
1472        let maximum_version = *range.end();
1473        let force_initial_eci = requires_non_default_eci(text);
1474
1475        let mut result = Vec::new();
1476        let mut start = 0;
1477        let length = offsets.len() - 1;
1478
1479        while start < length {
1480            if result.len() == 16 {
1481                return Err(EncodeError::DataTooLong {
1482                    required_bits: None, capacity_bits: 0
1483                });
1484            }
1485
1486            let mut low = start + 1;
1487
1488            // Numeric mode gives the largest possible scalar capacity for any text input.
1489            let mut high =
1490                length.min(start.saturating_add(self.qr_character_upper_bound(maximum_version)));
1491
1492            let mut fitting = None;
1493
1494            while low <= high {
1495                let middle = low + (high - low) / 2;
1496
1497                if self.text_fits(
1498                    &text[offsets[start]..offsets[middle]],
1499                    range.clone(),
1500                    force_initial_eci,
1501                ) {
1502                    fitting = Some(middle);
1503                    low = middle + 1;
1504                } else {
1505                    high = middle - 1;
1506                }
1507            }
1508
1509            let end = fitting
1510                .ok_or(EncodeError::DataTooLong {
1511                    required_bits: None, capacity_bits: 0
1512                })?;
1513
1514            result.push((start, end));
1515
1516            start = end;
1517        }
1518        Ok(result)
1519    }
1520
1521    fn minimum_area_byte_partition(
1522        &self,
1523        data: &[u8],
1524        part_count: usize,
1525        range: RangeInclusive<QrVersion>,
1526    ) -> Result<Vec<(usize, usize)>, EncodeError> {
1527        minimum_area_partition(data.len(), part_count, range, |start, version| {
1528            let high = data.len().min(start.saturating_add(self.qr_character_upper_bound(version)));
1529
1530            maximum_fitting_end(high, start, |end| {
1531                self.bytes_fit(&data[start..end], version..=version)
1532            })
1533        })
1534    }
1535
1536    fn minimum_area_text_partition(
1537        &self,
1538        text: &str,
1539        offsets: &[usize],
1540        part_count: usize,
1541        range: RangeInclusive<QrVersion>,
1542    ) -> Result<Vec<(usize, usize)>, EncodeError> {
1543        let length = offsets.len() - 1;
1544        let force_initial_eci = requires_non_default_eci(text);
1545
1546        minimum_area_partition(length, part_count, range, |start, version| {
1547            let high = length.min(start.saturating_add(self.qr_character_upper_bound(version)));
1548
1549            maximum_fitting_end(high, start, |end| {
1550                self.text_fits(
1551                    &text[offsets[start]..offsets[end]],
1552                    version..=version,
1553                    force_initial_eci,
1554                )
1555            })
1556        })
1557    }
1558
1559    // Reports whether the bytes fit some version in the range as one Structured Append part.
1560    fn bytes_fit(&self, data: &[u8], range: RangeInclusive<QrVersion>) -> bool {
1561        self.qr_range_fits(range, |version| {
1562            optimizer::bytes(data, optimizer::Profile::qr(version), self.fnc1.is_some())
1563        })
1564    }
1565
1566    // Reports whether the text fits some version in the range as one Structured Append part.
1567    fn text_fits(
1568        &self,
1569        text: &str,
1570        range: RangeInclusive<QrVersion>,
1571        force_initial_eci: bool,
1572    ) -> bool {
1573        self.qr_range_fits(range, |version| {
1574            optimizer::text(
1575                text,
1576                optimizer::Profile::qr(version),
1577                self.fnc1.is_some(),
1578                force_initial_eci,
1579            )
1580        })
1581    }
1582
1583    // Runs the fit check without drawing a symbol, so probing skips the matrix and mask work.
1584    fn qr_range_fits<F>(&self, range: RangeInclusive<QrVersion>, segments: F) -> bool
1585    where
1586        F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>, {
1587        if range.start() > range.end() {
1588            return false;
1589        }
1590
1591        // Only the presence of the header matters for the fit check, so the values are placeholders.
1592        let header = StructuredAppendInfo {
1593            index: 0, total: 16, parity: 0
1594        };
1595        let mut cache = GroupCache::new(segments);
1596
1597        for value in range.start().0..=range.end().0 {
1598            let version = QrVersion(value);
1599
1600            let Ok(candidate) = cache.get(version) else {
1601                return false;
1602            };
1603
1604            if model2::fits(candidate, version, self.error_correction, self.fnc1, Some(header)) {
1605                return true;
1606            }
1607        }
1608
1609        false
1610    }
1611
1612    #[inline]
1613    const fn qr_character_upper_bound(&self, version: QrVersion) -> usize {
1614        model2::data_codewords(version, self.error_correction) * 8 * 3 / 10 + 2
1615    }
1616
1617    fn encode_optimized_bytes(
1618        &self,
1619        data: &[u8],
1620        structured_append: Option<StructuredAppendInfo>,
1621    ) -> Result<Symbol, EncodeError> {
1622        self.encode_qr_range(
1623            self.versions.clone(),
1624            |version| optimizer::bytes(data, optimizer::Profile::qr(version), self.fnc1.is_some()),
1625            structured_append,
1626        )
1627    }
1628
1629    fn encode_optimized_text(
1630        &self,
1631        text: &str,
1632        structured_append: Option<StructuredAppendInfo>,
1633        force_initial_eci: bool,
1634    ) -> Result<Symbol, EncodeError> {
1635        self.encode_qr_range(
1636            self.versions.clone(),
1637            |version| {
1638                optimizer::text(
1639                    text,
1640                    optimizer::Profile::qr(version),
1641                    self.fnc1.is_some(),
1642                    force_initial_eci,
1643                )
1644            },
1645            structured_append,
1646        )
1647    }
1648
1649    fn encode_qr_range<F>(
1650        &self,
1651        range: RangeInclusive<QrVersion>,
1652        segments: F,
1653        structured_append: Option<StructuredAppendInfo>,
1654    ) -> Result<Symbol, EncodeError>
1655    where
1656        F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>, {
1657        if range.start() > range.end() {
1658            return Err(EncodeError::InvalidVersionRange);
1659        }
1660
1661        let mut cache = GroupCache::new(segments);
1662        let mut last_error = None;
1663
1664        for value in range.start().0..=range.end().0 {
1665            let version = QrVersion(value);
1666            let candidate = cache.get(version)?;
1667
1668            match model2::encode(
1669                candidate,
1670                version,
1671                self.error_correction,
1672                self.mask,
1673                self.boost_error_correction,
1674                self.fnc1,
1675                structured_append,
1676            ) {
1677                Ok(symbol) => return Ok(symbol),
1678                Err(
1679                    error @ EncodeError::DataTooLong {
1680                        ..
1681                    },
1682                ) => last_error = Some(error),
1683                Err(error) => return Err(error),
1684            }
1685        }
1686
1687        // The version range is verified to be non-empty, so at least one capacity error was stored.
1688        Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
1689    }
1690
1691    fn encode_segments_with_header(
1692        &self,
1693        segments: &[Segment],
1694        structured_append: Option<StructuredAppendInfo>,
1695    ) -> Result<Symbol, EncodeError> {
1696        let normalized;
1697
1698        let segments = if self.fnc1.is_some() {
1699            normalized = segments
1700                .iter()
1701                .map(|segment| {
1702                    if segment.mode == Mode::Alphanumeric {
1703                        Segment::fnc1_alphanumeric(segment.source_bytes())
1704                    } else {
1705                        Ok(segment.clone())
1706                    }
1707                })
1708                .collect::<Result<Vec<_>, _>>()?;
1709            normalized.as_slice()
1710        } else {
1711            segments
1712        };
1713
1714        self.encode_qr_range(self.versions.clone(), |_| Ok(segments.to_vec()), structured_append)
1715    }
1716}
1717
1718/// Configures and encodes Rectangular Micro QR Code symbols.
1719#[cfg(feature = "rmqr")]
1720#[derive(Clone, Debug)]
1721pub struct RmqrEncoder {
1722    error_correction:       RmqrErrorCorrection,
1723    versions:               RangeInclusive<RmqrVersion>,
1724    boost_error_correction: bool,
1725    fnc1:                   Option<Fnc1>,
1726}
1727
1728#[cfg(feature = "rmqr")]
1729impl RmqrEncoder {
1730    /// Creates an encoder that considers every rMQR version.
1731    #[inline]
1732    pub const fn new(error_correction: RmqrErrorCorrection) -> Self {
1733        Self {
1734            error_correction,
1735            versions: RmqrVersion::MIN..=RmqrVersion::MAX,
1736            boost_error_correction: true,
1737            fnc1: None,
1738        }
1739    }
1740
1741    /// Selects one exact rMQR version.
1742    #[must_use]
1743    #[inline]
1744    pub const fn version(mut self, version: RmqrVersion) -> Self {
1745        self.versions = version..=version;
1746        self
1747    }
1748
1749    /// Selects an inclusive range in rMQR format indicator order.
1750    #[must_use]
1751    #[inline]
1752    pub const fn version_range(mut self, versions: RangeInclusive<RmqrVersion>) -> Self {
1753        self.versions = versions;
1754        self
1755    }
1756
1757    /// Enables or disables upgrading Medium error correction to High when the selected version has room.
1758    #[must_use]
1759    #[inline]
1760    pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1761        self.boost_error_correction = boost;
1762        self
1763    }
1764
1765    /// Sets the FNC1 interpretation applied to the complete symbol.
1766    #[must_use]
1767    #[inline]
1768    pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1769        self.fnc1 = fnc1;
1770        self
1771    }
1772
1773    /// Encodes raw bytes with globally optimized Numeric, Alphanumeric and Byte segments.
1774    pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1775        let data = data.as_ref();
1776        let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
1777
1778        ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1779        self.encode_optimized(|version| {
1780            optimizer::bytes(
1781                data,
1782                optimizer::Profile::rmqr(rmqr::cci(version)),
1783                self.fnc1.is_some(),
1784            )
1785        })
1786    }
1787
1788    /// Encodes text with globally optimized character modes and ECI transitions.
1789    pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1790        let text = text.as_ref();
1791        let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
1792
1793        ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1794        self.encode_optimized(|version| {
1795            optimizer::text(
1796                text,
1797                optimizer::Profile::rmqr(rmqr::cci(version)),
1798                self.fnc1.is_some(),
1799                false,
1800            )
1801        })
1802    }
1803
1804    /// Encodes a value after converting it to its QR Code text representation.
1805    #[inline]
1806    pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1807        &self,
1808        value: &T,
1809    ) -> Result<Symbol, EncodeError> {
1810        let text = value.to_qr_text();
1811        self.encode_text(&text)
1812    }
1813
1814    /// Encodes explicit segments without changing their boundaries.
1815    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1816        let normalized;
1817        let segments = if self.fnc1.is_some() {
1818            normalized = segments
1819                .iter()
1820                .map(|segment| {
1821                    if segment.mode == Mode::Alphanumeric {
1822                        Segment::fnc1_alphanumeric(segment.source_bytes())
1823                    } else {
1824                        Ok(segment.clone())
1825                    }
1826                })
1827                .collect::<Result<Vec<_>, _>>()?;
1828            normalized.as_slice()
1829        } else {
1830            segments
1831        };
1832
1833        self.encode_optimized(|_| Ok(segments.to_vec()))
1834    }
1835
1836    fn input_capacity_upper_bound(&self) -> Result<(usize, usize), EncodeError> {
1837        let versions = self.candidates()?;
1838        let mut result = None;
1839
1840        for version in versions {
1841            let capacity_bits = rmqr::data_codewords(version, self.error_correction) * 8;
1842            let overhead_bits = 3
1843                + usize::from(rmqr::cci(version)[0])
1844                + match self.fnc1 {
1845                    Some(Fnc1::Gs1) => 3,
1846                    Some(Fnc1::Industry(_)) => 11,
1847                    None => 0,
1848                };
1849            let capacity = numeric_character_capacity(capacity_bits, overhead_bits);
1850
1851            if result.is_none_or(|(best, _)| capacity > best) {
1852                result = Some((capacity, capacity_bits));
1853            }
1854        }
1855
1856        result.ok_or(EncodeError::InvalidVersionRange)
1857    }
1858
1859    fn encode_optimized<F>(&self, mut optimize: F) -> Result<Symbol, EncodeError>
1860    where
1861        F: FnMut(RmqrVersion) -> Result<Vec<Segment>, EncodeError>, {
1862        let versions = self.candidates()?;
1863        let mut cache: Vec<([u8; 4], Vec<Segment>)> = Vec::with_capacity(15);
1864        let mut last_error = None;
1865
1866        for version in versions {
1867            let profile = rmqr::cci(version);
1868            let index = match cache.iter().position(|(cci, _)| *cci == profile) {
1869                Some(index) => index,
1870                None => {
1871                    cache.push((profile, optimize(version)?));
1872                    cache.len() - 1
1873                },
1874            };
1875
1876            match rmqr::encode(
1877                &cache[index].1,
1878                version,
1879                self.error_correction,
1880                self.boost_error_correction,
1881                self.fnc1,
1882            ) {
1883                Ok(symbol) => return Ok(symbol),
1884                Err(
1885                    error @ EncodeError::DataTooLong {
1886                        ..
1887                    },
1888                ) => last_error = Some(error),
1889                Err(error) => return Err(error),
1890            }
1891        }
1892
1893        Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
1894    }
1895
1896    fn candidates(&self) -> Result<Vec<RmqrVersion>, EncodeError> {
1897        if self.versions.start() > self.versions.end() {
1898            return Err(EncodeError::InvalidVersionRange);
1899        }
1900
1901        Ok(RmqrVersion::ALL_BY_AREA
1902            .into_iter()
1903            .filter(|version| self.versions.contains(version))
1904            .collect())
1905    }
1906}
1907
1908/// Configures and encodes Micro QR Code symbols.
1909#[cfg(feature = "micro-qr")]
1910#[derive(Clone, Debug)]
1911pub struct MicroEncoder {
1912    error_correction:       MicroErrorCorrection,
1913    versions:               RangeInclusive<MicroVersion>,
1914    mask:                   Option<MicroMask>,
1915    boost_error_correction: bool,
1916}
1917
1918#[cfg(feature = "micro-qr")]
1919impl MicroEncoder {
1920    /// Creates an encoder with automatic mask selection and all Micro QR Code versions.
1921    #[inline]
1922    pub const fn new(error_correction: MicroErrorCorrection) -> Self {
1923        Self {
1924            error_correction,
1925            versions: MicroVersion::M1..=MicroVersion::M4,
1926            mask: None,
1927            boost_error_correction: true,
1928        }
1929    }
1930
1931    /// Selects one exact Micro QR Code version.
1932    #[must_use]
1933    #[inline]
1934    pub const fn version(mut self, version: MicroVersion) -> Self {
1935        self.versions = version..=version;
1936        self
1937    }
1938
1939    /// Selects the inclusive Micro QR Code version range considered during encoding.
1940    #[must_use]
1941    #[inline]
1942    pub const fn version_range(mut self, versions: RangeInclusive<MicroVersion>) -> Self {
1943        self.versions = versions;
1944        self
1945    }
1946
1947    /// Forces an exact mask instead of selecting one automatically.
1948    #[must_use]
1949    #[inline]
1950    pub const fn mask(mut self, mask: MicroMask) -> Self {
1951        self.mask = Some(mask);
1952        self
1953    }
1954
1955    /// Enables or disables upgrading the error correction level when the selected version has room.
1956    #[must_use]
1957    #[inline]
1958    pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1959        self.boost_error_correction = boost;
1960        self
1961    }
1962
1963    /// Encodes raw bytes with globally optimized modes supported by each candidate version.
1964    pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1965        let data = data.as_ref();
1966        if let Some((version, capacity, capacity_bits)) = self.input_capacity_upper_bound()?
1967            && micro_bytes_are_representable(data, version)
1968        {
1969            ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1970        }
1971
1972        self.encode_range(|version| micro::optimize(data, version))
1973    }
1974
1975    /// Encodes text with globally optimized modes supported by each candidate version.
1976    pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1977        let text = text.as_ref();
1978        if let Some((version, capacity, capacity_bits)) = self.input_capacity_upper_bound()?
1979            && micro_text_is_representable_without_kanji(text, version)
1980        {
1981            ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1982        }
1983
1984        self.encode_range(|version| micro::optimize_text(text, version))
1985    }
1986
1987    /// Encodes a value after converting it to its QR Code text representation.
1988    #[inline]
1989    pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1990        &self,
1991        value: &T,
1992    ) -> Result<Symbol, EncodeError> {
1993        let text = value.to_qr_text();
1994        self.encode_text(&text)
1995    }
1996
1997    /// Encodes explicit segments without changing their boundaries.
1998    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1999        self.encode_range(|_| Ok(segments.to_vec()))
2000    }
2001
2002    fn input_capacity_upper_bound(
2003        &self,
2004    ) -> Result<Option<(MicroVersion, usize, usize)>, EncodeError> {
2005        if self.versions.start() > self.versions.end() {
2006            return Err(EncodeError::InvalidVersionRange);
2007        }
2008
2009        Ok(micro_versions(self.versions.clone())
2010            .filter_map(|version| {
2011                micro::input_capacity_upper_bound(version, self.error_correction)
2012                    .map(|(capacity, capacity_bits)| (version, capacity, capacity_bits))
2013            })
2014            .max_by_key(|&(_, capacity, _)| capacity))
2015    }
2016
2017    fn encode_range<F>(&self, mut segments: F) -> Result<Symbol, EncodeError>
2018    where
2019        F: FnMut(MicroVersion) -> Result<Vec<Segment>, EncodeError>, {
2020        if self.versions.start() > self.versions.end() {
2021            return Err(EncodeError::InvalidVersionRange);
2022        }
2023
2024        let mut last_error = None;
2025
2026        // Unsupported modes and error correction levels eliminate only the current candidate version.
2027        for version in micro_versions(self.versions.clone()) {
2028            let result = segments(version).and_then(|segments| {
2029                micro::encode(
2030                    &segments,
2031                    version,
2032                    self.error_correction,
2033                    self.mask,
2034                    self.boost_error_correction,
2035                )
2036            });
2037
2038            match result {
2039                Ok(symbol) => return Ok(symbol),
2040                Err(error) if candidate_rejection(&error) => last_error = Some(error),
2041                Err(error) => return Err(error),
2042            }
2043        }
2044
2045        Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
2046    }
2047}
2048
2049/// Tries a configured Micro QR Code encoder before a configured Model 2 QR Code encoder.
2050#[cfg(all(feature = "qr", feature = "micro-qr"))]
2051#[derive(Clone, Debug)]
2052pub struct AutoEncoder {
2053    qr:    QrEncoder,
2054    micro: MicroEncoder,
2055}
2056
2057#[cfg(all(feature = "qr", feature = "micro-qr"))]
2058impl AutoEncoder {
2059    /// Creates an encoder from independently configured Model 2 and Micro QR Code encoders.
2060    #[inline]
2061    pub const fn new(qr: QrEncoder, micro: MicroEncoder) -> Self {
2062        Self {
2063            qr,
2064            micro,
2065        }
2066    }
2067
2068    /// Encodes raw bytes using the smallest eligible symbol family.
2069    pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2070        let data = data.as_ref();
2071        match self.micro.encode_bytes(data) {
2072            Ok(symbol) => Ok(symbol),
2073            Err(error) if candidate_rejection(&error) => self.qr.encode_bytes(data),
2074            Err(error) => Err(error),
2075        }
2076    }
2077
2078    /// Encodes text using the smallest eligible symbol family.
2079    pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2080        let text = text.as_ref();
2081        match self.micro.encode_text(text) {
2082            Ok(symbol) => Ok(symbol),
2083            Err(error) if candidate_rejection(&error) => self.qr.encode_text(text),
2084            Err(error) => Err(error),
2085        }
2086    }
2087
2088    /// Encodes a value after converting it to its QR Code text representation once.
2089    #[inline]
2090    pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2091        &self,
2092        value: &T,
2093    ) -> Result<Symbol, EncodeError> {
2094        let text = value.to_qr_text();
2095        self.encode_text(&text)
2096    }
2097
2098    /// Encodes explicit segments using the smallest eligible symbol family.
2099    pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2100        match self.micro.encode_segments(segments) {
2101            Ok(symbol) => Ok(symbol),
2102            Err(error) if candidate_rejection(&error) => self.qr.encode_segments(segments),
2103            Err(error) => Err(error),
2104        }
2105    }
2106}
2107
2108#[cfg(feature = "qr")]
2109#[derive(Clone, Copy)]
2110struct PartitionState {
2111    end:      usize,
2112    area:     usize,
2113    previous: usize,
2114}
2115
2116#[cfg(feature = "qr")]
2117fn minimum_area_partition<F>(
2118    length: usize,
2119    part_count: usize,
2120    range: RangeInclusive<QrVersion>,
2121    mut maximum_end: F,
2122) -> Result<Vec<(usize, usize)>, EncodeError>
2123where
2124    F: FnMut(usize, QrVersion) -> Option<usize>, {
2125    // Each layer represents one additional symbol in the fixed-size Structured Append sequence.
2126    let mut layers = vec![vec![PartitionState {
2127        end: 0, area: 0, previous: 0
2128    }]];
2129
2130    for _ in 0..part_count {
2131        let previous_layer = layers.last().expect("the initial layer exists");
2132        let mut by_end = BTreeMap::new();
2133
2134        for (previous, state) in previous_layer.iter().enumerate() {
2135            for value in range.start().value()..=range.end().value() {
2136                let version = QrVersion(value);
2137
2138                let Some(end) = maximum_end(state.end, version) else {
2139                    continue;
2140                };
2141
2142                let size = usize::from(version.value()) * 4 + 17;
2143
2144                let candidate = PartitionState {
2145                    end,
2146                    area: state.area + size * size,
2147                    previous,
2148                };
2149
2150                by_end
2151                    .entry(end)
2152                    .and_modify(|current: &mut PartitionState| {
2153                        if candidate.area < current.area {
2154                            *current = candidate;
2155                        }
2156                    })
2157                    .or_insert(candidate);
2158            }
2159        }
2160
2161        let mut best_area = usize::MAX;
2162        let mut layer = Vec::new();
2163
2164        for state in by_end.into_values().rev() {
2165            // A state that reaches farther with no greater area dominates every earlier state.
2166            if state.area < best_area {
2167                best_area = state.area;
2168                layer.push(state);
2169            }
2170        }
2171
2172        layer.reverse();
2173
2174        if layer.is_empty() {
2175            return Err(EncodeError::DataTooLong {
2176                required_bits: None, capacity_bits: 0
2177            });
2178        }
2179
2180        layers.push(layer);
2181    }
2182
2183    let mut state_index = layers[part_count].iter().position(|state| state.end == length).ok_or(
2184        EncodeError::DataTooLong {
2185            required_bits: None, capacity_bits: 0
2186        },
2187    )?;
2188
2189    let mut result = Vec::with_capacity(part_count);
2190
2191    for layer_index in (1..=part_count).rev() {
2192        let state = layers[layer_index][state_index];
2193        let previous = layers[layer_index - 1][state.previous];
2194        result.push((previous.end, state.end));
2195        state_index = state.previous;
2196    }
2197
2198    result.reverse();
2199
2200    Ok(result)
2201}
2202
2203#[cfg(feature = "qr")]
2204fn maximum_fitting_end<F>(length: usize, start: usize, mut fits: F) -> Option<usize>
2205where
2206    F: FnMut(usize) -> bool, {
2207    if start == length {
2208        return None;
2209    }
2210
2211    let mut low = start + 1;
2212    let mut high = length;
2213    let mut result = None;
2214
2215    // Encoding fit is monotonic as the candidate slice grows from a fixed start.
2216    while low <= high {
2217        let middle = low + (high - low) / 2;
2218
2219        if fits(middle) {
2220            result = Some(middle);
2221            low = middle + 1;
2222        } else {
2223            high = middle - 1;
2224        }
2225    }
2226    result
2227}
2228
2229#[cfg(feature = "micro-qr")]
2230fn micro_versions(versions: RangeInclusive<MicroVersion>) -> impl Iterator<Item = MicroVersion> {
2231    let start = *versions.start();
2232    let end = *versions.end();
2233
2234    [MicroVersion::M1, MicroVersion::M2, MicroVersion::M3, MicroVersion::M4]
2235        .into_iter()
2236        .filter(move |version| *version >= start && *version <= end)
2237}
2238
2239#[cfg(feature = "micro-qr")]
2240fn micro_bytes_are_representable(data: &[u8], version: MicroVersion) -> bool {
2241    match version {
2242        MicroVersion::M1 => data.iter().all(u8::is_ascii_digit),
2243        MicroVersion::M2 => data.iter().all(|byte| alphanumeric_value(*byte).is_some()),
2244        MicroVersion::M3 | MicroVersion::M4 => true,
2245    }
2246}
2247
2248#[cfg(feature = "micro-qr")]
2249fn micro_text_is_representable_without_kanji(text: &str, version: MicroVersion) -> bool {
2250    match version {
2251        MicroVersion::M1 => text.bytes().all(|byte| byte.is_ascii_digit()),
2252        MicroVersion::M2 => {
2253            text.is_ascii() && text.bytes().all(|byte| alphanumeric_value(byte).is_some())
2254        },
2255        MicroVersion::M3 | MicroVersion::M4 => {
2256            text.chars().all(|character| u32::from(character) <= 0xFF)
2257        },
2258    }
2259}
2260
2261#[cfg(feature = "micro-qr")]
2262#[inline]
2263const fn candidate_rejection(error: &EncodeError) -> bool {
2264    matches!(
2265        error,
2266        EncodeError::DataTooLong { .. }
2267            | EncodeError::UnsupportedMode { .. }
2268            | EncodeError::UnsupportedErrorCorrection { .. }
2269            | EncodeError::TextNotRepresentable { .. }
2270    )
2271}
2272
2273#[cfg(feature = "qr")]
2274#[inline]
2275const fn version_group(version: QrVersion) -> usize {
2276    if version.0 <= 9 {
2277        0
2278    } else if version.0 <= 26 {
2279        1
2280    } else {
2281        2
2282    }
2283}
2284
2285// Caches segments per version group during a version scan, because character count indicator widths change only at versions 10 and 27.
2286#[cfg(feature = "qr")]
2287struct GroupCache<F> {
2288    segments: F,
2289    cache:    [Option<Vec<Segment>>; 3],
2290}
2291
2292#[cfg(feature = "qr")]
2293impl<F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>> GroupCache<F> {
2294    fn new(segments: F) -> Self {
2295        Self {
2296            segments,
2297            cache: [None, None, None],
2298        }
2299    }
2300
2301    fn get(&mut self, version: QrVersion) -> Result<&[Segment], EncodeError> {
2302        let group = version_group(version);
2303
2304        if self.cache[group].is_none() {
2305            self.cache[group] = Some((self.segments)(version)?);
2306        }
2307
2308        Ok(self.cache[group].as_deref().expect("the version group is cached"))
2309    }
2310
2311    fn take(&mut self, version: QrVersion) -> Result<Vec<Segment>, EncodeError> {
2312        self.get(version)?;
2313
2314        Ok(self.cache[version_group(version)].take().expect("the version group is cached"))
2315    }
2316}
2317
2318#[cfg(feature = "qr")]
2319#[inline]
2320const fn validate_part_count(count: usize) -> Result<(), EncodeError> {
2321    if count >= 1 && count <= 16 {
2322        Ok(())
2323    } else {
2324        Err(EncodeError::InvalidStructuredAppendPartCount {
2325            count,
2326        })
2327    }
2328}
2329
2330#[cfg(feature = "qr")]
2331fn text_boundaries(text: &str) -> Vec<usize> {
2332    let mut result: Vec<_> = text.char_indices().map(|(offset, _)| offset).collect();
2333    result.push(text.len());
2334    result
2335}
2336
2337#[cfg(feature = "qr")]
2338#[inline]
2339fn requires_non_default_eci(text: &str) -> bool {
2340    text.chars().any(|character| u32::from(character) > 0xFF)
2341}