From da43fe393b087fbc92def83896eb86a96732cd51 Mon Sep 17 00:00:00 2001 From: nuintun Date: Sun, 17 Mar 2024 10:46:35 +0800 Subject: [PATCH] feat: Update encoder --- packages/core/src/common/encoding/mapping.ts | 20 ++++++++--------- packages/core/src/common/encoding/utils.ts | 12 ++++++---- packages/core/src/common/utils.ts | 6 +++++ packages/core/src/decoder/utils/source.ts | 21 +++++++++--------- packages/core/src/encoder/Encoder.ts | 22 +++++++++---------- .../core/src/encoder/segments/Alphanumeric.ts | 6 ++--- packages/core/src/encoder/segments/Numeric.ts | 10 ++++----- packages/core/src/encoder/utils/encoder.ts | 17 +++++++++++--- 8 files changed, 68 insertions(+), 46 deletions(-) diff --git a/packages/core/src/common/encoding/mapping.ts b/packages/core/src/common/encoding/mapping.ts index 496656d7..8c142ae0 100644 --- a/packages/core/src/common/encoding/mapping.ts +++ b/packages/core/src/common/encoding/mapping.ts @@ -2,9 +2,9 @@ * @module mapping */ -import { getCharactersMapping, getEncodingMapping, getSerialRanges } from './utils'; +import { getMappingFromCharacters, getMappingFromEncodingRanges, getSerialEncodinRanges } from './utils'; -export const GB2312_MAPPING = getEncodingMapping( +export const GB2312_MAPPING = getMappingFromEncodingRanges( 'gb2312', [0xa1a1, 0xa1fe], [0xa2b1, 0xa2e2], @@ -20,12 +20,12 @@ export const GB2312_MAPPING = getEncodingMapping( [0xa8a1, 0xa8ba], [0xa8c5, 0xa8e9], [0xa9a4, 0xa9ef], - ...getSerialRanges(0xb0a1, 0xd6fe, [0, 93]), + ...getSerialEncodinRanges(0xb0a1, 0xd6fe, [0, 93]), [0xd7a1, 0xd7f9], - ...getSerialRanges(0xd8a1, 0xf7fe, [0, 93]) + ...getSerialEncodinRanges(0xd8a1, 0xf7fe, [0, 93]) ); -export const SHIFT_JIS_MAPPING = getEncodingMapping( +export const SHIFT_JIS_MAPPING = getMappingFromEncodingRanges( 'shift-jis', [0x8140, 0x817e], [0x8180, 0x81ac], @@ -47,19 +47,19 @@ export const SHIFT_JIS_MAPPING = getEncodingMapping( [0x8480, 0x8491], [0x849f, 0x84be], [0x889f, 0x88fc], - ...getSerialRanges(0x8940, 0x97fc, [0, 62, 64, 188]), + ...getSerialEncodinRanges(0x8940, 0x97fc, [0, 62, 64, 188]), [0x9840, 0x9872], [0x989f, 0x98fc], - ...getSerialRanges(0x9940, 0x9ffc, [0, 62, 64, 188]), - ...getSerialRanges(0xe040, 0xe9fc, [0, 62, 64, 188]), + ...getSerialEncodinRanges(0x9940, 0x9ffc, [0, 62, 64, 188]), + ...getSerialEncodinRanges(0xe040, 0xe9fc, [0, 62, 64, 188]), [0xea40, 0xea7e], [0xea80, 0xeaa4] ); export const NUMERIC_CHARACTERS = '0123456789'; -export const NUMERIC_MAPPING = getCharactersMapping(NUMERIC_CHARACTERS); +export const NUMERIC_MAPPING = getMappingFromCharacters(NUMERIC_CHARACTERS); export const ALPHANUMERIC_CHARACTERS = `${NUMERIC_CHARACTERS}ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:`; -export const ALPHANUMERIC_MAPPING = getCharactersMapping(ALPHANUMERIC_CHARACTERS); +export const ALPHANUMERIC_MAPPING = getMappingFromCharacters(ALPHANUMERIC_CHARACTERS); diff --git a/packages/core/src/common/encoding/utils.ts b/packages/core/src/common/encoding/utils.ts index b010acbb..e4a47eac 100644 --- a/packages/core/src/common/encoding/utils.ts +++ b/packages/core/src/common/encoding/utils.ts @@ -2,9 +2,11 @@ * @module utils */ +import { charAt } from '/common/utils'; + export type EncodingRange = [start: number, end: number]; -export function getCharactersMapping(characters: string): Map { +export function getMappingFromCharacters(characters: string): Map { let code = 0; const mapping = new Map(); @@ -16,7 +18,7 @@ export function getCharactersMapping(characters: string): Map { return mapping; } -export function getEncodingMapping(label: string, ...ranges: EncodingRange[]): Map { +export function getMappingFromEncodingRanges(label: string, ...ranges: EncodingRange[]): Map { const bytes: number[] = []; const codes: number[] = []; const mapping: Map = new Map(); @@ -24,7 +26,9 @@ export function getEncodingMapping(label: string, ...ranges: EncodingRange[]): M for (const [start, end] of ranges) { for (let code = start; code <= end; code++) { + // Now only support two bytes characters. bytes.push(code >> 8, code & 0xff); + // Cache the codes. codes.push(code); } } @@ -33,7 +37,7 @@ export function getEncodingMapping(label: string, ...ranges: EncodingRange[]): M const characters = decoder.decode(new Uint8Array(bytes)); for (let i = 0; i < length; i++) { - const character = characters.charAt(i); + const character = charAt(characters, i); if (!mapping.has(character)) { mapping.set(character, codes[i]); @@ -43,7 +47,7 @@ export function getEncodingMapping(label: string, ...ranges: EncodingRange[]): M return mapping; } -export function getSerialRanges(start: number, end: number, offsets: number[], delta: number = 256): EncodingRange[] { +export function getSerialEncodinRanges(start: number, end: number, offsets: number[], delta: number = 256): EncodingRange[] { const count = offsets.length - 1; const ranges: EncodingRange[] = []; diff --git a/packages/core/src/common/utils.ts b/packages/core/src/common/utils.ts index f7002e7d..c6add971 100644 --- a/packages/core/src/common/utils.ts +++ b/packages/core/src/common/utils.ts @@ -10,6 +10,12 @@ export function round(value: number): number { return toInt32(value + (value < 0 ? -0.5 : 0.5)); } +export function charAt(value: string, index: number): string { + const character = value.at(index); + + return character != null ? character : ''; +} + // Get hamming weight of int32. export function hammingWeight(value: number): number { // HD, Figure 5-2. diff --git a/packages/core/src/decoder/utils/source.ts b/packages/core/src/decoder/utils/source.ts index e494af9c..021d5bb5 100644 --- a/packages/core/src/decoder/utils/source.ts +++ b/packages/core/src/decoder/utils/source.ts @@ -2,10 +2,11 @@ * @module source */ +import { charAt } from '/common/utils'; import { FNC1 } from '/common/interface'; import { Version } from '/common/Version'; -import { BitSource } from '/common/BitSource'; import { TextDecode } from '/common/encoding'; +import { BitSource } from '/common/BitSource'; import { fromModeBits, Mode } from '/common/Mode'; import { Charset, fromCharsetValue } from '/common/Charset'; import { ALPHANUMERIC_CHARACTERS, NUMERIC_CHARACTERS } from '/common/encoding/mapping'; @@ -71,8 +72,8 @@ function decodeAlphanumericSegment(source: BitSource, count: number, fnc1: boole const nextTwoCharsBits = source.read(11); - content += ALPHANUMERIC_CHARACTERS.charAt(nextTwoCharsBits / 45); - content += ALPHANUMERIC_CHARACTERS.charAt(nextTwoCharsBits % 45); + content += charAt(ALPHANUMERIC_CHARACTERS, nextTwoCharsBits / 45); + content += charAt(ALPHANUMERIC_CHARACTERS, nextTwoCharsBits % 45); count -= 2; } @@ -83,7 +84,7 @@ function decodeAlphanumericSegment(source: BitSource, count: number, fnc1: boole throw new Error('illegal bits length'); } - content += ALPHANUMERIC_CHARACTERS.charAt(source.read(6)); + content += charAt(ALPHANUMERIC_CHARACTERS, source.read(6)); } return fnc1 ? processGSCharacter(content) : content; @@ -187,9 +188,9 @@ function decodeNumericSegment(source: BitSource, count: number): string { throw new Error('illegal numeric codeword'); } - content += NUMERIC_CHARACTERS.charAt(threeDigitsBits / 100); - content += NUMERIC_CHARACTERS.charAt((threeDigitsBits / 10) % 10); - content += NUMERIC_CHARACTERS.charAt(threeDigitsBits % 10); + content += charAt(NUMERIC_CHARACTERS, threeDigitsBits / 100); + content += charAt(NUMERIC_CHARACTERS, (threeDigitsBits / 10) % 10); + content += charAt(NUMERIC_CHARACTERS, threeDigitsBits % 10); count -= 3; } @@ -206,8 +207,8 @@ function decodeNumericSegment(source: BitSource, count: number): string { throw new Error('illegal numeric codeword'); } - content += NUMERIC_CHARACTERS.charAt(twoDigitsBits / 10); - content += NUMERIC_CHARACTERS.charAt(twoDigitsBits % 10); + content += charAt(NUMERIC_CHARACTERS, twoDigitsBits / 10); + content += charAt(NUMERIC_CHARACTERS, twoDigitsBits % 10); } else if (count === 1) { // One digit left over to read. if (source.available() < 4) { @@ -220,7 +221,7 @@ function decodeNumericSegment(source: BitSource, count: number): string { throw new Error('illegal numeric codeword'); } - content += NUMERIC_CHARACTERS.charAt(digitBits); + content += charAt(NUMERIC_CHARACTERS, digitBits); } return content; diff --git a/packages/core/src/encoder/Encoder.ts b/packages/core/src/encoder/Encoder.ts index 6a1c8765..9a828772 100644 --- a/packages/core/src/encoder/Encoder.ts +++ b/packages/core/src/encoder/Encoder.ts @@ -11,9 +11,9 @@ import { calculateBitsNeeded, chooseBestMaskAndMatrix, chooseRecommendVersion, + getSegmentLength, Hints, injectECCodewords, - isByteMode, isHanziMode, Segment, SegmentBlock, @@ -92,8 +92,8 @@ export class Encoder { for (const segment of segments) { const { mode } = segment; const head = new BitArray(); - const data = segment.encode(encode); - const length = isByteMode(segment) ? data.byteLength : segment.content.length; + const body = segment.encode(encode); + const length = getSegmentLength(segment, body); // Append ECI segment if applicable. currentECIValue = appendECI(head, segment, currentECIValue); @@ -114,7 +114,7 @@ export class Encoder { } // Push segment block. - segmentBlocks.push({ mode, head, data, length }); + segmentBlocks.push({ mode, head, body, length }); } let version: Version; @@ -131,21 +131,21 @@ export class Encoder { } } - const headAndDataBits = new BitArray(); + const buffer = new BitArray(); - for (const { mode, head, data, length } of segmentBlocks) { - headAndDataBits.append(head); + for (const { mode, head, body, length } of segmentBlocks) { + buffer.append(head); - appendLengthInfo(headAndDataBits, mode, version, length); + appendLengthInfo(buffer, mode, version, length); - headAndDataBits.append(data); + buffer.append(body); } const ecBlocks = version.getECBlocks(ecLevel); - appendTerminator(headAndDataBits, ecBlocks.numTotalDataCodewords); + appendTerminator(buffer, ecBlocks.numTotalDataCodewords); - const codewords = injectECCodewords(headAndDataBits, ecBlocks); + const codewords = injectECCodewords(buffer, ecBlocks); const [mask, matrix] = chooseBestMaskAndMatrix(codewords, version, ecLevel); return new Encoded(matrix, version, ecLevel, mask); diff --git a/packages/core/src/encoder/segments/Alphanumeric.ts b/packages/core/src/encoder/segments/Alphanumeric.ts index a3eaf0b7..7b36ba79 100644 --- a/packages/core/src/encoder/segments/Alphanumeric.ts +++ b/packages/core/src/encoder/segments/Alphanumeric.ts @@ -52,14 +52,14 @@ export class Alphanumeric { */ public encode(): BitArray { const bits = new BitArray(); - const content = this.#content; + const content = Array.from(this.#content); const { length } = content; for (let i = 0; i < length; ) { - const code1 = getAlphanumericCode(content.charAt(i)); + const code1 = getAlphanumericCode(content[i]); if (i + 1 < length) { - const code2 = getAlphanumericCode(content.charAt(i + 1)); + const code2 = getAlphanumericCode(content[i + 1]); // Encode two alphanumeric letters in 11 bits. bits.append(code1 * 45 + code2, 11); diff --git a/packages/core/src/encoder/segments/Numeric.ts b/packages/core/src/encoder/segments/Numeric.ts index 8e22d8df..80f8b726 100644 --- a/packages/core/src/encoder/segments/Numeric.ts +++ b/packages/core/src/encoder/segments/Numeric.ts @@ -52,23 +52,23 @@ export class Numeric { */ public encode(): BitArray { const bits = new BitArray(); - const content = this.#content; + const content = Array.from(this.#content); const { length } = content; for (let i = 0; i < length; ) { - const code1 = getNumericCode(content.charAt(i)); + const code1 = getNumericCode(content[i]); if (i + 2 < length) { // Encode three numeric letters in ten bits. - const code2 = getNumericCode(content.charAt(i + 1)); - const code3 = getNumericCode(content.charAt(i + 2)); + const code2 = getNumericCode(content[i + 1]); + const code3 = getNumericCode(content[i + 2]); bits.append(code1 * 100 + code2 * 10 + code3, 10); i += 3; } else if (i + 1 < length) { // Encode two numeric letters in seven bits. - const code2 = getNumericCode(content.charAt(i + 1)); + const code2 = getNumericCode(content[i + 1]); bits.append(code1 * 10 + code2, 7); diff --git a/packages/core/src/encoder/utils/encoder.ts b/packages/core/src/encoder/utils/encoder.ts index c75612cd..d8342647 100644 --- a/packages/core/src/encoder/utils/encoder.ts +++ b/packages/core/src/encoder/utils/encoder.ts @@ -26,7 +26,7 @@ export interface Hints { export interface SegmentBlock { mode: Mode; head: BitArray; - data: BitArray; + body: BitArray; length: number; } @@ -177,6 +177,17 @@ export function appendFNC1Info(bits: BitArray, fnc1: FNC1): void { } } +export function getSegmentLength(segment: Segment, bits: BitArray): number { + // Byte segment use byte Length. + if (isByteMode(segment)) { + return bits.byteLength; + } + + // Other segments use the real length of characters. + // All rest segments content codePointAt at 0x0000 to 0xffff, so use length directly. + return segment.content.length; +} + export function appendLengthInfo(bits: BitArray, mode: Mode, version: Version, numLetters: number): void { bits.append(numLetters, mode.getCharacterCountBits(version)); } @@ -202,8 +213,8 @@ function chooseVersion(numInputBits: number, ecLevel: ECLevel): Version { export function calculateBitsNeeded(segmentBlocks: SegmentBlock[], version: Version): number { let bitsNeeded = 0; - for (const { mode, head, data } of segmentBlocks) { - bitsNeeded += head.length + mode.getCharacterCountBits(version) + data.length; + for (const { mode, head, body } of segmentBlocks) { + bitsNeeded += head.length + mode.getCharacterCountBits(version) + body.length; } return bitsNeeded;