git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/barcode/aztec/state.go (about)

     1  package aztec
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"git.sr.ht/~pingoo/stdx/barcode/utils"
     7  )
     8  
     9  type encodingMode byte
    10  
    11  const (
    12  	mode_upper encodingMode = iota // 5 bits
    13  	mode_lower                     // 5 bits
    14  	mode_digit                     // 4 bits
    15  	mode_mixed                     // 5 bits
    16  	mode_punct                     // 5 bits
    17  )
    18  
    19  var (
    20  	// The Latch Table shows, for each pair of Modes, the optimal method for
    21  	// getting from one mode to another.  In the worst possible case, this can
    22  	// be up to 14 bits.  In the best possible case, we are already there!
    23  	// The high half-word of each entry gives the number of bits.
    24  	// The low half-word of each entry are the actual bits necessary to change
    25  	latchTable = map[encodingMode]map[encodingMode]int{
    26  		mode_upper: {
    27  			mode_upper: 0,
    28  			mode_lower: (5 << 16) + 28,
    29  			mode_digit: (5 << 16) + 30,
    30  			mode_mixed: (5 << 16) + 29,
    31  			mode_punct: (10 << 16) + (29 << 5) + 30,
    32  		},
    33  		mode_lower: {
    34  			mode_upper: (9 << 16) + (30 << 4) + 14,
    35  			mode_lower: 0,
    36  			mode_digit: (5 << 16) + 30,
    37  			mode_mixed: (5 << 16) + 29,
    38  			mode_punct: (10 << 16) + (29 << 5) + 30,
    39  		},
    40  		mode_digit: {
    41  			mode_upper: (4 << 16) + 14,
    42  			mode_lower: (9 << 16) + (14 << 5) + 28,
    43  			mode_digit: 0,
    44  			mode_mixed: (9 << 16) + (14 << 5) + 29,
    45  			mode_punct: (14 << 16) + (14 << 10) + (29 << 5) + 30,
    46  		},
    47  		mode_mixed: {
    48  			mode_upper: (5 << 16) + 29,
    49  			mode_lower: (5 << 16) + 28,
    50  			mode_digit: (10 << 16) + (29 << 5) + 30,
    51  			mode_mixed: 0,
    52  			mode_punct: (5 << 16) + 30,
    53  		},
    54  		mode_punct: {
    55  			mode_upper: (5 << 16) + 31,
    56  			mode_lower: (10 << 16) + (31 << 5) + 28,
    57  			mode_digit: (10 << 16) + (31 << 5) + 30,
    58  			mode_mixed: (10 << 16) + (31 << 5) + 29,
    59  			mode_punct: 0,
    60  		},
    61  	}
    62  	// A map showing the available shift codes.  (The shifts to BINARY are not shown)
    63  	shiftTable = map[encodingMode]map[encodingMode]int{
    64  		mode_upper: {
    65  			mode_punct: 0,
    66  		},
    67  		mode_lower: {
    68  			mode_punct: 0,
    69  			mode_upper: 28,
    70  		},
    71  		mode_mixed: {
    72  			mode_punct: 0,
    73  		},
    74  		mode_digit: {
    75  			mode_punct: 0,
    76  			mode_upper: 15,
    77  		},
    78  	}
    79  	charMap map[encodingMode][]int
    80  )
    81  
    82  type state struct {
    83  	mode            encodingMode
    84  	tokens          token
    85  	bShiftByteCount int
    86  	bitCount        int
    87  }
    88  type stateSlice []*state
    89  
    90  var initialState *state = &state{
    91  	mode:            mode_upper,
    92  	tokens:          nil,
    93  	bShiftByteCount: 0,
    94  	bitCount:        0,
    95  }
    96  
    97  func init() {
    98  	charMap = make(map[encodingMode][]int)
    99  	charMap[mode_upper] = make([]int, 256)
   100  	charMap[mode_lower] = make([]int, 256)
   101  	charMap[mode_digit] = make([]int, 256)
   102  	charMap[mode_mixed] = make([]int, 256)
   103  	charMap[mode_punct] = make([]int, 256)
   104  
   105  	charMap[mode_upper][' '] = 1
   106  	for c := 'A'; c <= 'Z'; c++ {
   107  		charMap[mode_upper][int(c)] = int(c - 'A' + 2)
   108  	}
   109  
   110  	charMap[mode_lower][' '] = 1
   111  	for c := 'a'; c <= 'z'; c++ {
   112  		charMap[mode_lower][c] = int(c - 'a' + 2)
   113  	}
   114  	charMap[mode_digit][' '] = 1
   115  	for c := '0'; c <= '9'; c++ {
   116  		charMap[mode_digit][c] = int(c - '0' + 2)
   117  	}
   118  	charMap[mode_digit][','] = 12
   119  	charMap[mode_digit]['.'] = 13
   120  
   121  	mixedTable := []int{
   122  		0, ' ', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
   123  		11, 12, 13, 27, 28, 29, 30, 31, '@', '\\', '^',
   124  		'_', '`', '|', '~', 127,
   125  	}
   126  	for i, v := range mixedTable {
   127  		charMap[mode_mixed][v] = i
   128  	}
   129  
   130  	punctTable := []int{
   131  		0, '\r', 0, 0, 0, 0, '!', '\'', '#', '$', '%', '&', '\'',
   132  		'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
   133  		'[', ']', '{', '}',
   134  	}
   135  	for i, v := range punctTable {
   136  		if v > 0 {
   137  			charMap[mode_punct][v] = i
   138  		}
   139  	}
   140  }
   141  
   142  func (em encodingMode) BitCount() byte {
   143  	if em == mode_digit {
   144  		return 4
   145  	}
   146  	return 5
   147  }
   148  
   149  // Create a new state representing this state with a latch to a (not
   150  // necessary different) mode, and then a code.
   151  func (s *state) latchAndAppend(mode encodingMode, value int) *state {
   152  	bitCount := s.bitCount
   153  	tokens := s.tokens
   154  
   155  	if mode != s.mode {
   156  		latch := latchTable[s.mode][mode]
   157  		tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16))
   158  		bitCount += latch >> 16
   159  	}
   160  	tokens = newSimpleToken(tokens, value, mode.BitCount())
   161  	return &state{
   162  		mode:            mode,
   163  		tokens:          tokens,
   164  		bShiftByteCount: 0,
   165  		bitCount:        bitCount + int(mode.BitCount()),
   166  	}
   167  }
   168  
   169  // Create a new state representing this state, with a temporary shift
   170  // to a different mode to output a single value.
   171  func (s *state) shiftAndAppend(mode encodingMode, value int) *state {
   172  	tokens := s.tokens
   173  
   174  	// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
   175  	tokens = newSimpleToken(tokens, shiftTable[s.mode][mode], s.mode.BitCount())
   176  	tokens = newSimpleToken(tokens, value, 5)
   177  
   178  	return &state{
   179  		mode:            s.mode,
   180  		tokens:          tokens,
   181  		bShiftByteCount: 0,
   182  		bitCount:        s.bitCount + int(s.mode.BitCount()) + 5,
   183  	}
   184  }
   185  
   186  // Create a new state representing this state, but an additional character
   187  // output in Binary Shift mode.
   188  func (s *state) addBinaryShiftChar(index int) *state {
   189  	tokens := s.tokens
   190  	mode := s.mode
   191  	bitCnt := s.bitCount
   192  	if s.mode == mode_punct || s.mode == mode_digit {
   193  		latch := latchTable[s.mode][mode_upper]
   194  		tokens = newSimpleToken(tokens, latch&0xFFFF, byte(latch>>16))
   195  		bitCnt += latch >> 16
   196  		mode = mode_upper
   197  	}
   198  	deltaBitCount := 8
   199  	if s.bShiftByteCount == 0 || s.bShiftByteCount == 31 {
   200  		deltaBitCount = 18
   201  	} else if s.bShiftByteCount == 62 {
   202  		deltaBitCount = 9
   203  	}
   204  	result := &state{
   205  		mode:            mode,
   206  		tokens:          tokens,
   207  		bShiftByteCount: s.bShiftByteCount + 1,
   208  		bitCount:        bitCnt + deltaBitCount,
   209  	}
   210  	if result.bShiftByteCount == 2047+31 {
   211  		// The string is as long as it's allowed to be.  We should end it.
   212  		result = result.endBinaryShift(index + 1)
   213  	}
   214  
   215  	return result
   216  }
   217  
   218  // Create the state identical to this one, but we are no longer in
   219  // Binary Shift mode.
   220  func (s *state) endBinaryShift(index int) *state {
   221  	if s.bShiftByteCount == 0 {
   222  		return s
   223  	}
   224  	tokens := newShiftToken(s.tokens, index-s.bShiftByteCount, s.bShiftByteCount)
   225  	return &state{
   226  		mode:            s.mode,
   227  		tokens:          tokens,
   228  		bShiftByteCount: 0,
   229  		bitCount:        s.bitCount,
   230  	}
   231  }
   232  
   233  // Returns true if "this" state is better (or equal) to be in than "that"
   234  // state under all possible circumstances.
   235  func (this *state) isBetterThanOrEqualTo(other *state) bool {
   236  	mySize := this.bitCount + (latchTable[this.mode][other.mode] >> 16)
   237  
   238  	if other.bShiftByteCount > 0 && (this.bShiftByteCount == 0 || this.bShiftByteCount > other.bShiftByteCount) {
   239  		mySize += 10 // Cost of entering Binary Shift mode.
   240  	}
   241  	return mySize <= other.bitCount
   242  }
   243  
   244  func (s *state) toBitList(text []byte) *utils.BitList {
   245  	tokens := make([]token, 0)
   246  	se := s.endBinaryShift(len(text))
   247  
   248  	for t := se.tokens; t != nil; t = t.prev() {
   249  		tokens = append(tokens, t)
   250  	}
   251  	res := new(utils.BitList)
   252  	for i := len(tokens) - 1; i >= 0; i-- {
   253  		tokens[i].appendTo(res, text)
   254  	}
   255  	return res
   256  }
   257  
   258  func (s *state) String() string {
   259  	tokens := make([]token, 0)
   260  	for t := s.tokens; t != nil; t = t.prev() {
   261  		tokens = append([]token{t}, tokens...)
   262  	}
   263  	return fmt.Sprintf("M:%d bits=%d bytes=%d: %v", s.mode, s.bitCount, s.bShiftByteCount, tokens)
   264  }