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 }