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