Skip to content

Commit

Permalink
feat: Update encoder
Browse files Browse the repository at this point in the history
  • Loading branch information
nuintun committed Mar 17, 2024
1 parent 3537590 commit da43fe3
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 46 deletions.
20 changes: 10 additions & 10 deletions packages/core/src/common/encoding/mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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],
Expand All @@ -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);
12 changes: 8 additions & 4 deletions packages/core/src/common/encoding/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
* @module utils
*/

import { charAt } from '/common/utils';

export type EncodingRange = [start: number, end: number];

export function getCharactersMapping(characters: string): Map<string, number> {
export function getMappingFromCharacters(characters: string): Map<string, number> {
let code = 0;

const mapping = new Map<string, number>();
Expand All @@ -16,15 +18,17 @@ export function getCharactersMapping(characters: string): Map<string, number> {
return mapping;
}

export function getEncodingMapping(label: string, ...ranges: EncodingRange[]): Map<string, number> {
export function getMappingFromEncodingRanges(label: string, ...ranges: EncodingRange[]): Map<string, number> {
const bytes: number[] = [];
const codes: number[] = [];
const mapping: Map<string, number> = new Map();
const decoder = new TextDecoder(label, { fatal: true });

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);
}
}
Expand All @@ -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]);
Expand All @@ -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[] = [];

Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 11 additions & 10 deletions packages/core/src/decoder/utils/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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) {
Expand All @@ -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;
Expand Down
22 changes: 11 additions & 11 deletions packages/core/src/encoder/Encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
calculateBitsNeeded,
chooseBestMaskAndMatrix,
chooseRecommendVersion,
getSegmentLength,
Hints,
injectECCodewords,
isByteMode,
isHanziMode,
Segment,
SegmentBlock,
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/encoder/segments/Alphanumeric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/encoder/segments/Numeric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
17 changes: 14 additions & 3 deletions packages/core/src/encoder/utils/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface Hints {
export interface SegmentBlock {
mode: Mode;
head: BitArray;
data: BitArray;
body: BitArray;
length: number;
}

Expand Down Expand Up @@ -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));
}
Expand All @@ -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;
Expand Down

0 comments on commit da43fe3

Please sign in to comment.