1#[cfg(any(feature = "qr", feature = "rmqr"))]
2use alloc::borrow::Cow;
3#[cfg(feature = "qr")]
4use alloc::collections::BTreeMap;
5#[cfg(feature = "qr")]
6use alloc::vec;
7use alloc::{string::String, vec::Vec};
8#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
9use core::ops::RangeInclusive;
10
11mod bits;
12#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
13mod reed_solomon;
14
15#[cfg(feature = "micro-qr")]
16mod micro;
17#[cfg(feature = "qr")]
18mod model2;
19#[cfg(any(feature = "qr", feature = "rmqr"))]
20mod optimizer;
21#[cfg(feature = "rmqr")]
22mod rmqr;
23
24use bits::BitBuffer;
25
26use crate::EncodeError;
27
28#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
29#[inline]
30const fn numeric_character_capacity(capacity_bits: usize, overhead_bits: usize) -> usize {
31 let payload_bits = capacity_bits.saturating_sub(overhead_bits);
32
33 payload_bits / 10 * 3
34 + match payload_bits % 10 {
35 4..=6 => 1,
36 7..=9 => 2,
37 _ => 0,
38 }
39}
40
41#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
42#[inline]
43const fn ensure_input_length(
44 length: usize,
45 per_symbol_capacity: usize,
46 capacity_bits: usize,
47 symbol_count: usize,
48) -> Result<(), EncodeError> {
49 if length > per_symbol_capacity.saturating_mul(symbol_count) {
50 Err(EncodeError::DataTooLong {
51 required_bits: None,
52 capacity_bits: capacity_bits.saturating_mul(symbol_count),
53 })
54 } else {
55 Ok(())
56 }
57}
58
59#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
63#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
64#[non_exhaustive]
65pub enum SymbolErrorCorrection {
66 #[cfg(feature = "micro-qr")]
68 DetectionOnly,
69 Low,
71 Medium,
73 Quartile,
75 #[cfg(any(feature = "qr", feature = "rmqr"))]
77 High,
78}
79
80#[cfg(feature = "qr")]
82#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
83pub enum QrErrorCorrection {
84 Low,
86 Medium,
88 Quartile,
90 High,
92}
93
94#[cfg(feature = "qr")]
95impl QrErrorCorrection {
96 #[inline]
97 pub(crate) const fn qr_ordinal(self) -> usize {
98 match self {
99 Self::Low => 0,
100 Self::Medium => 1,
101 Self::Quartile => 2,
102 Self::High => 3,
103 }
104 }
105
106 #[inline]
107 pub(crate) const fn qr_format_bits(self) -> u8 {
108 match self {
109 Self::Low => 1,
110 Self::Medium => 0,
111 Self::Quartile => 3,
112 Self::High => 2,
113 }
114 }
115}
116
117#[cfg(feature = "qr")]
118impl From<QrErrorCorrection> for SymbolErrorCorrection {
119 #[inline]
120 fn from(value: QrErrorCorrection) -> Self {
121 match value {
122 QrErrorCorrection::Low => Self::Low,
123 QrErrorCorrection::Medium => Self::Medium,
124 QrErrorCorrection::Quartile => Self::Quartile,
125 QrErrorCorrection::High => Self::High,
126 }
127 }
128}
129
130#[cfg(feature = "micro-qr")]
132#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
133pub enum MicroErrorCorrection {
134 DetectionOnly,
136 Low,
138 Medium,
140 Quartile,
142}
143
144#[cfg(feature = "micro-qr")]
145impl From<MicroErrorCorrection> for SymbolErrorCorrection {
146 #[inline]
147 fn from(value: MicroErrorCorrection) -> Self {
148 match value {
149 MicroErrorCorrection::DetectionOnly => Self::DetectionOnly,
150 MicroErrorCorrection::Low => Self::Low,
151 MicroErrorCorrection::Medium => Self::Medium,
152 MicroErrorCorrection::Quartile => Self::Quartile,
153 }
154 }
155}
156
157#[cfg(feature = "rmqr")]
159#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
160pub enum RmqrErrorCorrection {
161 Medium,
163 High,
165}
166
167#[cfg(feature = "rmqr")]
168impl From<RmqrErrorCorrection> for SymbolErrorCorrection {
169 #[inline]
170 fn from(value: RmqrErrorCorrection) -> Self {
171 match value {
172 RmqrErrorCorrection::Medium => Self::Medium,
173 RmqrErrorCorrection::High => Self::High,
174 }
175 }
176}
177
178#[cfg(feature = "qr")]
180#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
181pub struct QrVersion(u8);
182
183#[cfg(feature = "qr")]
184impl QrVersion {
185 pub const MAX: Self = Self(40);
187 pub const MIN: Self = Self(1);
189
190 #[inline]
192 pub const fn new(value: u8) -> Result<Self, EncodeError> {
193 if value >= 1 && value <= 40 {
194 Ok(Self(value))
195 } else {
196 Err(EncodeError::InvalidVersionRange)
197 }
198 }
199
200 #[inline]
202 pub const fn value(self) -> u8 {
203 self.0
204 }
205}
206
207#[cfg(feature = "micro-qr")]
209#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
210pub enum MicroVersion {
211 M1,
213 M2,
215 M3,
217 M4,
219}
220
221#[cfg(feature = "micro-qr")]
222impl MicroVersion {
223 #[inline]
225 pub const fn size(self) -> usize {
226 match self {
227 Self::M1 => 11,
228 Self::M2 => 13,
229 Self::M3 => 15,
230 Self::M4 => 17,
231 }
232 }
233}
234
235#[cfg(feature = "rmqr")]
237#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
238#[repr(u8)]
239pub enum RmqrVersion {
240 R7x43,
242 R7x59,
244 R7x77,
246 R7x99,
248 R7x139,
250 R9x43,
252 R9x59,
254 R9x77,
256 R9x99,
258 R9x139,
260 R11x27,
262 R11x43,
264 R11x59,
266 R11x77,
268 R11x99,
270 R11x139,
272 R13x27,
274 R13x43,
276 R13x59,
278 R13x77,
280 R13x99,
282 R13x139,
284 R15x43,
286 R15x59,
288 R15x77,
290 R15x99,
292 R15x139,
294 R17x43,
296 R17x59,
298 R17x77,
300 R17x99,
302 R17x139,
304}
305
306#[cfg(feature = "rmqr")]
307impl RmqrVersion {
308 pub(crate) const ALL: [Self; 32] = [
309 Self::R7x43,
310 Self::R7x59,
311 Self::R7x77,
312 Self::R7x99,
313 Self::R7x139,
314 Self::R9x43,
315 Self::R9x59,
316 Self::R9x77,
317 Self::R9x99,
318 Self::R9x139,
319 Self::R11x27,
320 Self::R11x43,
321 Self::R11x59,
322 Self::R11x77,
323 Self::R11x99,
324 Self::R11x139,
325 Self::R13x27,
326 Self::R13x43,
327 Self::R13x59,
328 Self::R13x77,
329 Self::R13x99,
330 Self::R13x139,
331 Self::R15x43,
332 Self::R15x59,
333 Self::R15x77,
334 Self::R15x99,
335 Self::R15x139,
336 Self::R17x43,
337 Self::R17x59,
338 Self::R17x77,
339 Self::R17x99,
340 Self::R17x139,
341 ];
342 pub(crate) const ALL_BY_AREA: [Self; 32] = Self::sorted_by_area();
344 pub const MAX: Self = Self::R17x139;
346 pub const MIN: Self = Self::R7x43;
348
349 const fn sorted_by_area() -> [Self; 32] {
350 let mut versions = Self::ALL;
351 let length = versions.len();
352 let mut index = 0;
353
354 while index < length {
356 let mut smallest = index;
357 let mut candidate = index + 1;
358
359 while candidate < length {
360 if versions[candidate].area_key() < versions[smallest].area_key() {
361 smallest = candidate;
362 }
363
364 candidate += 1;
365 }
366
367 let swap = versions[index];
368 versions[index] = versions[smallest];
369 versions[smallest] = swap;
370 index += 1;
371 }
372
373 versions
374 }
375
376 const fn area_key(self) -> u64 {
378 let width = self.width() as u64;
379 let height = self.height() as u64;
380
381 (width * height) * 1_000_000 + height * 1_000 + width
382 }
383
384 #[inline]
386 pub const fn value(self) -> u8 {
387 self as u8
388 }
389
390 #[inline]
392 pub const fn width(self) -> usize {
393 match self {
394 Self::R11x27 | Self::R13x27 => 27,
395 Self::R7x43
396 | Self::R9x43
397 | Self::R11x43
398 | Self::R13x43
399 | Self::R15x43
400 | Self::R17x43 => 43,
401 Self::R7x59
402 | Self::R9x59
403 | Self::R11x59
404 | Self::R13x59
405 | Self::R15x59
406 | Self::R17x59 => 59,
407 Self::R7x77
408 | Self::R9x77
409 | Self::R11x77
410 | Self::R13x77
411 | Self::R15x77
412 | Self::R17x77 => 77,
413 Self::R7x99
414 | Self::R9x99
415 | Self::R11x99
416 | Self::R13x99
417 | Self::R15x99
418 | Self::R17x99 => 99,
419 Self::R7x139
420 | Self::R9x139
421 | Self::R11x139
422 | Self::R13x139
423 | Self::R15x139
424 | Self::R17x139 => 139,
425 }
426 }
427
428 #[inline]
430 pub const fn height(self) -> usize {
431 match self {
432 Self::R7x43 | Self::R7x59 | Self::R7x77 | Self::R7x99 | Self::R7x139 => 7,
433 Self::R9x43 | Self::R9x59 | Self::R9x77 | Self::R9x99 | Self::R9x139 => 9,
434 Self::R11x27
435 | Self::R11x43
436 | Self::R11x59
437 | Self::R11x77
438 | Self::R11x99
439 | Self::R11x139 => 11,
440 Self::R13x27
441 | Self::R13x43
442 | Self::R13x59
443 | Self::R13x77
444 | Self::R13x99
445 | Self::R13x139 => 13,
446 Self::R15x43 | Self::R15x59 | Self::R15x77 | Self::R15x99 | Self::R15x139 => 15,
447 Self::R17x43 | Self::R17x59 | Self::R17x77 | Self::R17x99 | Self::R17x139 => 17,
448 }
449 }
450}
451
452#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
456#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
457#[non_exhaustive]
458pub enum SymbolVersion {
459 #[cfg(feature = "qr")]
461 Qr(QrVersion),
462 #[cfg(feature = "micro-qr")]
464 Micro(MicroVersion),
465 #[cfg(feature = "rmqr")]
467 Rmqr(RmqrVersion),
468}
469
470#[cfg(feature = "qr")]
472#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
473pub struct QrMask(u8);
474
475#[cfg(feature = "qr")]
476impl QrMask {
477 #[inline]
479 pub const fn new(value: u8) -> Result<Self, EncodeError> {
480 if value < 8 { Ok(Self(value)) } else { Err(EncodeError::InvalidMask) }
481 }
482
483 #[inline]
485 pub const fn value(self) -> u8 {
486 self.0
487 }
488}
489
490#[cfg(feature = "micro-qr")]
492#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
493pub struct MicroMask(u8);
494
495#[cfg(feature = "micro-qr")]
496impl MicroMask {
497 #[inline]
499 pub const fn new(value: u8) -> Result<Self, EncodeError> {
500 if value < 4 { Ok(Self(value)) } else { Err(EncodeError::InvalidMask) }
501 }
502
503 #[inline]
505 pub const fn value(self) -> u8 {
506 self.0
507 }
508}
509
510#[cfg(any(feature = "qr", feature = "rmqr"))]
512#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
513pub struct EciAssignment(u32);
514
515#[cfg(any(feature = "qr", feature = "rmqr"))]
516impl EciAssignment {
517 pub const ISO_8859_1: Self = Self(3);
519 pub const SHIFT_JIS: Self = Self(20);
521 pub const UTF_8: Self = Self(26);
523
524 #[inline]
526 pub const fn new(value: u32) -> Result<Self, EncodeError> {
527 if value <= 999_999 {
528 Ok(Self(value))
529 } else {
530 Err(EncodeError::InvalidEciAssignment(value))
531 }
532 }
533
534 #[inline]
536 pub const fn value(self) -> u32 {
537 self.0
538 }
539}
540
541#[cfg(any(feature = "qr", feature = "rmqr"))]
543#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
544pub struct ApplicationIndicator(u8);
545
546#[cfg(any(feature = "qr", feature = "rmqr"))]
547impl ApplicationIndicator {
548 #[inline]
550 pub const fn numeric(value: u8) -> Result<Self, EncodeError> {
551 if value <= 99 { Ok(Self(value)) } else { Err(EncodeError::InvalidApplicationIndicator) }
552 }
553
554 #[inline]
556 pub const fn letter(value: char) -> Result<Self, EncodeError> {
557 if value.is_ascii_alphabetic() {
558 Ok(Self(value as u8 + 100))
559 } else {
560 Err(EncodeError::InvalidApplicationIndicator)
561 }
562 }
563
564 #[inline]
566 pub const fn value(self) -> u8 {
567 self.0
568 }
569}
570
571#[cfg(any(feature = "qr", feature = "rmqr"))]
573#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
574pub enum Fnc1 {
575 Gs1,
577 Industry(ApplicationIndicator),
579}
580
581#[cfg(feature = "qr")]
583#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
584pub struct StructuredAppendInfo {
585 index: u8,
586 total: u8,
587 parity: u8,
588}
589
590#[cfg(feature = "qr")]
591impl StructuredAppendInfo {
592 #[inline]
594 pub const fn index(self) -> u8 {
595 self.index
596 }
597
598 #[inline]
600 pub const fn total(self) -> u8 {
601 self.total
602 }
603
604 #[inline]
606 pub const fn parity(self) -> u8 {
607 self.parity
608 }
609}
610
611#[derive(Clone, Copy, Debug, Eq, PartialEq)]
612pub(crate) enum Mode {
613 Numeric,
614 Alphanumeric,
615 Byte,
616 #[cfg_attr(not(feature = "kanji"), allow(dead_code))]
617 Kanji,
618 #[cfg_attr(not(any(feature = "qr", feature = "rmqr")), allow(dead_code))]
619 Eci,
620}
621
622impl Mode {
623 #[cfg(feature = "qr")]
624 #[inline]
625 const fn qr_bits(self) -> u8 {
626 match self {
627 Self::Numeric => 0b0001,
628 Self::Alphanumeric => 0b0010,
629 Self::Byte => 0b0100,
630 Self::Kanji => 0b1000,
631 Self::Eci => 0b0111,
632 }
633 }
634
635 #[cfg(feature = "qr")]
636 #[inline]
637 const fn cci_bits(self, version: QrVersion) -> u8 {
638 let group = version_group(version);
639
640 match self {
641 Self::Numeric => [10, 12, 14][group],
642 Self::Alphanumeric => [9, 11, 13][group],
643 Self::Byte => [8, 16, 16][group],
644 Self::Kanji => [8, 10, 12][group],
645 Self::Eci => 0,
646 }
647 }
648}
649
650#[derive(Clone, Debug, Eq, PartialEq)]
652pub struct Segment {
653 mode: Mode,
654 character_count: usize,
655 bits: BitBuffer,
656 source: Vec<u8>,
657}
658
659impl Segment {
660 pub fn numeric(data: impl AsRef<str>) -> Result<Self, EncodeError> {
662 let data = data.as_ref();
663 let mut bits = BitBuffer::with_capacity(data.len() * 10 / 3 + 4);
664 let bytes = data.as_bytes();
665
666 for (offset, byte) in bytes.iter().enumerate() {
667 if !byte.is_ascii_digit() {
668 return Err(EncodeError::InvalidData {
669 mode: "numeric",
670 byte_offset: offset,
671 });
672 }
673 }
674
675 for chunk in bytes.chunks(3) {
676 let value = chunk.iter().fold(0, |value, byte| value * 10 + u32::from(byte - b'0'));
677 bits.append(value, [0, 4, 7, 10][chunk.len()]);
678 }
679
680 Ok(Self {
681 mode: Mode::Numeric,
682 character_count: bytes.len(),
683 bits,
684 source: bytes.to_vec(),
685 })
686 }
687
688 pub fn alphanumeric(data: impl AsRef<str>) -> Result<Self, EncodeError> {
690 let data = data.as_ref();
691 let mut values = Vec::with_capacity(data.len());
692
693 for (offset, byte) in data.bytes().enumerate() {
694 let Some(value) = alphanumeric_value(byte) else {
695 return Err(EncodeError::InvalidData {
696 mode: "alphanumeric",
697 byte_offset: offset,
698 });
699 };
700 values.push(value);
701 }
702
703 let mut bits = BitBuffer::with_capacity(values.len() * 11 / 2 + 6);
704
705 for chunk in values.chunks(2) {
706 if chunk.len() == 2 {
707 bits.append(u32::from(chunk[0]) * 45 + u32::from(chunk[1]), 11);
708 } else {
709 bits.append(u32::from(chunk[0]), 6);
710 }
711 }
712
713 Ok(Self {
714 mode: Mode::Alphanumeric,
715 character_count: values.len(),
716 bits,
717 source: data.as_bytes().to_vec(),
718 })
719 }
720
721 #[cfg(any(feature = "qr", feature = "rmqr"))]
722 fn fnc1_alphanumeric(data: &[u8]) -> Result<Self, EncodeError> {
723 let mut values = Vec::with_capacity(data.len());
724
725 for (offset, &byte) in data.iter().enumerate() {
726 if byte == 0x1D {
728 values.push(alphanumeric_value(b'%').expect("percent is alphanumeric"));
729 } else if byte == b'%' {
730 let value = alphanumeric_value(byte).expect("percent is alphanumeric");
731 values.extend_from_slice(&[value, value]);
732 } else if let Some(value) = alphanumeric_value(byte) {
733 values.push(value);
734 } else {
735 return Err(EncodeError::InvalidData {
736 mode: "FNC1 alphanumeric",
737 byte_offset: offset,
738 });
739 }
740 }
741
742 let mut bits = BitBuffer::with_capacity(values.len() * 11 / 2 + 6);
743
744 for chunk in values.chunks(2) {
745 if chunk.len() == 2 {
746 bits.append(u32::from(chunk[0]) * 45 + u32::from(chunk[1]), 11);
747 } else {
748 bits.append(u32::from(chunk[0]), 6);
749 }
750 }
751
752 Ok(Self {
753 mode: Mode::Alphanumeric,
754 character_count: values.len(),
755 bits,
756 source: data.to_vec(),
757 })
758 }
759
760 pub fn bytes(data: impl AsRef<[u8]>) -> Self {
762 let data = data.as_ref();
763
764 Self {
766 mode: Mode::Byte,
767 character_count: data.len(),
768 bits: BitBuffer::from_bytes(data.to_vec()),
769 source: Vec::new(),
770 }
771 }
772
773 #[cfg(any(feature = "qr", feature = "rmqr"))]
775 fn source_bytes(&self) -> &[u8] {
776 match self.mode {
777 Mode::Byte => self.bits.as_bytes(),
778 _ => &self.source,
779 }
780 }
781
782 #[cfg(any(feature = "qr", feature = "rmqr"))]
784 pub fn eci(assignment: EciAssignment) -> Self {
785 let mut bits = BitBuffer::with_capacity(24);
786 let value = assignment.0;
787
788 if value < 128 {
790 bits.append(value, 8);
791 } else if value < 16_384 {
792 bits.append(0b10, 2);
793 bits.append(value, 14);
794 } else {
795 bits.append(0b110, 3);
796 bits.append(value, 21);
797 }
798
799 Self {
800 mode: Mode::Eci,
801 character_count: 0,
802 bits,
803 source: Vec::new(),
804 }
805 }
806
807 #[cfg(feature = "kanji")]
808 pub fn kanji(data: impl AsRef<str>) -> Result<Self, EncodeError> {
812 let data = data.as_ref();
813 let mut bits = BitBuffer::with_capacity(data.chars().count() * 13);
814 let mut source = Vec::with_capacity(data.len() * 2);
815 let mut count = 0;
816
817 for (offset, character) in data.char_indices() {
818 let Some((value, encoded)) = kanji_encoding(character) else {
819 return Err(EncodeError::InvalidData {
820 mode: "Kanji", byte_offset: offset
821 });
822 };
823
824 bits.append(u32::from(value), 13);
825 source.extend_from_slice(&encoded);
826 count += 1;
827 }
828 Ok(Self {
829 mode: Mode::Kanji,
830 character_count: count,
831 bits,
832 source,
833 })
834 }
835
836 #[inline]
838 pub const fn character_count(&self) -> usize {
839 self.character_count
840 }
841}
842
843#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
845#[inline]
846pub(crate) const fn is_latin1_character(character: char) -> bool {
847 matches!(character as u32, 0x00..=0x7F | 0xA0..=0xFF)
848}
849
850#[cfg(any(feature = "micro-qr", all(feature = "kanji", any(feature = "qr", feature = "rmqr"))))]
853#[inline]
854pub(crate) const fn is_legacy_byte_character(character: char) -> bool {
855 is_latin1_character(character) && !matches!(character as u32, 0xE0..=0xEB)
856}
857
858#[cfg(any(feature = "qr", feature = "rmqr"))]
860fn normalize_fnc1_segments(
861 segments: &[Segment],
862 fnc1: bool,
863) -> Result<Cow<'_, [Segment]>, EncodeError> {
864 if !fnc1 {
865 return Ok(Cow::Borrowed(segments));
866 }
867
868 segments
869 .iter()
870 .map(|segment| {
871 if segment.mode == Mode::Alphanumeric {
872 Segment::fnc1_alphanumeric(segment.source_bytes())
873 } else {
874 Ok(segment.clone())
875 }
876 })
877 .collect::<Result<Vec<_>, _>>()
878 .map(Cow::Owned)
879}
880
881#[cfg(feature = "kanji")]
883fn shift_jis_encoding(character: char) -> Option<([u8; 2], usize)> {
884 let mut utf8 = [0; 4];
885 let text = character.encode_utf8(&mut utf8);
886 let (encoded, _, had_errors) = encoding_rs::SHIFT_JIS.encode(text);
887
888 if had_errors {
889 return None;
890 }
891
892 match *encoded {
893 [byte @ (0x5C | 0x7E)] => (!character.is_ascii()).then_some(([byte, 0], 1)),
895 [byte @ (0x00..=0x7F | 0xA1..=0xDF)] => Some(([byte, 0], 1)),
897 [lead, trail] if is_unambiguous_jis_x_0208(u16::from_be_bytes([lead, trail])) => {
898 Some(([lead, trail], 2))
899 },
900 _ => None,
901 }
902}
903
904#[cfg(feature = "kanji")]
908#[inline]
909const fn is_unambiguous_jis_x_0208(value: u16) -> bool {
910 matches!(value, 0x8140..=0x84BE | 0x889F..=0x9FFC | 0xE040..=0xEAA4)
911 && !matches!(value, 0x815C | 0x815F | 0x8160 | 0x8161 | 0x817C | 0x8191 | 0x8192 | 0x81CA)
912}
913
914#[cfg(feature = "kanji")]
915fn kanji_encoding(character: char) -> Option<(u16, [u8; 2])> {
916 let (bytes, length) = shift_jis_encoding(character)?;
917
918 if length != 2 {
919 return None;
920 }
921
922 let value = u16::from_be_bytes(bytes);
923
924 let adjusted = match value {
926 0x8140..=0x9FFC => value - 0x8140,
927 0xE040..=0xEBBF => value - 0xC140,
928 _ => return None,
929 };
930
931 Some(((adjusted >> 8) * 0xC0 + (adjusted & 0xFF), bytes))
932}
933
934#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
936#[inline]
937const fn mode_rank(mode: Mode) -> u8 {
938 match mode {
939 Mode::Numeric => 0,
940 Mode::Alphanumeric => 1,
941 Mode::Kanji => 2,
942 Mode::Byte => 3,
943 Mode::Eci => 4,
944 }
945}
946
947#[inline]
948pub(crate) const fn alphanumeric_value(byte: u8) -> Option<u8> {
949 match byte {
950 b'0'..=b'9' => Some(byte - b'0'),
951 b'A'..=b'Z' => Some(byte - b'A' + 10),
952 b' ' => Some(36),
953 b'$' => Some(37),
954 b'%' => Some(38),
955 b'*' => Some(39),
956 b'+' => Some(40),
957 b'-' => Some(41),
958 b'.' => Some(42),
959 b'/' => Some(43),
960 b':' => Some(44),
961 _ => None,
962 }
963}
964
965#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
968#[inline]
969pub(crate) const fn bch_remainder(data: u32, generator: u32, degree: u32) -> u32 {
970 let mut remainder = data;
971 let mut round = 0;
972
973 while round < degree {
974 remainder = (remainder << 1) ^ ((remainder >> (degree - 1)) * generator);
975 round += 1;
976 }
977
978 remainder
979}
980
981#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
983#[derive(Clone, Debug, Eq, PartialEq)]
984pub struct Symbol {
985 pub(crate) version: SymbolVersion,
986 pub(crate) error_correction: SymbolErrorCorrection,
987 pub(crate) mask: u8,
988 pub(crate) modules: Vec<bool>,
989 #[cfg(feature = "qr")]
990 pub(crate) structured_append: Option<StructuredAppendInfo>,
991}
992
993#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
994impl Symbol {
995 #[inline]
999 pub const fn size(&self) -> usize {
1000 self.width()
1001 }
1002
1003 #[inline]
1005 pub const fn width(&self) -> usize {
1006 match self.version {
1007 #[cfg(feature = "qr")]
1008 SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
1009 #[cfg(feature = "micro-qr")]
1010 SymbolVersion::Micro(version) => version.size(),
1011 #[cfg(feature = "rmqr")]
1012 SymbolVersion::Rmqr(version) => version.width(),
1013 }
1014 }
1015
1016 #[inline]
1018 pub const fn height(&self) -> usize {
1019 match self.version {
1020 #[cfg(feature = "qr")]
1021 SymbolVersion::Qr(version) => version.0 as usize * 4 + 17,
1022 #[cfg(feature = "micro-qr")]
1023 SymbolVersion::Micro(version) => version.size(),
1024 #[cfg(feature = "rmqr")]
1025 SymbolVersion::Rmqr(version) => version.height(),
1026 }
1027 }
1028
1029 #[inline]
1031 pub const fn version(&self) -> SymbolVersion {
1032 self.version
1033 }
1034
1035 #[inline]
1037 pub const fn error_correction(&self) -> SymbolErrorCorrection {
1038 self.error_correction
1039 }
1040
1041 #[inline]
1043 pub const fn mask(&self) -> u8 {
1044 self.mask
1045 }
1046
1047 #[cfg(feature = "qr")]
1049 #[inline]
1050 pub const fn structured_append(&self) -> Option<StructuredAppendInfo> {
1051 self.structured_append
1052 }
1053
1054 #[inline]
1056 pub fn module(&self, x: usize, y: usize) -> Option<bool> {
1057 let width = self.width();
1058 (x < width && y < self.height()).then(|| self.modules[y * width + x])
1059 }
1060
1061 #[inline]
1065 pub fn modules(&self) -> &[bool] {
1066 &self.modules
1067 }
1068
1069 pub fn to_matrix(&self) -> Vec<Vec<bool>> {
1071 self.modules.chunks(self.width()).map(<[bool]>::to_vec).collect()
1072 }
1073}
1074
1075pub trait ToQRText {
1080 fn to_qr_text(&self) -> String;
1082}
1083
1084#[cfg(feature = "qr")]
1086#[derive(Clone, Debug)]
1087pub struct QrEncoder {
1088 error_correction: QrErrorCorrection,
1089 versions: RangeInclusive<QrVersion>,
1090 mask: Option<QrMask>,
1091 boost_error_correction: bool,
1092 fnc1: Option<Fnc1>,
1093}
1094
1095#[cfg(feature = "qr")]
1096impl QrEncoder {
1097 #[inline]
1099 pub const fn new(error_correction: QrErrorCorrection) -> Self {
1100 Self {
1101 error_correction,
1102 versions: QrVersion::MIN..=QrVersion::MAX,
1103 mask: None,
1104 boost_error_correction: true,
1105 fnc1: None,
1106 }
1107 }
1108
1109 #[must_use]
1111 #[inline]
1112 pub const fn version(mut self, version: QrVersion) -> Self {
1113 self.versions = version..=version;
1114 self
1115 }
1116
1117 #[must_use]
1119 #[inline]
1120 pub const fn version_range(mut self, versions: RangeInclusive<QrVersion>) -> Self {
1121 self.versions = versions;
1122 self
1123 }
1124
1125 #[must_use]
1127 #[inline]
1128 pub const fn mask(mut self, mask: QrMask) -> Self {
1129 self.mask = Some(mask);
1130 self
1131 }
1132
1133 #[must_use]
1135 #[inline]
1136 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1137 self.boost_error_correction = boost;
1138 self
1139 }
1140
1141 #[must_use]
1143 #[inline]
1144 pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
1145 self.fnc1 = fnc1;
1146 self
1147 }
1148
1149 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
1151 let data = data.as_ref();
1152 let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1153
1154 ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
1155
1156 self.encode_optimized_bytes(data, None)
1157 }
1158
1159 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
1161 let text = text.as_ref();
1162 let (capacity, capacity_bits) = self.input_capacity_upper_bound(false)?;
1163
1164 ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
1165
1166 self.encode_optimized_text(text, None, optimizer::Start::Free)
1167 }
1168
1169 #[inline]
1171 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
1172 &self,
1173 value: &T,
1174 ) -> Result<Symbol, EncodeError> {
1175 let text = value.to_qr_text();
1176 self.encode_text(&text)
1177 }
1178
1179 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
1183 self.encode_segments_with_header(segments, None)
1184 }
1185
1186 pub fn encode_structured_append_bytes(
1188 &self,
1189 parts: &[&[u8]],
1190 ) -> Result<Vec<Symbol>, EncodeError> {
1191 validate_part_count(parts.len())?;
1192 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1193
1194 for part in parts {
1195 ensure_input_length(part.len(), capacity, capacity_bits, 1)?;
1196 }
1197
1198 let parity = parts
1199 .iter()
1200 .flat_map(|part| part.iter().copied())
1201 .fold(0, |parity, byte| parity ^ byte);
1202
1203 parts
1204 .iter()
1205 .enumerate()
1206 .map(|(index, part)| {
1207 self.encode_optimized_bytes(
1208 part,
1209 Some(StructuredAppendInfo {
1210 index: index as u8,
1211 total: parts.len() as u8,
1212 parity,
1213 }),
1214 )
1215 })
1216 .collect()
1217 }
1218
1219 pub fn encode_structured_append_text(
1221 &self,
1222 parts: &[&str],
1223 ) -> Result<Vec<Symbol>, EncodeError> {
1224 validate_part_count(parts.len())?;
1225 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1226
1227 for part in parts {
1228 ensure_input_length(part.chars().count(), capacity, capacity_bits, 1)?;
1229 }
1230
1231 let total = parts.len() as u8;
1232 let mut selected: Option<SequencePlan> = None;
1233
1234 for regime in SequenceRegime::ALL {
1236 if let Some(candidate) = self.structured_text_plans(parts, regime)?
1237 && selected.as_ref().is_none_or(|selected| {
1238 (candidate.version, candidate.area) < (selected.version, selected.area)
1239 })
1240 {
1241 selected = Some(candidate);
1242 }
1243 }
1244
1245 let Some(SequencePlan {
1246 parts: plans, ..
1247 }) = selected
1248 else {
1249 return Err(self.structured_text_error(parts));
1250 };
1251
1252 let parity = plans
1254 .iter()
1255 .flat_map(|(_, segments)| segments)
1256 .flat_map(|segment| segment.source_bytes().iter().copied())
1257 .fold(0, |parity, byte| parity ^ byte);
1258
1259 plans
1260 .into_iter()
1261 .enumerate()
1262 .map(|(index, (version, segments))| {
1263 model2::encode(
1264 &segments,
1265 version,
1266 self.error_correction,
1267 self.mask,
1268 self.boost_error_correction,
1269 self.fnc1,
1270 Some(StructuredAppendInfo {
1271 index: index as u8,
1272 total,
1273 parity,
1274 }),
1275 )
1276 })
1277 .collect()
1278 }
1279
1280 fn qr_structured_plan(
1282 &self,
1283 text: &str,
1284 start: optimizer::Start,
1285 ) -> Result<Option<(QrVersion, Vec<Segment>)>, EncodeError> {
1286 if self.versions.start() > self.versions.end() {
1287 return Err(EncodeError::InvalidVersionRange);
1288 }
1289
1290 let header = StructuredAppendInfo {
1292 index: 0, total: 16, parity: 0
1293 };
1294 let range = self.versions.clone();
1295 let tables = optimizer::TextTables::new(text, self.fnc1.is_some());
1296 let mut cache = GroupCache::new(|version| {
1297 optimizer::text(&tables, optimizer::Profile::qr(version), start)
1298 });
1299
1300 for value in range.start().0..=range.end().0 {
1301 let version = QrVersion(value);
1302
1303 let Ok(segments) = cache.get(version) else {
1305 return Ok(None);
1306 };
1307
1308 if model2::fits(segments, version, self.error_correction, self.fnc1, Some(header)) {
1309 return Ok(Some((version, segments.to_vec())));
1310 }
1311 }
1312
1313 Ok(None)
1314 }
1315
1316 fn structured_text_plans(
1319 &self,
1320 parts: &[&str],
1321 regime: SequenceRegime,
1322 ) -> Result<Option<SequencePlan>, EncodeError> {
1323 if !regime.suits(&parts.concat()) {
1324 return Ok(None);
1325 }
1326
1327 let mut options = Vec::with_capacity(parts.len());
1329
1330 for (index, part) in parts.iter().enumerate() {
1331 let mut by_state: [Vec<(QrVersion, Vec<Segment>, bool)>; 2] = [Vec::new(), Vec::new()];
1332
1333 for eci_in_force in [false, true] {
1334 if eci_in_force && (index == 0 || regime != SequenceRegime::Standard) {
1336 continue;
1337 }
1338
1339 for &(start, _) in regime.starts(eci_in_force) {
1340 if let Some((version, segments)) = self.qr_structured_plan(part, start)? {
1341 let leaves_eci = eci_in_force
1342 || segments.iter().any(|segment| segment.mode == Mode::Eci);
1343
1344 by_state[usize::from(eci_in_force)].push((version, segments, leaves_eci));
1345 }
1346 }
1347 }
1348
1349 options.push(by_state);
1350 }
1351
1352 let mut caps: Vec<QrVersion> = options
1353 .iter()
1354 .flat_map(|by_state| by_state.iter().flatten().map(|(version, ..)| *version))
1355 .collect();
1356
1357 caps.sort_unstable();
1358 caps.dedup();
1359
1360 for cap in caps {
1361 let mut best: Vec<[Option<(usize, usize, usize)>; 2]> =
1363 vec![[None; 2]; parts.len() + 1];
1364
1365 best[0][0] = Some((0, 0, 0));
1366
1367 for (index, by_state) in options.iter().enumerate() {
1368 for eci_in_force in [false, true] {
1369 let Some((area, ..)) = best[index][usize::from(eci_in_force)] else {
1370 continue;
1371 };
1372
1373 for (choice, (version, _, leaves_eci)) in
1374 by_state[usize::from(eci_in_force)].iter().enumerate()
1375 {
1376 if *version > cap {
1377 continue;
1378 }
1379
1380 let size = usize::from(version.value()) * 4 + 17;
1381 let candidate = (area + size * size, usize::from(eci_in_force), choice);
1382 let slot = &mut best[index + 1][usize::from(*leaves_eci)];
1383
1384 if slot.is_none_or(|current| candidate.0 < current.0) {
1385 *slot = Some(candidate);
1386 }
1387 }
1388 }
1389 }
1390
1391 let Some((mut state, area)) = (0..2)
1392 .filter_map(|state| best[parts.len()][state].map(|(area, ..)| (state, area)))
1393 .min_by_key(|&(_, area)| area)
1394 else {
1395 continue;
1396 };
1397
1398 let mut plans = Vec::with_capacity(parts.len());
1399
1400 for index in (0..parts.len()).rev() {
1401 let (_, previous, choice) = best[index + 1][state].expect("the path is reachable");
1402 let (version, segments, _) = options[index][previous][choice].clone();
1403
1404 plans.push((version, segments));
1405 state = previous;
1406 }
1407
1408 plans.reverse();
1409
1410 return Ok(Some(SequencePlan {
1411 version: cap,
1412 area,
1413 parts: plans,
1414 }));
1415 }
1416
1417 Ok(None)
1418 }
1419
1420 fn structured_text_error(&self, parts: &[&str]) -> EncodeError {
1422 let version = *self.versions.end();
1423 let capacity_error = EncodeError::DataTooLong {
1424 required_bits: None,
1425 capacity_bits: model2::data_codewords(version, self.error_correction) * 8,
1426 };
1427
1428 for (index, part) in parts.iter().enumerate() {
1430 let start =
1431 if index == 0 { optimizer::Start::Default } else { optimizer::Start::Explicit };
1432 let tables = optimizer::TextTables::new(part, self.fnc1.is_some());
1433 let Ok(segments) = optimizer::text(&tables, optimizer::Profile::qr(version), start)
1434 else {
1435 continue;
1436 };
1437
1438 if let Err(error) = model2::encode(
1439 &segments,
1440 version,
1441 self.error_correction,
1442 self.mask,
1443 false,
1444 self.fnc1,
1445 Some(StructuredAppendInfo {
1446 index: index as u8,
1447 total: parts.len() as u8,
1448 parity: 0,
1449 }),
1450 ) {
1451 return error;
1452 }
1453 }
1454
1455 capacity_error
1456 }
1457
1458 pub fn encode_structured_append_segments(
1474 &self,
1475 parts: &[&[Segment]],
1476 ) -> Result<Vec<Symbol>, EncodeError> {
1477 validate_part_count(parts.len())?;
1478
1479 let parity = parts
1480 .iter()
1481 .flat_map(|part| part.iter())
1482 .flat_map(|segment| segment.source_bytes().iter().copied())
1483 .fold(0, |parity, byte| parity ^ byte);
1484
1485 parts
1486 .iter()
1487 .enumerate()
1488 .map(|(index, part)| {
1489 self.encode_segments_with_header(
1490 part,
1491 Some(StructuredAppendInfo {
1492 index: index as u8,
1493 total: parts.len() as u8,
1494 parity,
1495 }),
1496 )
1497 })
1498 .collect()
1499 }
1500
1501 pub fn encode_bytes_with_structured_append(
1505 &self,
1506 data: impl AsRef<[u8]>,
1507 ) -> Result<Vec<Symbol>, EncodeError> {
1508 let data = data.as_ref();
1509
1510 match self.encode_bytes(data) {
1511 Ok(symbol) => return Ok(vec![symbol]),
1512 Err(EncodeError::DataTooLong {
1513 ..
1514 }) => {},
1515 Err(error) => return Err(error),
1516 }
1517
1518 let range = self.structured_append_range()?;
1519 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1520
1521 ensure_input_length(data.len(), capacity, capacity_bits, 16)?;
1522
1523 let sequence_capacity_error = || EncodeError::DataTooLong {
1525 required_bits: None,
1526 capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1527 };
1528
1529 let reach = self.qr_character_upper_bound(*range.end());
1530 let mut costs = PrefixCosts::new(|start: usize, version, ()| {
1531 let end = data.len().min(start.saturating_add(reach));
1532
1533 optimizer::byte_costs(
1534 &data[start..end],
1535 optimizer::Profile::qr(version),
1536 self.fnc1.is_some(),
1537 )
1538 });
1539
1540 let minimum_parts = self
1542 .minimum_byte_parts(&mut costs, data.len(), range.clone())
1543 .ok_or_else(sequence_capacity_error)?;
1544
1545 let candidate = smallest_version_cap(&range, |candidate| {
1546 self.minimum_byte_parts(&mut costs, data.len(), *range.start()..=candidate)
1547 == Some(minimum_parts)
1548 })
1549 .ok_or_else(sequence_capacity_error)?;
1550 let parts = self
1551 .minimum_area_byte_partition(
1552 &mut costs,
1553 data.len(),
1554 minimum_parts,
1555 *range.start()..=candidate,
1556 )
1557 .map_err(|_| sequence_capacity_error())?;
1558
1559 let slices: Vec<&[u8]> = parts.into_iter().map(|(start, end)| &data[start..end]).collect();
1560
1561 self.encode_structured_append_bytes(&slices)
1562 }
1563
1564 pub fn encode_text_with_structured_append(
1568 &self,
1569 text: impl AsRef<str>,
1570 ) -> Result<Vec<Symbol>, EncodeError> {
1571 let text = text.as_ref();
1572
1573 match self.encode_text(text) {
1574 Ok(symbol) => return Ok(vec![symbol]),
1575 Err(EncodeError::DataTooLong {
1576 ..
1577 }) => {},
1578 Err(error) => return Err(error),
1579 }
1580
1581 let range = self.structured_append_range()?;
1582 let (capacity, capacity_bits) = self.input_capacity_upper_bound(true)?;
1583
1584 ensure_input_length(text.chars().count(), capacity, capacity_bits, 16)?;
1585
1586 let input = SplitText::new(text);
1587 let reach = self.qr_character_upper_bound(*range.end());
1588 let mut costs = PrefixCosts::new(|start: usize, version, mode| {
1589 let end = input.len().min(start.saturating_add(reach));
1590
1591 optimizer::text_costs(
1592 input.slice(start, end),
1593 optimizer::Profile::qr(version),
1594 self.fnc1.is_some(),
1595 mode,
1596 )
1597 });
1598
1599 let sequence_capacity_error = || EncodeError::DataTooLong {
1601 required_bits: None,
1602 capacity_bits: model2::data_codewords(*range.end(), self.error_correction) * 8 * 16,
1603 };
1604
1605 let mut selected: Option<SequencePartition> = None;
1608
1609 for regime in SequenceRegime::ALL {
1610 if !regime.suits(text) {
1611 continue;
1612 }
1613
1614 let Some(minimum_parts) =
1616 self.minimum_text_parts(&input, &mut costs, range.clone(), regime)
1617 else {
1618 continue;
1619 };
1620
1621 if selected.as_ref().is_some_and(|selected| minimum_parts > selected.count) {
1622 continue;
1623 }
1624
1625 let Some(candidate) = smallest_version_cap(&range, |candidate| {
1626 self.minimum_text_parts(&input, &mut costs, *range.start()..=candidate, regime)
1627 == Some(minimum_parts)
1628 }) else {
1629 continue;
1630 };
1631
1632 let (area, parts) = self
1633 .minimum_area_text_partition(
1634 &input,
1635 &mut costs,
1636 minimum_parts,
1637 *range.start()..=candidate,
1638 regime,
1639 )
1640 .map_err(|_| sequence_capacity_error())?;
1641
1642 if selected.as_ref().is_none_or(|selected| {
1643 (minimum_parts, candidate, area) < (selected.count, selected.version, selected.area)
1644 }) {
1645 selected = Some(SequencePartition {
1646 count: minimum_parts,
1647 version: candidate,
1648 area,
1649 parts,
1650 });
1651 }
1652 }
1653
1654 let parts = selected.ok_or_else(sequence_capacity_error)?.parts;
1655
1656 let slices: Vec<&str> =
1657 parts.into_iter().map(|(start, end)| input.slice(start, end)).collect();
1658 self.encode_structured_append_text(&slices)
1659 }
1660
1661 fn structured_append_range(&self) -> Result<RangeInclusive<QrVersion>, EncodeError> {
1662 if self.versions.start() > self.versions.end() {
1663 Err(EncodeError::InvalidVersionRange)
1664 } else {
1665 Ok(self.versions.clone())
1666 }
1667 }
1668
1669 fn input_capacity_upper_bound(
1670 &self,
1671 structured_append: bool,
1672 ) -> Result<(usize, usize), EncodeError> {
1673 if self.versions.start() > self.versions.end() {
1674 return Err(EncodeError::InvalidVersionRange);
1675 }
1676
1677 let version = *self.versions.end();
1678 let capacity_bits = model2::data_codewords(version, self.error_correction) * 8;
1679 let overhead_bits = 4
1680 + usize::from(Mode::Numeric.cci_bits(version))
1681 + usize::from(structured_append) * 20
1682 + match self.fnc1 {
1683 Some(Fnc1::Gs1) => 4,
1684 Some(Fnc1::Industry(_)) => 12,
1685 None => 0,
1686 };
1687
1688 Ok((numeric_character_capacity(capacity_bits, overhead_bits), capacity_bits))
1689 }
1690
1691 fn minimum_byte_parts<F>(
1693 &self,
1694 costs: &mut PrefixCosts<(), F>,
1695 length: usize,
1696 range: RangeInclusive<QrVersion>,
1697 ) -> Option<usize>
1698 where
1699 F: FnMut(usize, QrVersion, ()) -> Vec<Option<usize>>, {
1700 let mut start = 0;
1701 let mut count = 0;
1702
1703 while start < length {
1704 if count == 16 {
1705 return None;
1706 }
1707
1708 start = self.farthest_part_end(costs, start, (), range.clone())?;
1709 count += 1;
1710 }
1711
1712 Some(count)
1713 }
1714
1715 fn farthest_part_end<K, F>(
1717 &self,
1718 costs: &mut PrefixCosts<K, F>,
1719 start: usize,
1720 key: K,
1721 range: RangeInclusive<QrVersion>,
1722 ) -> Option<usize>
1723 where
1724 K: Copy + Ord,
1725 F: FnMut(usize, QrVersion, K) -> Vec<Option<usize>>, {
1726 let header_bits = model2::header_bits(self.fnc1, true);
1727 let mut result = None;
1728
1729 for value in range.start().value()..=range.end().value() {
1730 let version = QrVersion(value);
1731 let capacity_bits = model2::data_codewords(version, self.error_correction) * 8;
1732
1733 let count = costs.get(start, version, key)[1..].partition_point(|cost| {
1735 cost.is_some_and(|bits| header_bits + bits <= capacity_bits)
1736 });
1737
1738 if count > 0 {
1739 result = result.max(Some(start + count));
1740 }
1741 }
1742
1743 result
1744 }
1745
1746 fn minimum_text_parts<F>(
1748 &self,
1749 input: &SplitText<'_>,
1750 costs: &mut PrefixCosts<optimizer::Start, F>,
1751 range: RangeInclusive<QrVersion>,
1752 regime: SequenceRegime,
1753 ) -> Option<usize>
1754 where
1755 F: FnMut(usize, QrVersion, optimizer::Start) -> Vec<Option<usize>>, {
1756 let length = input.len();
1757 let mut layer = vec![(0, false)];
1758
1759 for count in 1..=16 {
1760 let mut farthest = [None; 2];
1761
1762 for &(start, eci_in_force) in &layer {
1763 for (end, leaves_eci) in self
1764 .text_part_ends(input, costs, start, eci_in_force, regime, range.clone())
1765 .into_iter()
1766 .flatten()
1767 {
1768 if end == length {
1769 return Some(count);
1770 }
1771
1772 let slot = &mut farthest[usize::from(leaves_eci)];
1773
1774 if slot.is_none_or(|current| end > current) {
1775 *slot = Some(end);
1776 }
1777 }
1778 }
1779
1780 layer.clear();
1782 layer.extend(farthest[0].map(|end| (end, false)));
1783
1784 if let Some(end) = farthest[1]
1785 && farthest[0].is_none_or(|free| end > free)
1786 {
1787 layer.push((end, true));
1788 }
1789
1790 if layer.is_empty() {
1791 return None;
1792 }
1793 }
1794
1795 None
1796 }
1797
1798 fn text_part_ends<F>(
1801 &self,
1802 input: &SplitText<'_>,
1803 costs: &mut PrefixCosts<optimizer::Start, F>,
1804 start: usize,
1805 eci_in_force: bool,
1806 regime: SequenceRegime,
1807 range: RangeInclusive<QrVersion>,
1808 ) -> [Option<(usize, bool)>; 2]
1809 where
1810 F: FnMut(usize, QrVersion, optimizer::Start) -> Vec<Option<usize>>, {
1811 let high =
1813 input.len().min(start.saturating_add(self.qr_character_upper_bound(*range.end())));
1814 let mut result = [None; 2];
1815 let mut farthest = None;
1816
1817 for (index, &(mode, leaves_eci)) in regime.starts(eci_in_force).iter().enumerate() {
1818 if leaves_eci && !eci_in_force && input.eci_useful_from[start] >= high {
1820 continue;
1821 }
1822
1823 let end = self.farthest_part_end(costs, start, mode, range.clone());
1824
1825 if let Some(end) = end
1826 && farthest.is_none_or(|farthest| end > farthest)
1827 {
1828 result[index] = Some((end, leaves_eci));
1829 farthest = Some(end);
1830 }
1831 }
1832
1833 result
1834 }
1835
1836 fn minimum_area_byte_partition<F>(
1837 &self,
1838 costs: &mut PrefixCosts<(), F>,
1839 length: usize,
1840 part_count: usize,
1841 range: RangeInclusive<QrVersion>,
1842 ) -> Result<Vec<(usize, usize)>, EncodeError>
1843 where
1844 F: FnMut(usize, QrVersion, ()) -> Vec<Option<usize>>, {
1845 let (_, parts) = minimum_area_partition(length, part_count, range, |start, _, version| {
1847 let end = self.farthest_part_end(costs, start, (), version..=version);
1848
1849 [end.map(|end| (end, false)), None]
1850 })?;
1851
1852 Ok(parts)
1853 }
1854
1855 fn minimum_area_text_partition<F>(
1856 &self,
1857 input: &SplitText<'_>,
1858 costs: &mut PrefixCosts<optimizer::Start, F>,
1859 part_count: usize,
1860 range: RangeInclusive<QrVersion>,
1861 regime: SequenceRegime,
1862 ) -> Result<(usize, Vec<(usize, usize)>), EncodeError>
1863 where
1864 F: FnMut(usize, QrVersion, optimizer::Start) -> Vec<Option<usize>>, {
1865 minimum_area_partition(input.len(), part_count, range, |start, eci_in_force, version| {
1866 self.text_part_ends(input, costs, start, eci_in_force, regime, version..=version)
1867 })
1868 }
1869
1870 #[inline]
1871 const fn qr_character_upper_bound(&self, version: QrVersion) -> usize {
1872 model2::data_codewords(version, self.error_correction) * 8 * 3 / 10 + 2
1873 }
1874
1875 fn encode_optimized_bytes(
1876 &self,
1877 data: &[u8],
1878 structured_append: Option<StructuredAppendInfo>,
1879 ) -> Result<Symbol, EncodeError> {
1880 self.encode_qr_range(
1881 self.versions.clone(),
1882 |version| optimizer::bytes(data, optimizer::Profile::qr(version), self.fnc1.is_some()),
1883 structured_append,
1884 )
1885 }
1886
1887 fn encode_optimized_text(
1888 &self,
1889 text: &str,
1890 structured_append: Option<StructuredAppendInfo>,
1891 start: optimizer::Start,
1892 ) -> Result<Symbol, EncodeError> {
1893 let tables = optimizer::TextTables::new(text, self.fnc1.is_some());
1894
1895 self.encode_qr_range(
1896 self.versions.clone(),
1897 |version| optimizer::text(&tables, optimizer::Profile::qr(version), start),
1898 structured_append,
1899 )
1900 }
1901
1902 fn encode_qr_range<F>(
1903 &self,
1904 range: RangeInclusive<QrVersion>,
1905 segments: F,
1906 structured_append: Option<StructuredAppendInfo>,
1907 ) -> Result<Symbol, EncodeError>
1908 where
1909 F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>, {
1910 if range.start() > range.end() {
1911 return Err(EncodeError::InvalidVersionRange);
1912 }
1913
1914 let mut cache = GroupCache::new(segments);
1915 let mut last_error = None;
1916
1917 for value in range.start().0..=range.end().0 {
1918 let version = QrVersion(value);
1919 let candidate = cache.get(version)?;
1920
1921 match model2::encode(
1922 candidate,
1923 version,
1924 self.error_correction,
1925 self.mask,
1926 self.boost_error_correction,
1927 self.fnc1,
1928 structured_append,
1929 ) {
1930 Ok(symbol) => return Ok(symbol),
1931 Err(
1932 error @ EncodeError::DataTooLong {
1933 ..
1934 },
1935 ) => last_error = Some(error),
1936 Err(error) => return Err(error),
1937 }
1938 }
1939
1940 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
1942 }
1943
1944 fn encode_segments_with_header(
1945 &self,
1946 segments: &[Segment],
1947 structured_append: Option<StructuredAppendInfo>,
1948 ) -> Result<Symbol, EncodeError> {
1949 let segments = normalize_fnc1_segments(segments, self.fnc1.is_some())?;
1950
1951 self.encode_qr_range(self.versions.clone(), |_| Ok(segments.to_vec()), structured_append)
1952 }
1953}
1954
1955#[cfg(feature = "rmqr")]
1957#[derive(Clone, Debug)]
1958pub struct RmqrEncoder {
1959 error_correction: RmqrErrorCorrection,
1960 versions: RangeInclusive<RmqrVersion>,
1961 boost_error_correction: bool,
1962 fnc1: Option<Fnc1>,
1963}
1964
1965#[cfg(feature = "rmqr")]
1966impl RmqrEncoder {
1967 #[inline]
1969 pub const fn new(error_correction: RmqrErrorCorrection) -> Self {
1970 Self {
1971 error_correction,
1972 versions: RmqrVersion::MIN..=RmqrVersion::MAX,
1973 boost_error_correction: true,
1974 fnc1: None,
1975 }
1976 }
1977
1978 #[must_use]
1980 #[inline]
1981 pub const fn version(mut self, version: RmqrVersion) -> Self {
1982 self.versions = version..=version;
1983 self
1984 }
1985
1986 #[must_use]
1988 #[inline]
1989 pub const fn version_range(mut self, versions: RangeInclusive<RmqrVersion>) -> Self {
1990 self.versions = versions;
1991 self
1992 }
1993
1994 #[must_use]
1996 #[inline]
1997 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
1998 self.boost_error_correction = boost;
1999 self
2000 }
2001
2002 #[must_use]
2004 #[inline]
2005 pub const fn fnc1(mut self, fnc1: Option<Fnc1>) -> Self {
2006 self.fnc1 = fnc1;
2007 self
2008 }
2009
2010 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2012 let data = data.as_ref();
2013 let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2014
2015 ensure_input_length(data.len(), capacity, capacity_bits, 1)?;
2016 self.encode_optimized(|version| {
2017 optimizer::bytes(
2018 data,
2019 optimizer::Profile::rmqr(rmqr::cci(version)),
2020 self.fnc1.is_some(),
2021 )
2022 })
2023 }
2024
2025 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2027 let text = text.as_ref();
2028 let (capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2029
2030 ensure_input_length(text.chars().count(), capacity, capacity_bits, 1)?;
2031
2032 let tables = optimizer::TextTables::new(text, self.fnc1.is_some());
2033
2034 self.encode_optimized(|version| {
2035 optimizer::text(
2036 &tables,
2037 optimizer::Profile::rmqr(rmqr::cci(version)),
2038 optimizer::Start::Free,
2039 )
2040 })
2041 }
2042
2043 #[inline]
2045 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2046 &self,
2047 value: &T,
2048 ) -> Result<Symbol, EncodeError> {
2049 let text = value.to_qr_text();
2050 self.encode_text(&text)
2051 }
2052
2053 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2057 let segments = normalize_fnc1_segments(segments, self.fnc1.is_some())?;
2058
2059 self.encode_candidates(|version| {
2060 rmqr::encode(
2061 &segments,
2062 version,
2063 self.error_correction,
2064 self.boost_error_correction,
2065 self.fnc1,
2066 )
2067 })
2068 }
2069
2070 fn input_capacity_upper_bound(&self) -> Result<(usize, usize), EncodeError> {
2071 let versions = self.candidates()?;
2072 let mut result = None;
2073
2074 for version in versions {
2075 let capacity_bits = rmqr::data_codewords(version, self.error_correction) * 8;
2076 let overhead_bits = 3
2077 + usize::from(rmqr::cci(version)[0])
2078 + match self.fnc1 {
2079 Some(Fnc1::Gs1) => 3,
2080 Some(Fnc1::Industry(_)) => 11,
2081 None => 0,
2082 };
2083 let capacity = numeric_character_capacity(capacity_bits, overhead_bits);
2084
2085 if result.is_none_or(|(best, _)| capacity > best) {
2086 result = Some((capacity, capacity_bits));
2087 }
2088 }
2089
2090 result.ok_or(EncodeError::InvalidVersionRange)
2091 }
2092
2093 fn encode_optimized<F>(&self, mut optimize: F) -> Result<Symbol, EncodeError>
2095 where
2096 F: FnMut(RmqrVersion) -> Result<Vec<Segment>, EncodeError>, {
2097 let mut cache: Vec<([u8; 4], Vec<Segment>)> = Vec::with_capacity(15);
2098
2099 self.encode_candidates(|version| {
2100 let profile = rmqr::cci(version);
2101 let index = match cache.iter().position(|(cci, _)| *cci == profile) {
2102 Some(index) => index,
2103 None => {
2104 cache.push((profile, optimize(version)?));
2105 cache.len() - 1
2106 },
2107 };
2108
2109 rmqr::encode(
2110 &cache[index].1,
2111 version,
2112 self.error_correction,
2113 self.boost_error_correction,
2114 self.fnc1,
2115 )
2116 })
2117 }
2118
2119 fn encode_candidates<F>(&self, mut encode: F) -> Result<Symbol, EncodeError>
2121 where
2122 F: FnMut(RmqrVersion) -> Result<Symbol, EncodeError>, {
2123 let versions = self.candidates()?;
2124 let mut last_error = None;
2125
2126 for version in versions {
2127 match encode(version) {
2128 Ok(symbol) => return Ok(symbol),
2129 Err(
2130 error @ EncodeError::DataTooLong {
2131 ..
2132 },
2133 ) => last_error = Some(error),
2134 Err(error) => return Err(error),
2135 }
2136 }
2137
2138 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
2139 }
2140
2141 fn candidates(&self) -> Result<Vec<RmqrVersion>, EncodeError> {
2142 if self.versions.start() > self.versions.end() {
2143 return Err(EncodeError::InvalidVersionRange);
2144 }
2145
2146 Ok(RmqrVersion::ALL_BY_AREA
2147 .into_iter()
2148 .filter(|version| self.versions.contains(version))
2149 .collect())
2150 }
2151}
2152
2153#[cfg(feature = "micro-qr")]
2155#[derive(Clone, Debug)]
2156pub struct MicroEncoder {
2157 error_correction: MicroErrorCorrection,
2158 versions: RangeInclusive<MicroVersion>,
2159 mask: Option<MicroMask>,
2160 boost_error_correction: bool,
2161}
2162
2163#[cfg(feature = "micro-qr")]
2164impl MicroEncoder {
2165 #[inline]
2167 pub const fn new(error_correction: MicroErrorCorrection) -> Self {
2168 Self {
2169 error_correction,
2170 versions: MicroVersion::M1..=MicroVersion::M4,
2171 mask: None,
2172 boost_error_correction: true,
2173 }
2174 }
2175
2176 #[must_use]
2178 #[inline]
2179 pub const fn version(mut self, version: MicroVersion) -> Self {
2180 self.versions = version..=version;
2181 self
2182 }
2183
2184 #[must_use]
2186 #[inline]
2187 pub const fn version_range(mut self, versions: RangeInclusive<MicroVersion>) -> Self {
2188 self.versions = versions;
2189 self
2190 }
2191
2192 #[must_use]
2194 #[inline]
2195 pub const fn mask(mut self, mask: MicroMask) -> Self {
2196 self.mask = Some(mask);
2197 self
2198 }
2199
2200 #[must_use]
2202 #[inline]
2203 pub const fn boost_error_correction(mut self, boost: bool) -> Self {
2204 self.boost_error_correction = boost;
2205 self
2206 }
2207
2208 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2210 let data = data.as_ref();
2211 let (version, capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2212
2213 micro::check_bytes(data, version, capacity, capacity_bits)?;
2214
2215 self.encode_range(|version| micro::optimize(data, version))
2216 }
2217
2218 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2220 let text = text.as_ref();
2221 let (version, capacity, capacity_bits) = self.input_capacity_upper_bound()?;
2222 let input = micro::Text::new(text, version, capacity, capacity_bits)?;
2223
2224 self.encode_range(|version| micro::optimize_text(&input, version))
2225 }
2226
2227 #[inline]
2229 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2230 &self,
2231 value: &T,
2232 ) -> Result<Symbol, EncodeError> {
2233 let text = value.to_qr_text();
2234 self.encode_text(&text)
2235 }
2236
2237 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2242 self.encode_range(|_| {
2243 Ok(segments
2244 .iter()
2245 .filter(|segment| segment.mode != Mode::Numeric || segment.character_count > 0)
2246 .cloned()
2247 .collect())
2248 })
2249 }
2250
2251 fn input_capacity_upper_bound(&self) -> Result<(MicroVersion, usize, usize), EncodeError> {
2253 if self.versions.start() > self.versions.end() {
2254 return Err(EncodeError::InvalidVersionRange);
2255 }
2256
2257 micro_versions(self.versions.clone())
2258 .filter_map(|version| {
2259 micro::input_capacity_upper_bound(version, self.error_correction)
2260 .map(|(capacity, capacity_bits)| (version, capacity, capacity_bits))
2261 })
2262 .max_by_key(|&(_, capacity, _)| capacity)
2263 .ok_or(EncodeError::UnsupportedErrorCorrection {
2264 version: SymbolVersion::Micro(*self.versions.end()),
2265 error_correction: self.error_correction.into(),
2266 })
2267 }
2268
2269 fn encode_range<F>(&self, mut segments: F) -> Result<Symbol, EncodeError>
2270 where
2271 F: FnMut(MicroVersion) -> Result<Vec<Segment>, EncodeError>, {
2272 if self.versions.start() > self.versions.end() {
2273 return Err(EncodeError::InvalidVersionRange);
2274 }
2275
2276 let mut last_error = None;
2277
2278 for version in micro_versions(self.versions.clone()) {
2280 if micro::input_capacity_upper_bound(version, self.error_correction).is_none() {
2281 last_error = Some(EncodeError::UnsupportedErrorCorrection {
2282 version: SymbolVersion::Micro(version),
2283 error_correction: self.error_correction.into(),
2284 });
2285 continue;
2286 }
2287
2288 let result = segments(version).and_then(|segments| {
2289 micro::encode(
2290 &segments,
2291 version,
2292 self.error_correction,
2293 self.mask,
2294 self.boost_error_correction,
2295 )
2296 });
2297
2298 match result {
2299 Ok(symbol) => return Ok(symbol),
2300 Err(error) if candidate_rejection(&error) => last_error = Some(error),
2301 Err(error) => return Err(error),
2302 }
2303 }
2304
2305 Err(last_error.unwrap_or(EncodeError::InvalidVersionRange))
2306 }
2307}
2308
2309#[cfg(all(feature = "qr", feature = "micro-qr"))]
2311#[derive(Clone, Debug)]
2312pub struct AutoEncoder {
2313 qr: QrEncoder,
2314 micro: MicroEncoder,
2315}
2316
2317#[cfg(all(feature = "qr", feature = "micro-qr"))]
2318impl AutoEncoder {
2319 #[inline]
2321 pub const fn new(qr: QrEncoder, micro: MicroEncoder) -> Self {
2322 Self {
2323 qr,
2324 micro,
2325 }
2326 }
2327
2328 pub fn encode_bytes(&self, data: impl AsRef<[u8]>) -> Result<Symbol, EncodeError> {
2330 let data = data.as_ref();
2331 if self.qr.fnc1.is_some() {
2332 return self.qr.encode_bytes(data);
2333 }
2334 match self.micro.encode_bytes(data) {
2335 Ok(symbol) => Ok(symbol),
2336 Err(error) if candidate_rejection(&error) => self.qr.encode_bytes(data),
2337 Err(error) => Err(error),
2338 }
2339 }
2340
2341 pub fn encode_text(&self, text: impl AsRef<str>) -> Result<Symbol, EncodeError> {
2343 let text = text.as_ref();
2344 if self.qr.fnc1.is_some() {
2345 return self.qr.encode_text(text);
2346 }
2347 match self.micro.encode_text(text) {
2348 Ok(symbol) => Ok(symbol),
2349 Err(error) if candidate_rejection(&error) => self.qr.encode_text(text),
2350 Err(error) => Err(error),
2351 }
2352 }
2353
2354 #[inline]
2356 pub fn encode_to_qr_text<T: ToQRText + ?Sized>(
2357 &self,
2358 value: &T,
2359 ) -> Result<Symbol, EncodeError> {
2360 let text = value.to_qr_text();
2361 self.encode_text(&text)
2362 }
2363
2364 pub fn encode_segments(&self, segments: &[Segment]) -> Result<Symbol, EncodeError> {
2366 if self.qr.fnc1.is_some() {
2367 return self.qr.encode_segments(segments);
2368 }
2369 match self.micro.encode_segments(segments) {
2370 Ok(symbol) => Ok(symbol),
2371 Err(error) if candidate_rejection(&error) => self.qr.encode_segments(segments),
2372 Err(error) => Err(error),
2373 }
2374 }
2375}
2376
2377#[cfg(feature = "qr")]
2378#[derive(Clone, Copy)]
2379struct PartitionState {
2380 end: usize,
2381 area: usize,
2382 previous: usize,
2383 eci_in_force: bool,
2385}
2386
2387#[cfg(feature = "qr")]
2390fn minimum_area_partition<F>(
2391 length: usize,
2392 part_count: usize,
2393 range: RangeInclusive<QrVersion>,
2394 mut part_ends: F,
2395) -> Result<(usize, Vec<(usize, usize)>), EncodeError>
2396where
2397 F: FnMut(usize, bool, QrVersion) -> [Option<(usize, bool)>; 2], {
2398 let mut layers = vec![vec![PartitionState {
2400 end: 0,
2401 area: 0,
2402 previous: 0,
2403 eci_in_force: false,
2404 }]];
2405
2406 for _ in 0..part_count {
2407 let previous_layer = layers.last().expect("the initial layer exists");
2408 let mut by_end = BTreeMap::new();
2409
2410 for (previous, state) in previous_layer.iter().enumerate() {
2411 for value in range.start().value()..=range.end().value() {
2412 let version = QrVersion(value);
2413 let size = usize::from(version.value()) * 4 + 17;
2414
2415 for (end, eci_in_force) in
2416 part_ends(state.end, state.eci_in_force, version).into_iter().flatten()
2417 {
2418 let candidate = PartitionState {
2419 end,
2420 area: state.area + size * size,
2421 previous,
2422 eci_in_force,
2423 };
2424
2425 by_end
2427 .entry((end, !eci_in_force))
2428 .and_modify(|current: &mut PartitionState| {
2429 if candidate.area < current.area {
2430 *current = candidate;
2431 }
2432 })
2433 .or_insert(candidate);
2434 }
2435 }
2436 }
2437
2438 let mut best_free_area = usize::MAX;
2439 let mut best_area = usize::MAX;
2440 let mut layer = Vec::new();
2441
2442 for state in by_end.into_values().rev() {
2444 if state.eci_in_force {
2445 if state.area < best_area {
2446 best_area = state.area;
2447 layer.push(state);
2448 }
2449 } else if state.area < best_free_area {
2450 best_free_area = state.area;
2451 best_area = best_area.min(state.area);
2452 layer.push(state);
2453 }
2454 }
2455
2456 layer.reverse();
2457
2458 if layer.is_empty() {
2459 return Err(EncodeError::DataTooLong {
2460 required_bits: None, capacity_bits: 0
2461 });
2462 }
2463
2464 layers.push(layer);
2465 }
2466
2467 let (mut state_index, area) = layers[part_count]
2468 .iter()
2469 .enumerate()
2470 .filter(|(_, state)| state.end == length)
2471 .map(|(index, state)| (index, state.area))
2472 .min_by_key(|&(_, area)| area)
2473 .ok_or(EncodeError::DataTooLong {
2474 required_bits: None, capacity_bits: 0
2475 })?;
2476
2477 let mut result = Vec::with_capacity(part_count);
2478
2479 for layer_index in (1..=part_count).rev() {
2480 let state = layers[layer_index][state_index];
2481 let previous = layers[layer_index - 1][state.previous];
2482 result.push((previous.end, state.end));
2483 state_index = state.previous;
2484 }
2485
2486 result.reverse();
2487
2488 Ok((area, result))
2489}
2490
2491#[cfg(feature = "qr")]
2493fn smallest_version_cap<F>(
2494 range: &RangeInclusive<QrVersion>,
2495 mut reaches_count: F,
2496) -> Option<QrVersion>
2497where
2498 F: FnMut(QrVersion) -> bool, {
2499 let mut low = range.start().value();
2500 let mut high = range.end().value();
2501 let mut result = None;
2502
2503 while low <= high {
2504 let middle = low + (high - low) / 2;
2505
2506 if reaches_count(QrVersion(middle)) {
2507 result = Some(QrVersion(middle));
2508 high = middle - 1;
2509 } else {
2510 low = middle + 1;
2511 }
2512 }
2513
2514 result
2515}
2516
2517#[cfg(feature = "micro-qr")]
2518fn micro_versions(versions: RangeInclusive<MicroVersion>) -> impl Iterator<Item = MicroVersion> {
2519 let start = *versions.start();
2520 let end = *versions.end();
2521
2522 [MicroVersion::M1, MicroVersion::M2, MicroVersion::M3, MicroVersion::M4]
2523 .into_iter()
2524 .filter(move |version| *version >= start && *version <= end)
2525}
2526
2527#[cfg(feature = "micro-qr")]
2528#[inline]
2529const fn candidate_rejection(error: &EncodeError) -> bool {
2530 matches!(
2531 error,
2532 EncodeError::DataTooLong { .. }
2533 | EncodeError::UnsupportedMode { .. }
2534 | EncodeError::UnsupportedErrorCorrection { .. }
2535 | EncodeError::TextNotRepresentable { .. }
2536 )
2537}
2538
2539#[cfg(feature = "qr")]
2540#[inline]
2541const fn version_group(version: QrVersion) -> usize {
2542 if version.0 <= 9 {
2543 0
2544 } else if version.0 <= 26 {
2545 1
2546 } else {
2547 2
2548 }
2549}
2550
2551#[cfg(feature = "qr")]
2553struct GroupCache<F> {
2554 segments: F,
2555 cache: [Option<Vec<Segment>>; 3],
2556}
2557
2558#[cfg(feature = "qr")]
2559impl<F: FnMut(QrVersion) -> Result<Vec<Segment>, EncodeError>> GroupCache<F> {
2560 fn new(segments: F) -> Self {
2561 Self {
2562 segments,
2563 cache: [None, None, None],
2564 }
2565 }
2566
2567 fn get(&mut self, version: QrVersion) -> Result<&[Segment], EncodeError> {
2568 let group = version_group(version);
2569
2570 if self.cache[group].is_none() {
2571 self.cache[group] = Some((self.segments)(version)?);
2572 }
2573
2574 Ok(self.cache[group].as_deref().expect("the version group is cached"))
2575 }
2576}
2577
2578#[cfg(feature = "qr")]
2579#[inline]
2580const fn validate_part_count(count: usize) -> Result<(), EncodeError> {
2581 if count >= 1 && count <= 16 {
2582 Ok(())
2583 } else {
2584 Err(EncodeError::InvalidStructuredAppendPartCount {
2585 count,
2586 })
2587 }
2588}
2589
2590#[cfg(feature = "qr")]
2592struct PrefixCosts<K, F> {
2593 compute: F,
2594 costs: BTreeMap<(usize, usize, K), Vec<Option<usize>>>,
2595}
2596
2597#[cfg(feature = "qr")]
2598impl<K: Copy + Ord, F: FnMut(usize, QrVersion, K) -> Vec<Option<usize>>> PrefixCosts<K, F> {
2599 #[inline]
2600 fn new(compute: F) -> Self {
2601 Self {
2602 compute,
2603 costs: BTreeMap::new(),
2604 }
2605 }
2606
2607 fn get(&mut self, start: usize, version: QrVersion, key: K) -> &[Option<usize>] {
2609 let compute = &mut self.compute;
2610
2611 self.costs
2612 .entry((start, version_group(version), key))
2613 .or_insert_with(|| compute(start, version, key))
2614 }
2615}
2616
2617#[cfg(feature = "qr")]
2619struct SplitText<'a> {
2620 text: &'a str,
2621 offsets: Vec<usize>,
2623 eci_useful_from: Vec<usize>,
2625}
2626
2627#[cfg(feature = "qr")]
2628impl<'a> SplitText<'a> {
2629 fn new(text: &'a str) -> Self {
2630 let mut offsets: Vec<usize> = text.char_indices().map(|(offset, _)| offset).collect();
2631
2632 offsets.push(text.len());
2633
2634 let useful: Vec<bool> = text.chars().map(eci_may_help).collect();
2635 let mut eci_useful_from = vec![useful.len(); useful.len() + 1];
2636
2637 for index in (0..useful.len()).rev() {
2638 eci_useful_from[index] = if useful[index] { index } else { eci_useful_from[index + 1] };
2639 }
2640
2641 Self {
2642 text,
2643 offsets,
2644 eci_useful_from,
2645 }
2646 }
2647
2648 #[inline]
2650 fn len(&self) -> usize {
2651 self.offsets.len() - 1
2652 }
2653
2654 #[inline]
2656 fn slice(&self, start: usize, end: usize) -> &'a str {
2657 &self.text[self.offsets[start]..self.offsets[end]]
2658 }
2659}
2660
2661#[cfg(feature = "qr")]
2663#[inline]
2664fn eci_may_help(character: char) -> bool {
2665 #[cfg(feature = "kanji")]
2666 if kanji_encoding(character).is_some() {
2667 return true;
2668 }
2669
2670 !is_latin1_character(character)
2671}
2672
2673#[cfg(feature = "qr")]
2675struct SequencePlan {
2676 version: QrVersion,
2677 area: usize,
2678 parts: Vec<(QrVersion, Vec<Segment>)>,
2679}
2680
2681#[cfg(feature = "qr")]
2683struct SequencePartition {
2684 count: usize,
2685 version: QrVersion,
2686 area: usize,
2687 parts: Vec<(usize, usize)>,
2688}
2689
2690#[cfg(feature = "qr")]
2692#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2693enum SequenceRegime {
2694 #[cfg(feature = "kanji")]
2696 Legacy,
2697 Standard,
2699}
2700
2701#[cfg(feature = "qr")]
2702impl SequenceRegime {
2703 #[cfg(feature = "kanji")]
2704 const ALL: [Self; 2] = [Self::Legacy, Self::Standard];
2705 #[cfg(not(feature = "kanji"))]
2706 const ALL: [Self; 1] = [Self::Standard];
2707
2708 #[inline]
2710 const fn starts(self, eci_in_force: bool) -> &'static [(optimizer::Start, bool)] {
2711 match self {
2712 #[cfg(feature = "kanji")]
2713 Self::Legacy => &[(optimizer::Start::Legacy, false)],
2714 Self::Standard if eci_in_force => &[(optimizer::Start::Explicit, true)],
2716 Self::Standard => {
2717 &[(optimizer::Start::DefaultEciFree, false), (optimizer::Start::Default, true)]
2718 },
2719 }
2720 }
2721
2722 #[cfg_attr(not(feature = "kanji"), allow(unused_variables))]
2724 fn suits(self, text: &str) -> bool {
2725 match self {
2726 #[cfg(feature = "kanji")]
2728 Self::Legacy => {
2729 text.chars().any(|character| kanji_encoding(character).is_some())
2730 && text.chars().all(|character| {
2731 is_legacy_byte_character(character) || kanji_encoding(character).is_some()
2732 })
2733 },
2734 Self::Standard => true,
2735 }
2736 }
2737}