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

     1  package aztec
     2  
     3  import (
     4  	"git.sr.ht/~pingoo/stdx/barcode/utils"
     5  )
     6  
     7  func highlevelEncode(data []byte) *utils.BitList {
     8  	states := stateSlice{initialState}
     9  
    10  	for index := 0; index < len(data); index++ {
    11  		pairCode := 0
    12  		nextChar := byte(0)
    13  		if index+1 < len(data) {
    14  			nextChar = data[index+1]
    15  		}
    16  
    17  		switch cur := data[index]; {
    18  		case cur == '\r' && nextChar == '\n':
    19  			pairCode = 2
    20  		case cur == '.' && nextChar == ' ':
    21  			pairCode = 3
    22  		case cur == ',' && nextChar == ' ':
    23  			pairCode = 4
    24  		case cur == ':' && nextChar == ' ':
    25  			pairCode = 5
    26  		}
    27  		if pairCode > 0 {
    28  			// We have one of the four special PUNCT pairs.  Treat them specially.
    29  			// Get a new set of states for the two new characters.
    30  			states = updateStateListForPair(states, data, index, pairCode)
    31  			index++
    32  		} else {
    33  			// Get a new set of states for the new character.
    34  			states = updateStateListForChar(states, data, index)
    35  		}
    36  	}
    37  	minBitCnt := int((^uint(0)) >> 1)
    38  	var result *state = nil
    39  	for _, s := range states {
    40  		if s.bitCount < minBitCnt {
    41  			minBitCnt = s.bitCount
    42  			result = s
    43  		}
    44  	}
    45  	if result != nil {
    46  		return result.toBitList(data)
    47  	} else {
    48  		return new(utils.BitList)
    49  	}
    50  }
    51  
    52  func simplifyStates(states stateSlice) stateSlice {
    53  	var result stateSlice = nil
    54  	for _, newState := range states {
    55  		add := true
    56  		var newResult stateSlice = nil
    57  
    58  		for _, oldState := range result {
    59  			if add && oldState.isBetterThanOrEqualTo(newState) {
    60  				add = false
    61  			}
    62  			if !(add && newState.isBetterThanOrEqualTo(oldState)) {
    63  				newResult = append(newResult, oldState)
    64  			}
    65  		}
    66  
    67  		if add {
    68  			result = append(newResult, newState)
    69  		} else {
    70  			result = newResult
    71  		}
    72  
    73  	}
    74  
    75  	return result
    76  }
    77  
    78  // We update a set of states for a new character by updating each state
    79  // for the new character, merging the results, and then removing the
    80  // non-optimal states.
    81  func updateStateListForChar(states stateSlice, data []byte, index int) stateSlice {
    82  	var result stateSlice = nil
    83  	for _, s := range states {
    84  		if r := updateStateForChar(s, data, index); len(r) > 0 {
    85  			result = append(result, r...)
    86  		}
    87  	}
    88  	return simplifyStates(result)
    89  }
    90  
    91  // Return a set of states that represent the possible ways of updating this
    92  // state for the next character.  The resulting set of states are added to
    93  // the "result" list.
    94  func updateStateForChar(s *state, data []byte, index int) stateSlice {
    95  	var result stateSlice = nil
    96  	ch := data[index]
    97  	charInCurrentTable := charMap[s.mode][ch] > 0
    98  
    99  	var stateNoBinary *state = nil
   100  	for mode := mode_upper; mode <= mode_punct; mode++ {
   101  		charInMode := charMap[mode][ch]
   102  		if charInMode > 0 {
   103  			if stateNoBinary == nil {
   104  				// Only create stateNoBinary the first time it's required.
   105  				stateNoBinary = s.endBinaryShift(index)
   106  			}
   107  			// Try generating the character by latching to its mode
   108  			if !charInCurrentTable || mode == s.mode || mode == mode_digit {
   109  				// If the character is in the current table, we don't want to latch to
   110  				// any other mode except possibly digit (which uses only 4 bits).  Any
   111  				// other latch would be equally successful *after* this character, and
   112  				// so wouldn't save any bits.
   113  				res := stateNoBinary.latchAndAppend(mode, charInMode)
   114  				result = append(result, res)
   115  			}
   116  			// Try generating the character by switching to its mode.
   117  			if _, ok := shiftTable[s.mode][mode]; !charInCurrentTable && ok {
   118  				// It never makes sense to temporarily shift to another mode if the
   119  				// character exists in the current mode.  That can never save bits.
   120  				res := stateNoBinary.shiftAndAppend(mode, charInMode)
   121  				result = append(result, res)
   122  			}
   123  		}
   124  	}
   125  	if s.bShiftByteCount > 0 || charMap[s.mode][ch] == 0 {
   126  		// It's never worthwhile to go into binary shift mode if you're not already
   127  		// in binary shift mode, and the character exists in your current mode.
   128  		// That can never save bits over just outputting the char in the current mode.
   129  		res := s.addBinaryShiftChar(index)
   130  		result = append(result, res)
   131  	}
   132  	return result
   133  }
   134  
   135  // We update a set of states for a new character by updating each state
   136  // for the new character, merging the results, and then removing the
   137  // non-optimal states.
   138  func updateStateListForPair(states stateSlice, data []byte, index int, pairCode int) stateSlice {
   139  	var result stateSlice = nil
   140  	for _, s := range states {
   141  		if r := updateStateForPair(s, data, index, pairCode); len(r) > 0 {
   142  			result = append(result, r...)
   143  		}
   144  	}
   145  	return simplifyStates(result)
   146  }
   147  
   148  func updateStateForPair(s *state, data []byte, index int, pairCode int) stateSlice {
   149  	var result stateSlice
   150  	stateNoBinary := s.endBinaryShift(index)
   151  	// Possibility 1.  Latch to MODE_PUNCT, and then append this code
   152  	result = append(result, stateNoBinary.latchAndAppend(mode_punct, pairCode))
   153  	if s.mode != mode_punct {
   154  		// Possibility 2.  Shift to MODE_PUNCT, and then append this code.
   155  		// Every state except MODE_PUNCT (handled above) can shift
   156  		result = append(result, stateNoBinary.shiftAndAppend(mode_punct, pairCode))
   157  	}
   158  	if pairCode == 3 || pairCode == 4 {
   159  		// both characters are in DIGITS.  Sometimes better to just add two digits
   160  		digitState := stateNoBinary.
   161  			latchAndAppend(mode_digit, 16-pairCode). // period or comma in DIGIT
   162  			latchAndAppend(mode_digit, 1)            // space in DIGIT
   163  		result = append(result, digitState)
   164  	}
   165  	if s.bShiftByteCount > 0 {
   166  		// It only makes sense to do the characters as binary if we're already
   167  		// in binary mode.
   168  		result = append(result, s.addBinaryShiftChar(index).addBinaryShiftChar(index+1))
   169  	}
   170  	return result
   171  }