#
vincentlu
7 天以前 2743d98b983b7ecd049093931749db749b4b2fe4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.zy.acs.common.utils;
 
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Locale;
 
/**
 * 地码编解码规则,按进程级配置生效。
 */
public final class QrCodeCodecSupport {
 
    private static volatile CodecConfig config = CodecConfig.defaultConfig();
 
    private QrCodeCodecSupport() {
    }
 
    public static void configure(String mode, Integer bytes, Integer displayLength, String charsetName) {
        config = CodecConfig.of(mode, bytes, displayLength, charsetName);
    }
 
    public static int qrCodeBytes() {
        return config.bytes;
    }
 
    public static String decode(byte[] bytes, int pos) {
        byte[] qrCodeBytes = Utils.sliceWithReverse(bytes, pos, config.bytes);
        if (config.mode == Mode.NUMERIC) {
            return Utils.zeroFill(unsignedValue(qrCodeBytes).toString(), config.displayLength);
        }
        return stripPadding(new String(qrCodeBytes, config.charset));
    }
 
    public static byte[] encode(String qrCode) {
        if (config.mode == Mode.NUMERIC) {
            return Utils.reverse(toFixedNumericBytes(qrCode, config.bytes));
        }
        return Utils.reverse(toFixedStringBytes(qrCode, config.bytes, config.charset));
    }
 
    private static BigInteger unsignedValue(byte[] bytes) {
        return bytes.length == 0 ? BigInteger.ZERO : new BigInteger(1, bytes);
    }
 
    private static byte[] toFixedNumericBytes(String qrCode, int size) {
        String value = qrCode == null ? "" : qrCode.trim();
        if (value.isEmpty()) {
            value = "0";
        }
        BigInteger numeric = new BigInteger(value);
        if (numeric.signum() < 0) {
            throw new IllegalArgumentException("qrCode must not be negative: " + qrCode);
        }
        byte[] raw = numeric.toByteArray();
        if (raw.length > 1 && raw[0] == 0) {
            raw = Arrays.copyOfRange(raw, 1, raw.length);
        }
        if (raw.length > size) {
            throw new IllegalArgumentException("qrCode exceeds configured byte size: " + qrCode);
        }
        byte[] result = new byte[size];
        System.arraycopy(raw, 0, result, size - raw.length, raw.length);
        return result;
    }
 
    private static byte[] toFixedStringBytes(String qrCode, int size, Charset charset) {
        String value = qrCode == null ? "" : qrCode;
        byte[] raw = value.getBytes(charset);
        if (raw.length > size) {
            throw new IllegalArgumentException("qrCode exceeds configured byte size: " + qrCode);
        }
        byte[] result = new byte[size];
        Arrays.fill(result, (byte) '0');
        System.arraycopy(raw, 0, result, size - raw.length, raw.length);
        return result;
    }
 
    private static String stripPadding(String value) {
        int end = value.length();
        while (end > 0 && value.charAt(end - 1) == '\u0000') {
            end--;
        }
        return value.substring(0, end);
    }
 
    private enum Mode {
        NUMERIC,
        STRING;
 
        static Mode of(String value) {
            if (value == null) {
                return NUMERIC;
            }
            String normalized = value.trim().toUpperCase(Locale.ROOT);
            if ("STRING".equals(normalized)) {
                return STRING;
            }
            return NUMERIC;
        }
    }
 
    private static final class CodecConfig {
        private final Mode mode;
        private final int bytes;
        private final int displayLength;
        private final Charset charset;
 
        private CodecConfig(Mode mode, int bytes, int displayLength, Charset charset) {
            this.mode = mode;
            this.bytes = bytes;
            this.displayLength = displayLength;
            this.charset = charset;
        }
 
        static CodecConfig defaultConfig() {
            return new CodecConfig(Mode.NUMERIC, 4, 8, StandardCharsets.US_ASCII);
        }
 
        static CodecConfig of(String mode, Integer bytes, Integer displayLength, String charsetName) {
            Mode resolvedMode = Mode.of(mode);
            int resolvedBytes = bytes == null || bytes <= 0 ? 4 : bytes;
            int resolvedDisplayLength = displayLength == null || displayLength <= 0 ? 8 : displayLength;
            Charset resolvedCharset = charsetName == null || charsetName.trim().isEmpty()
                    ? StandardCharsets.US_ASCII
                    : Charset.forName(charsetName.trim());
            return new CodecConfig(resolvedMode, resolvedBytes, resolvedDisplayLength, resolvedCharset);
        }
    }
}