Skip to main content

qrcode_core/
optimize.rs

1#![allow(clippy::unicode_not_nfc)]
2//! Data mode segmentation optimizer.
3//!
4//! QR codes support four data modes (Numeric, Alphanumeric, Byte, Kanji),
5//! each with different efficiency for different character types. This module
6//! finds the optimal sequence of mode switches to minimize the total number
7//! of bits required to encode the input data.
8//!
9//! The optimizer uses dynamic programming to explore all possible mode
10//! transitions and selects the segmentation that produces the shortest
11//! bit stream for the target QR code version.
12#[cfg(not(feature = "std"))]
13#[allow(unused_imports)]
14use alloc::{
15    borrow::ToOwned,
16    format,
17    string::{String, ToString},
18    vec,
19    vec::Vec,
20};
21
22use crate::types::{Mode, Version};
23use core::marker::PhantomData;
24use core::slice::Iter;
25
26//------------------------------------------------------------------------------
27//{{{ Segment
28
29/// A segment of data committed to an encoding mode.
30#[derive(PartialEq, Eq, Debug, Copy, Clone)]
31pub struct Segment {
32    /// The encoding mode of the segment of data.
33    pub mode: Mode,
34
35    /// The start index of the segment.
36    pub begin: usize,
37
38    /// The end index (exclusive) of the segment.
39    pub end: usize,
40}
41
42impl Segment {
43    /// Compute the number of bits (including the size of the mode indicator and
44    /// length bits) when this segment is encoded.
45    pub fn encoded_len(&self, version: Version) -> usize {
46        let byte_size = self.end - self.begin;
47        let chars_count = if self.mode == Mode::Kanji { byte_size / 2 } else { byte_size };
48
49        let mode_bits_count = version.mode_bits_count();
50        let length_bits_count = self.mode.length_bits_count(version);
51        let data_bits_count = self.mode.data_bits_count(chars_count);
52
53        mode_bits_count + length_bits_count + data_bits_count
54    }
55}
56
57//}}}
58//------------------------------------------------------------------------------
59//{{{ Parser
60
61/// This iterator is basically equivalent to
62///
63/// ```ignore
64/// data.map(|c| ExclCharSet::from_u8(*c))
65///     .chain(Some(ExclCharSet::End).move_iter())
66///     .enumerate()
67/// ```
68///
69/// But the type is too hard to write, thus the new type.
70///
71struct EcsIter<I> {
72    base: I,
73    index: usize,
74    ended: bool,
75}
76
77impl<'a, I: Iterator<Item = &'a u8>> Iterator for EcsIter<I> {
78    type Item = (usize, ExclCharSet);
79
80    fn next(&mut self) -> Option<(usize, ExclCharSet)> {
81        if self.ended {
82            return None;
83        }
84
85        match self.base.next() {
86            None => {
87                self.ended = true;
88                Some((self.index, ExclCharSet::End))
89            }
90            Some(c) => {
91                let old_index = self.index;
92                self.index += 1;
93                Some((old_index, ExclCharSet::from_u8(*c)))
94            }
95        }
96    }
97}
98
99/// QR code data parser to classify the input into distinct segments.
100pub struct Parser<'a> {
101    ecs_iter: EcsIter<Iter<'a, u8>>,
102    state: State,
103    begin: usize,
104    pending_single_byte: bool,
105}
106
107impl<'a> Parser<'a> {
108    /// Creates a new iterator which parse the data into segments that only
109    /// contains their exclusive subsets. No optimization is done at this point.
110    ///
111    ///     use qrcode_core::optimize::{Parser, Segment};
112    ///     use qrcode_core::types::Mode::{Alphanumeric, Numeric, Byte};
113    ///
114    ///     let parse_res = Parser::new(b"ABC123abcd").collect::<Vec<Segment>>();
115    ///     assert_eq!(parse_res, vec![Segment { mode: Alphanumeric, begin: 0, end: 3 },
116    ///                                Segment { mode: Numeric, begin: 3, end: 6 },
117    ///                                Segment { mode: Byte, begin: 6, end: 10 }]);
118    ///
119    pub fn new(data: &[u8]) -> Parser<'_> {
120        Parser {
121            ecs_iter: EcsIter { base: data.iter(), index: 0, ended: false },
122            state: State::Init,
123            begin: 0,
124            pending_single_byte: false,
125        }
126    }
127}
128
129impl<'a> Iterator for Parser<'a> {
130    type Item = Segment;
131
132    fn next(&mut self) -> Option<Segment> {
133        if self.pending_single_byte {
134            self.pending_single_byte = false;
135            self.begin += 1;
136            return Some(Segment { mode: Mode::Byte, begin: self.begin - 1, end: self.begin });
137        }
138
139        loop {
140            let (i, ecs) = self.ecs_iter.next()?;
141            let (next_state, action) = STATE_TRANSITION[self.state as usize + ecs as usize];
142            self.state = next_state;
143
144            let old_begin = self.begin;
145            let push_mode = match action {
146                Action::Idle => continue,
147                Action::Numeric => Mode::Numeric,
148                Action::Alpha => Mode::Alphanumeric,
149                Action::Byte => Mode::Byte,
150                Action::Kanji => Mode::Kanji,
151                Action::KanjiAndSingleByte => {
152                    let next_begin = i - 1;
153                    if self.begin == next_begin {
154                        Mode::Byte
155                    } else {
156                        self.pending_single_byte = true;
157                        self.begin = next_begin;
158                        return Some(Segment { mode: Mode::Kanji, begin: old_begin, end: next_begin });
159                    }
160                }
161            };
162
163            self.begin = i;
164            return Some(Segment { mode: push_mode, begin: old_begin, end: i });
165        }
166    }
167}
168
169#[cfg(test)]
170mod parse_tests {
171    use crate::optimize::{Parser, Segment};
172    use crate::types::Mode;
173
174    fn parse(data: &[u8]) -> Vec<Segment> {
175        Parser::new(data).collect()
176    }
177
178    #[test]
179    fn test_parse_1() {
180        let segs = parse(b"01049123451234591597033130128%10ABC123");
181        assert_eq!(
182            segs,
183            vec![
184                Segment { mode: Mode::Numeric, begin: 0, end: 29 },
185                Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
186                Segment { mode: Mode::Numeric, begin: 30, end: 32 },
187                Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
188                Segment { mode: Mode::Numeric, begin: 35, end: 38 },
189            ]
190        );
191    }
192
193    #[test]
194    fn test_parse_shift_jis_example_1() {
195        let segs = parse(b"\x82\xa0\x81\x41\x41\xb1\x81\xf0"); // "あ、AアÅ"
196        assert_eq!(
197            segs,
198            vec![
199                Segment { mode: Mode::Kanji, begin: 0, end: 4 },
200                Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
201                Segment { mode: Mode::Byte, begin: 5, end: 6 },
202                Segment { mode: Mode::Kanji, begin: 6, end: 8 },
203            ]
204        );
205    }
206
207    #[test]
208    fn test_parse_utf_8() {
209        // Mojibake?
210        let segs = parse(b"\xe3\x81\x82\xe3\x80\x81A\xef\xbd\xb1\xe2\x84\xab");
211        assert_eq!(
212            segs,
213            vec![
214                Segment { mode: Mode::Kanji, begin: 0, end: 4 },
215                Segment { mode: Mode::Byte, begin: 4, end: 5 },
216                Segment { mode: Mode::Kanji, begin: 5, end: 7 },
217                Segment { mode: Mode::Byte, begin: 7, end: 10 },
218                Segment { mode: Mode::Kanji, begin: 10, end: 12 },
219                Segment { mode: Mode::Byte, begin: 12, end: 13 },
220            ]
221        );
222    }
223
224    #[test]
225    fn test_not_kanji_1() {
226        let segs = parse(b"\x81\x30");
227        assert_eq!(
228            segs,
229            vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Numeric, begin: 1, end: 2 }]
230        );
231    }
232
233    #[test]
234    fn test_not_kanji_2() {
235        // Note that it's implementation detail that the byte seq is split into
236        // two. Perhaps adjust the test to check for this.
237        let segs = parse(b"\xeb\xc0");
238        assert_eq!(
239            segs,
240            vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Byte, begin: 1, end: 2 }]
241        );
242    }
243
244    #[test]
245    fn test_not_kanji_3() {
246        let segs = parse(b"\x81\x7f");
247        assert_eq!(
248            segs,
249            vec![Segment { mode: Mode::Byte, begin: 0, end: 1 }, Segment { mode: Mode::Byte, begin: 1, end: 2 }]
250        );
251    }
252
253    #[test]
254    fn test_not_kanji_4() {
255        let segs = parse(b"\x81\x40\x81");
256        assert_eq!(
257            segs,
258            vec![Segment { mode: Mode::Kanji, begin: 0, end: 2 }, Segment { mode: Mode::Byte, begin: 2, end: 3 }]
259        );
260    }
261}
262
263//}}}
264//------------------------------------------------------------------------------
265//{{{ Optimizer
266
267/// Iterator that merges consecutive parser segments to minimize the total
268/// encoded length for a given [`Version`]. Created via [`Parser::optimize`].
269pub struct Optimizer<I> {
270    optimized: Vec<Segment>,
271    index: usize,
272    _source: PhantomData<I>,
273}
274
275impl<I: Iterator<Item = Segment>> Optimizer<I> {
276    /// Optimize the segments by combining adjacent segments when beneficial.
277    ///
278    /// This uses dynamic programming over the parser's segment boundaries. It
279    /// finds the minimum-size contiguous merge plan for those boundaries, but
280    /// does not split a parser segment into smaller pieces.
281    ///
282    pub fn new(segments: I, version: Version) -> Self {
283        let segments = segments.collect::<Vec<_>>();
284        Self { optimized: optimize_segments(&segments, version), index: 0, _source: PhantomData }
285    }
286}
287
288impl<'a> Parser<'a> {
289    /// Turns this parser into an [`Optimizer`] for `version`, which yields
290    /// optimally merged segments.
291    pub fn optimize(self, version: Version) -> Optimizer<Parser<'a>> {
292        Optimizer::new(self, version)
293    }
294}
295
296impl<I: Iterator<Item = Segment>> Iterator for Optimizer<I> {
297    type Item = Segment;
298
299    fn next(&mut self) -> Option<Segment> {
300        let segment = self.optimized.get(self.index).copied();
301        if segment.is_some() {
302            self.index += 1;
303        }
304        segment
305    }
306
307    fn size_hint(&self) -> (usize, Option<usize>) {
308        let remaining = self.optimized.len().saturating_sub(self.index);
309        (remaining, Some(remaining))
310    }
311}
312
313impl<I: Iterator<Item = Segment>> ExactSizeIterator for Optimizer<I> {}
314impl<I: Iterator<Item = Segment>> core::iter::FusedIterator for Optimizer<I> {}
315
316/// Computes the total encoded length of all segments.
317pub fn total_encoded_len(segments: &[Segment], version: Version) -> usize {
318    segments.iter().map(|seg| seg.encoded_len(version)).sum()
319}
320
321/// Computes the minimum-size merge plan for parser segments.
322///
323/// Segment boundaries are preserved; adjacent segments may be merged into the
324/// smallest common data mode that can encode the merged range.
325#[must_use]
326pub fn optimize_segments(segments: &[Segment], version: Version) -> Vec<Segment> {
327    let len = segments.len();
328    if len == 0 {
329        return Vec::new();
330    }
331
332    let mut best_bits = vec![usize::MAX; len + 1];
333    let mut best_count = vec![usize::MAX; len + 1];
334    let mut previous = vec![0_usize; len + 1];
335    let mut previous_mode = vec![Mode::Byte; len + 1];
336    best_bits[0] = 0;
337    best_count[0] = 0;
338
339    for end in 1..=len {
340        let mut mode = segments[end - 1].mode;
341        for start in (0..end).rev() {
342            if start + 1 < end {
343                mode = segments[start].mode.max(mode);
344            }
345            let merged = Segment { mode, begin: segments[start].begin, end: segments[end - 1].end };
346            let Some(candidate_bits) = best_bits[start].checked_add(merged.encoded_len(version)) else {
347                continue;
348            };
349            let candidate_count = best_count[start] + 1;
350            if candidate_bits < best_bits[end] || candidate_bits == best_bits[end] && candidate_count < best_count[end]
351            {
352                best_bits[end] = candidate_bits;
353                best_count[end] = candidate_count;
354                previous[end] = start;
355                previous_mode[end] = mode;
356            }
357        }
358    }
359
360    let mut cursor = len;
361    let mut optimized = Vec::with_capacity(best_count[len]);
362    while cursor > 0 {
363        let start = previous[cursor];
364        optimized.push(Segment {
365            mode: previous_mode[cursor],
366            begin: segments[start].begin,
367            end: segments[cursor - 1].end,
368        });
369        cursor = start;
370    }
371    optimized.reverse();
372    optimized
373}
374
375#[cfg(test)]
376mod optimize_tests {
377    use crate::optimize::{Optimizer, Segment, optimize_segments, total_encoded_len};
378    use crate::types::{Mode, Version};
379
380    fn test_optimization_result(given: &[Segment], expected: &[Segment], version: Version) {
381        let prev_len = total_encoded_len(given, version);
382        let opt_segs = Optimizer::new(given.iter().copied(), version).collect::<Vec<_>>();
383        let new_len = total_encoded_len(&opt_segs, version);
384        if given != opt_segs {
385            assert!(prev_len > new_len, "{prev_len} > {new_len}");
386        }
387        assert_eq!(
388            opt_segs,
389            expected,
390            "Optimization gave something better: {} < {} ({:?})",
391            new_len,
392            total_encoded_len(expected, version),
393            opt_segs
394        );
395    }
396
397    #[test]
398    fn test_example_1() {
399        test_optimization_result(
400            &[
401                Segment { mode: Mode::Alphanumeric, begin: 0, end: 3 },
402                Segment { mode: Mode::Numeric, begin: 3, end: 6 },
403                Segment { mode: Mode::Byte, begin: 6, end: 10 },
404            ],
405            &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 6 }, Segment { mode: Mode::Byte, begin: 6, end: 10 }],
406            Version::Normal(1),
407        );
408    }
409
410    #[test]
411    fn test_example_2() {
412        test_optimization_result(
413            &[
414                Segment { mode: Mode::Numeric, begin: 0, end: 29 },
415                Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
416                Segment { mode: Mode::Numeric, begin: 30, end: 32 },
417                Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
418                Segment { mode: Mode::Numeric, begin: 35, end: 38 },
419            ],
420            &[
421                Segment { mode: Mode::Numeric, begin: 0, end: 29 },
422                Segment { mode: Mode::Alphanumeric, begin: 29, end: 38 },
423            ],
424            Version::Normal(9),
425        );
426    }
427
428    #[test]
429    fn test_example_3() {
430        test_optimization_result(
431            &[
432                Segment { mode: Mode::Kanji, begin: 0, end: 4 },
433                Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
434                Segment { mode: Mode::Byte, begin: 5, end: 6 },
435                Segment { mode: Mode::Kanji, begin: 6, end: 8 },
436            ],
437            &[Segment { mode: Mode::Byte, begin: 0, end: 8 }],
438            Version::Normal(1),
439        );
440    }
441
442    #[test]
443    fn test_example_4() {
444        test_optimization_result(
445            &[Segment { mode: Mode::Kanji, begin: 0, end: 10 }, Segment { mode: Mode::Byte, begin: 10, end: 11 }],
446            &[Segment { mode: Mode::Kanji, begin: 0, end: 10 }, Segment { mode: Mode::Byte, begin: 10, end: 11 }],
447            Version::Normal(1),
448        );
449    }
450
451    #[test]
452    fn test_annex_j_guideline_1a() {
453        test_optimization_result(
454            &[
455                Segment { mode: Mode::Numeric, begin: 0, end: 3 },
456                Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
457            ],
458            &[
459                Segment { mode: Mode::Numeric, begin: 0, end: 3 },
460                Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
461            ],
462            Version::Micro(2),
463        );
464    }
465
466    #[test]
467    fn test_annex_j_guideline_1b() {
468        test_optimization_result(
469            &[
470                Segment { mode: Mode::Numeric, begin: 0, end: 2 },
471                Segment { mode: Mode::Alphanumeric, begin: 2, end: 4 },
472            ],
473            &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
474            Version::Micro(2),
475        );
476    }
477
478    #[test]
479    fn test_annex_j_guideline_1c() {
480        test_optimization_result(
481            &[
482                Segment { mode: Mode::Numeric, begin: 0, end: 3 },
483                Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
484            ],
485            &[Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
486            Version::Micro(3),
487        );
488    }
489
490    #[test]
491    fn dynamic_programming_can_skip_a_local_merge_for_a_better_total() {
492        let given = [
493            Segment { mode: Mode::Numeric, begin: 0, end: 7 },
494            Segment { mode: Mode::Alphanumeric, begin: 7, end: 8 },
495            Segment { mode: Mode::Numeric, begin: 8, end: 9 },
496        ];
497
498        let optimized = optimize_segments(&given, Version::Normal(1));
499
500        assert_eq!(
501            optimized,
502            vec![
503                Segment { mode: Mode::Numeric, begin: 0, end: 7 },
504                Segment { mode: Mode::Alphanumeric, begin: 7, end: 9 },
505            ]
506        );
507        assert!(
508            total_encoded_len(&optimized, Version::Normal(1))
509                < total_encoded_len(&[Segment { mode: Mode::Alphanumeric, begin: 0, end: 9 }], Version::Normal(1))
510        );
511    }
512}
513
514//}}}
515//------------------------------------------------------------------------------
516//{{{ Internal types and data for parsing
517
518/// All values of `u8` can be split into 9 different character sets when
519/// determining which encoding to use. This enum represents these groupings for
520/// parsing purpose.
521#[derive(Copy, Clone)]
522enum ExclCharSet {
523    /// The end of string.
524    End = 0,
525
526    /// All symbols supported by the Alphanumeric encoding, i.e. space, `$`, `%`,
527    /// `*`, `+`, `-`, `.`, `/` and `:`.
528    Symbol = 1,
529
530    /// All numbers (0–9).
531    Numeric = 2,
532
533    /// All uppercase letters (A–Z). These characters may also appear in the
534    /// second byte of a Shift JIS 2-byte encoding.
535    Alpha = 3,
536
537    /// The first byte of a Shift JIS 2-byte encoding, in the range 0x81–0x9f.
538    KanjiHi1 = 4,
539
540    /// The first byte of a Shift JIS 2-byte encoding, in the range 0xe0–0xea.
541    KanjiHi2 = 5,
542
543    /// The first byte of a Shift JIS 2-byte encoding, of value 0xeb. This is
544    /// different from the other two range that the second byte has a smaller
545    /// range.
546    KanjiHi3 = 6,
547
548    /// The second byte of a Shift JIS 2-byte encoding, in the range 0x40–0xbf,
549    /// excluding letters (covered by `Alpha`), 0x81–0x9f (covered by `KanjiHi1`),
550    /// and the invalid byte 0x7f.
551    KanjiLo1 = 7,
552
553    /// The second byte of a Shift JIS 2-byte encoding, in the range 0xc0–0xfc,
554    /// excluding the range 0xe0–0xeb (covered by `KanjiHi2` and `KanjiHi3`).
555    /// This half of byte-pair cannot appear as the second byte leaded by
556    /// `KanjiHi3`.
557    KanjiLo2 = 8,
558
559    /// Any other values not covered by the above character sets.
560    Byte = 9,
561}
562
563impl ExclCharSet {
564    /// Determines which character set a byte is in.
565    fn from_u8(c: u8) -> Self {
566        match c {
567            0x20 | 0x24 | 0x25 | 0x2a | 0x2b | 0x2d..=0x2f | 0x3a => ExclCharSet::Symbol,
568            0x30..=0x39 => ExclCharSet::Numeric,
569            0x41..=0x5a => ExclCharSet::Alpha,
570            0x81..=0x9f => ExclCharSet::KanjiHi1,
571            0xe0..=0xea => ExclCharSet::KanjiHi2,
572            0xeb => ExclCharSet::KanjiHi3,
573            0x40 | 0x5b..=0x7e | 0x80 | 0xa0..=0xbf => ExclCharSet::KanjiLo1,
574            0xc0..=0xdf | 0xec..=0xfc => ExclCharSet::KanjiLo2,
575            _ => ExclCharSet::Byte,
576        }
577    }
578}
579
580/// The current parsing state.
581#[derive(Copy, Clone)]
582enum State {
583    /// Just initialized.
584    Init = 0,
585
586    /// Inside a string that can be exclusively encoded as Numeric.
587    Numeric = 10,
588
589    /// Inside a string that can be exclusively encoded as Alphanumeric.
590    Alpha = 20,
591
592    /// Inside a string that can be exclusively encoded as 8-Bit Byte.
593    Byte = 30,
594
595    /// Just encountered the first byte of a Shift JIS 2-byte sequence of the
596    /// set `KanjiHi1` or `KanjiHi2`.
597    KanjiHi12 = 40,
598
599    /// Just encountered the first byte of a Shift JIS 2-byte sequence of the
600    /// set `KanjiHi3`.
601    KanjiHi3 = 50,
602
603    /// Inside a string that can be exclusively encoded as Kanji.
604    Kanji = 60,
605}
606
607/// What should the parser do after a state transition.
608#[derive(Copy, Clone)]
609enum Action {
610    /// The parser should do nothing.
611    Idle,
612
613    /// Push the current segment as a Numeric string, and reset the marks.
614    Numeric,
615
616    /// Push the current segment as an Alphanumeric string, and reset the marks.
617    Alpha,
618
619    /// Push the current segment as a 8-Bit Byte string, and reset the marks.
620    Byte,
621
622    /// Push the current segment as a Kanji string, and reset the marks.
623    Kanji,
624
625    /// Push the current segment excluding the last byte as a Kanji string, then
626    /// push the remaining single byte as a Byte string, and reset the marks.
627    KanjiAndSingleByte,
628}
629
630static STATE_TRANSITION: [(State, Action); 70] = [
631    // STATE_TRANSITION[current_state + next_character] == (next_state, what_to_do)
632
633    // Init state:
634    (State::Init, Action::Idle),      // End
635    (State::Alpha, Action::Idle),     // Symbol
636    (State::Numeric, Action::Idle),   // Numeric
637    (State::Alpha, Action::Idle),     // Alpha
638    (State::KanjiHi12, Action::Idle), // KanjiHi1
639    (State::KanjiHi12, Action::Idle), // KanjiHi2
640    (State::KanjiHi3, Action::Idle),  // KanjiHi3
641    (State::Byte, Action::Idle),      // KanjiLo1
642    (State::Byte, Action::Idle),      // KanjiLo2
643    (State::Byte, Action::Idle),      // Byte
644    // Numeric state:
645    (State::Init, Action::Numeric),      // End
646    (State::Alpha, Action::Numeric),     // Symbol
647    (State::Numeric, Action::Idle),      // Numeric
648    (State::Alpha, Action::Numeric),     // Alpha
649    (State::KanjiHi12, Action::Numeric), // KanjiHi1
650    (State::KanjiHi12, Action::Numeric), // KanjiHi2
651    (State::KanjiHi3, Action::Numeric),  // KanjiHi3
652    (State::Byte, Action::Numeric),      // KanjiLo1
653    (State::Byte, Action::Numeric),      // KanjiLo2
654    (State::Byte, Action::Numeric),      // Byte
655    // Alpha state:
656    (State::Init, Action::Alpha),      // End
657    (State::Alpha, Action::Idle),      // Symbol
658    (State::Numeric, Action::Alpha),   // Numeric
659    (State::Alpha, Action::Idle),      // Alpha
660    (State::KanjiHi12, Action::Alpha), // KanjiHi1
661    (State::KanjiHi12, Action::Alpha), // KanjiHi2
662    (State::KanjiHi3, Action::Alpha),  // KanjiHi3
663    (State::Byte, Action::Alpha),      // KanjiLo1
664    (State::Byte, Action::Alpha),      // KanjiLo2
665    (State::Byte, Action::Alpha),      // Byte
666    // Byte state:
667    (State::Init, Action::Byte),      // End
668    (State::Alpha, Action::Byte),     // Symbol
669    (State::Numeric, Action::Byte),   // Numeric
670    (State::Alpha, Action::Byte),     // Alpha
671    (State::KanjiHi12, Action::Byte), // KanjiHi1
672    (State::KanjiHi12, Action::Byte), // KanjiHi2
673    (State::KanjiHi3, Action::Byte),  // KanjiHi3
674    (State::Byte, Action::Idle),      // KanjiLo1
675    (State::Byte, Action::Idle),      // KanjiLo2
676    (State::Byte, Action::Idle),      // Byte
677    // KanjiHi12 state:
678    (State::Init, Action::KanjiAndSingleByte),    // End
679    (State::Alpha, Action::KanjiAndSingleByte),   // Symbol
680    (State::Numeric, Action::KanjiAndSingleByte), // Numeric
681    (State::Kanji, Action::Idle),                 // Alpha
682    (State::Kanji, Action::Idle),                 // KanjiHi1
683    (State::Kanji, Action::Idle),                 // KanjiHi2
684    (State::Kanji, Action::Idle),                 // KanjiHi3
685    (State::Kanji, Action::Idle),                 // KanjiLo1
686    (State::Kanji, Action::Idle),                 // KanjiLo2
687    (State::Byte, Action::KanjiAndSingleByte),    // Byte
688    // KanjiHi3 state:
689    (State::Init, Action::KanjiAndSingleByte),      // End
690    (State::Alpha, Action::KanjiAndSingleByte),     // Symbol
691    (State::Numeric, Action::KanjiAndSingleByte),   // Numeric
692    (State::Kanji, Action::Idle),                   // Alpha
693    (State::Kanji, Action::Idle),                   // KanjiHi1
694    (State::KanjiHi12, Action::KanjiAndSingleByte), // KanjiHi2
695    (State::KanjiHi3, Action::KanjiAndSingleByte),  // KanjiHi3
696    (State::Kanji, Action::Idle),                   // KanjiLo1
697    (State::Byte, Action::KanjiAndSingleByte),      // KanjiLo2
698    (State::Byte, Action::KanjiAndSingleByte),      // Byte
699    // Kanji state:
700    (State::Init, Action::Kanji),     // End
701    (State::Alpha, Action::Kanji),    // Symbol
702    (State::Numeric, Action::Kanji),  // Numeric
703    (State::Alpha, Action::Kanji),    // Alpha
704    (State::KanjiHi12, Action::Idle), // KanjiHi1
705    (State::KanjiHi12, Action::Idle), // KanjiHi2
706    (State::KanjiHi3, Action::Idle),  // KanjiHi3
707    (State::Byte, Action::Kanji),     // KanjiLo1
708    (State::Byte, Action::Kanji),     // KanjiLo2
709    (State::Byte, Action::Kanji),     // Byte
710];
711
712//}}}