diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index 09ee8d15..47d6a147 100644
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -63,7 +63,9 @@
"JSON to CSV",
"Avro to JSON",
"CBOR Encode",
- "CBOR Decode"
+ "CBOR Decode",
+ "From GSM-7",
+ "To GSM-7"
]
},
{
diff --git a/src/core/lib/Gsm7.mjs b/src/core/lib/Gsm7.mjs
new file mode 100644
index 00000000..7728a838
--- /dev/null
+++ b/src/core/lib/Gsm7.mjs
@@ -0,0 +1,657 @@
+/**
+ * GSM-7 functions.
+ *
+ * @author edouard hinard []
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import OperationError from "../errors/OperationError.mjs";
+
+/**
+ * Encoding tables from 3GPP document:
+ * Technical Specification
+ * 3rd Generation Partnership Project;
+ * Technical Specification Group Core Network and Terminals;
+ * Alphabets and language-specific information
+ * (Release 16)
+ * 3GPP TS 23.038 V16.0.0 (2020-07)
+ */
+const charsets = {
+ // 6.2.1 GSM 7 bit Default Alphabet
+ Default: `
+ @ Δ SP 0 ¡ P ¿ p
+ £ _ ! 1 A Q a q
+ $ Φ " 2 B R b r
+ ¥ Γ # 3 C S c s
+ è Λ ¤ 4 D T d t
+ é Ω % 5 E U e u
+ ù Π & 6 F V f v
+ ì Ψ ' 7 G W g w
+ ò Σ ( 8 H X h x
+ Ç Θ ) 9 I Y i y
+ LF Ξ * : J Z j z
+ Ø 1) + ; K Ä k ä
+ ø Æ , < L Ö l ö
+ CR æ - = M Ñ m ñ
+ Å ß . > N Ü n ü
+ å É / ? O § o à
+ `,
+
+ // A.3.1 Turkish National Language Locking Shift Table
+ Turkish: `
+ @ Δ SP 0 İ P ç p
+ £ _ ! 1 A Q a q
+ $ Φ " 2 B R b r
+ ¥ Γ # 3 C S c s
+ € Λ ¤ 4 D T d t
+ é Ω % 5 E U e u
+ ù Π & 6 F V f v
+ ı Ψ ' 7 G W g w
+ ò Σ ( 8 H X h x
+ Ç Θ ) 9 I Y i y
+ LF Ξ * : J Z j z
+ Ğ 1) + ; K Ä k ä
+ ğ Ş , < L Ö l ö
+ CR ş - = M Ñ m ñ
+ Å ß . > N Ü n ü
+ å É / ? O § o à
+ `,
+
+ // A.3.3 Portuguese National Language Locking Shift Table
+ Portuguese: `
+ @ Δ SP 0 Í P ~ p
+ £ _ ! 1 A Q a q
+ $ ª " 2 B R b r
+ ¥ Ç # 3 C S c s
+ ê À º 4 D T d t
+ é ∞ % 5 E U e u
+ ú ^ & 6 F V f v
+ í \ ' 7 G W g w
+ ó € ( 8 H X h x
+ ç Ó ) 9 I Y i y
+ LF | * : J Z j z
+ Ô 1) + ; K Ã k ã
+ ô Â , < L Õ l õ
+ CR â - = M Ú m \`
+ Á Ê . > N Ü n ü
+ á É / ? O § o à
+ `,
+
+ // A.3.4 Bengali National Language Locking Shift Table
+ Bengali: `
+ 0981 0990 SP 0 09AC 09BE 09CE p
+ 0982 0000 ! 1 09AD 09BF a q
+ 0983 0000 099F 2 09AE 09C0 b r
+ 0985 0993 09A0 3 09AF 09C1 c s
+ 0986 0994 09A1 4 09B0 09C2 d t
+ 0987 0995 09A2 5 0000 09C3 e u
+ 0988 0996 09A3 6 09B2 09C4 f v
+ 0989 0997 09A4 7 0000 0000 g w
+ 098A 0998 ) 8 0000 0000 h x
+ 098B 0999 ( 9 0000 09C7 i y
+ LF 099A 09A5 : 09B6 09C8 j z
+ 098C 1) 09A6 ; 09B7 0000 k 09D7
+ 0000 099B , 0000 09B8 0000 l 09DC
+ CR 099C 09A7 09AA 09B9 09CB m 09DD
+ 0000 099D . 09AB 09BC 09CC n 09F0
+ 098F 099E 09A8 ? 09BD 09CD o 09F1
+ `,
+
+ // A.3.5 Gujarati National Language Locking Shift Table
+ Gujarati: `
+ 0A81 0A90 SP 0 0AAC 0ABE 0AD0 p
+ 0A82 0A91 ! 1 0AAD 0ABF a q
+ 0A83 0000 0A9F 2 0AAE 0AC0 b r
+ 0A85 0A93 0AA0 3 0AAF 0AC1 c s
+ 0A86 0A94 0AA1 4 0AB0 0AC2 d t
+ 0A87 0A95 0AA2 5 0000 0AC3 e u
+ 0A88 0A96 0AA3 6 0AB2 0AC4 f v
+ 0A89 0A97 0AA4 7 0AB3 0AC5 g w
+ 0A8A 0A98 ) 8 0000 0000 h x
+ 0A8B 0A99 ( 9 0AB5 0AC7 i y
+ LF 0A9A 0AA5 : 0AB6 0AC8 j z
+ 0A8C 1) 0AA6 ; 0AB7 0AC9 k 0AE0
+ 0A8D 0A9B , 0000 0AB8 0000 l 0AE1
+ CR 0A9C 0AA7 0AAA 0AB9 0ACB m 0AE2
+ 0000 0A9D . 0AAB 0ABC 0ACC n 0AE3
+ 0A8F 0A9E 0AA8 ? 0ABD 0ACD o 0AF1
+ `,
+
+ // A.3.6 Hindi National Language Locking Shift Table
+ Hindi: `
+ 0901 0910 SP 0 092C 093E 0950 p
+ 0902 0911 ! 1 092D 093F a q
+ 0903 0912 091F 2 092E 0940 b r
+ 0905 0913 0920 3 092F 0941 c s
+ 0906 0914 0921 4 0930 0942 d t
+ 0907 0915 0922 5 0931 0943 e u
+ 0908 0916 0923 6 0932 0944 f v
+ 0909 0917 0924 7 0933 0945 g w
+ 090A 0918 ) 8 0934 0946 h x
+ 090B 0919 ( 9 0935 0947 i y
+ LF 091A 0925 : 0936 0948 j z
+ 090C 1) 0926 ; 0937 0949 k 0972
+ 090D 091B , 0929 0938 094A l 097B
+ CR 091C 0927 092A 0939 094B m 097C
+ 090E 091D . 092B 093C 094C n 097E
+ 090F 091E 0928 ? 093D 094D o 097F
+ `,
+
+ //A.3.7 Kannada National Language Locking Shift Table
+ Kannada: `
+ 0000 0C90 SP 0 0CAC 0CBE 0CD5 p
+ 0C82 0000 ! 1 0CAD 0CBF a q
+ 0C83 0C92 0C9F 2 0CAE 0CC0 b r
+ 0C85 0C93 0CA0 3 0CAF 0CC1 c s
+ 0C86 0C94 0CAA 4 0CB0 0CC2 d t
+ 0C87 0C95 0CA2 5 0CB1 0CC3 e u
+ 0C88 0C96 0CA3 6 0CB2 0CC4 f v
+ 0C89 0C97 0CA4 7 0CB3 0000 g w
+ 0C8A 0C98 ) 8 0000 0CC6 h x
+ 0C8B 0C99 ( 9 0CB5 0CC7 i y
+ LF 0C9A 0CA5 : 0CB6 0CC8 j z
+ 0C8C 1) 0CA6 ; 0CB7 0000 k 0CD6
+ 0000 0C9B , 0000 0CB8 0CCA l 0CE0
+ CR 0C9C 0CA7 0CAA 0CB9 0CCB m 0CE1
+ 0C8E 0C9D . 0CAB 0CBC 0CCC n 0CE2
+ 0C8F 0C9E 0CA8 ? 0CBD 0CCD o 0CE3
+ `,
+
+ // A.3.8 Malayalam National Language Locking Shift Table
+ Malayalam: `
+ 0000 0D10 SP 0 0D2C 0D3E 0D57 p
+ 0D02 0000 ! 1 0D2D 0D3F a q
+ 0D03 0D12 0D1F 2 0D2E 0D40 b r
+ 0D05 0D13 0D20 3 0D2F 0D41 c s
+ 0D06 0D14 0D21 4 0D30 0D42 d t
+ 0D07 0D15 0D22 5 0D31 0D43 e u
+ 0D08 0D16 0D23 6 0D32 0D44 f v
+ 0D09 0D17 0D24 7 0D33 0000 g w
+ 0D0A 0D18 ) 8 0D34 0D46 h x
+ 0D0B 0D19 ( 9 0D35 0D47 i y
+ LF 0D1A 0D25 : 0D36 0D48 j z
+ 0D0C 1) 0D26 ; 0D37 0000 k 0D60
+ 0000 0D1B , 0000 0D38 0D4A l 0D61
+ CR 0D1C 0D27 0D2A 0D39 0D4B m 0D62
+ 0D0E 0D1D . 0D2B 0000 0D4C n 0D63
+ 0D0F 0D1E 0D28 ? 0D3D 0D4D o 0D79
+ `,
+
+ // A.3.9 Oriya National Language Locking Shift Table
+ Oriya: `
+ 0B01 0B10 SP 0 0B2C 0B3E 0B56 p
+ 0B02 0000 ! 1 0B2D 0B3F a q
+ 0B03 0000 0B1F 2 0B2E 0B40 b r
+ 0B05 0B13 0B20 3 0B2F 0B41 c s
+ 0B06 0B14 0B21 4 0B30 0B42 d t
+ 0B07 0B15 0B22 5 0000 0B43 e u
+ 0B08 0B16 0B23 6 0B32 0B44 f v
+ 0B09 0B17 0B24 7 0B33 0000 g w
+ 0B0A 0B18 ) 8 0000 0000 h x
+ 0B0B 0B19 ( 9 0B35 0B47 i y
+ LF 0B1A 0B25 : 0B36 0B48 j z
+ 0B0C 1) 0B26 ; 0B37 0000 k 0B57
+ 0000 0B1B , 0000 0B38 0000 l 0B60
+ CR 0B1C 0B27 0B2A 0B39 0B4B m 0B61
+ 0000 0B1D . 0B2B 0B3C 0B4C n 0B62
+ 0B0F 0B1E 0B28 ? 0B3D 0B4D o 0B63
+ `,
+
+ // A.3.10 Punjabi National Language Locking Shift Table
+ Punjabi: `
+ 0A01 0A10 SP 0 0A2C 0A3E 0A51 p
+ 0A02 0000 ! 1 0A2D 0A3F a q
+ 0A03 0000 0A1F 2 0A2E 0A40 b r
+ 0A05 0A13 0A20 3 0A2F 0A41 c s
+ 0A06 0A14 0A21 4 0A30 0A42 d t
+ 0A07 0A15 0A22 5 0000 0000 e u
+ 0A08 0A16 0A23 6 0A32 0000 f v
+ 0A09 0A17 0A24 7 0A33 0000 g w
+ 0A0A 0A18 ) 8 0000 0000 h x
+ 0000 0A19 ( 9 0A35 0A47 i y
+ LF 0A1A 0A25 : 0A36 0A48 j z
+ 0000 1) 0A26 ; 0000 0000 k 0A70
+ 0000 0A1B , 0000 0A38 0000 l 0A71
+ CR 0A1C 0A27 0A2A 0A39 0A4B m 0A72
+ 0000 0A1D . 0A2B 0A3C 0A4C n 0A73
+ 0A0F 0A1E 0A28 ? 0000 0A4D o 0A74
+ `,
+
+ // A.3.11 Tamil National Language Locking Shift Table
+ Tamil: `
+ 0000 0B90 SP 0 0000 0BBE 0BD0 p
+ 0B82 0B91 ! 1 0000 0BBF a q
+ 0B83 0B92 0B9F 2 0BAE 0BC0 b r
+ 0B85 0B93 0000 3 0BAF 0BC1 c s
+ 0B86 0B94 0000 4 0BB0 0BC2 d t
+ 0B87 0B95 0000 5 0BB1 0000 e u
+ 0B88 0000 0BA3 6 0BB2 0000 f v
+ 0B89 0000 0BA4 7 0BB3 0000 g w
+ 0B8A 0000 ) 8 0BB4 0BC6 h x
+ 0000 0B99 ( 9 0BB5 0BC7 i y
+ LF 0B9A 0000 : 0BB6 0BC8 j z
+ 0000 1) 0000 ; 0BB7 0000 k 0BD7
+ 0000 0000 , 0BA9 0BB8 0BCA l 0BF0
+ CR 0B9C 0000 0BAA 0BB9 0BCB m 0BF1
+ 0B8E 0000 . 0000 0000 0BCC n 0BF2
+ 0B8F 0B9E 0BA8 ? 0000 0BCD o 0BF9
+ `,
+
+ // A.3.12 Telugu National Language Locking Shift Table
+ Telugu: `
+ 0C01 0c10 SP 0 0C2C 0C3E 0C55 p
+ 0C02 0C11 ! 1 0C2D 0C3F a q
+ 0C03 0C12 0C1F 2 0C2E 0C40 b r
+ 0C05 0C13 0C20 3 0C2F 0C41 c s
+ 0C06 0C14 0C21 4 0C30 0C42 d t
+ 0C07 0C15 0C22 5 0C31 0C43 e u
+ 0C08 0C16 0C23 6 0C32 0C44 f v
+ 0C09 0C17 0C24 7 0C33 0000 g w
+ 0C0A 0C18 ) 8 0000 0C46 h x
+ 0C0B 0C19 ( 9 0C35 0C47 i y
+ LF 0C1A 0C25 : 0C36 0C48 j z
+ 0C0C 1) 0C26 ; 0C37 0000 k 0C56
+ 0000 0C1B , 0000 0C38 0C4A l 0C60
+ CR 0C1C 0C27 0C2A 0C39 0C4B m 0C61
+ 0C0E 0C1D . 0C2B 0000 0C4C n 0C62
+ 0C0F 0C1E 0C28 ? 0C3D 0C4D o 0C63
+ `,
+
+ // A.3.13 Urdu National Language Locking Shift Table
+ Urdu: `
+ 0627 062B SP 0 0635 06BA 0654 p
+ 0622 062C ! 1 0636 06BB a q
+ 0628 0681 068F 2 0637 06BC b r
+ 067B 0684 068D 3 0638 0648 c s
+ 0680 0683 0630 4 0639 06C4 d t
+ 067E 0685 0631 5 0641 06D5 e u
+ 06A6 0686 0691 6 0642 06C1 f v
+ 062A 0687 0693 7 06A9 06BE g w
+ 06C2 062D ) 8 06AA 0621 h x
+ 067F 062E ( 9 06AB 06CC i y
+ LF 062F 0699 : 06AF 06D0 j z
+ 0679 1) 0632 ; 06B3 06D2 k 0655
+ 067D 068C , 069A 06B1 064D l 0651
+ CR 0688 0696 0633 0644 0650 m 0653
+ 067A 0689 . 0634 0645 064F n 0656
+ 067C 068A 0698 ? 0646 0657 o 0670
+ `
+}
+
+const extensions = {
+ // 6.2.1.1 GSM 7 bit default alphabet extension table
+ Default: `
+ 0000 0000 0000 0000 | 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 ^ 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 € 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 { 0000 0000 0000 0000 0000
+ 0000 0000 } 0000 0000 0000 0000 0000
+ 3) 0000 0000 0000 0000 0000 0000 0000
+ 0000 1) 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 [ 0000 0000 0000 0000
+ 0000 0000 0000 ~ 0000 0000 0000 0000
+ 0000 0000 0000 ] 0000 0000 0000 0000
+ 0000 0000 \ 0000 0000 0000 0000 0000
+ `,
+
+ // A.2.1 Turkish National Language Single Shift Table
+ Turkish: `
+ 0000 0000 0000 0000 | 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 Ş ç ş
+ 0000 ^ 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 € 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 Ğ 0000 ğ 0000
+ 0000 0000 { 0000 0000 0000 0000 0000
+ 0000 0000 } 0000 İ 0000 ı 0000
+ 3) 0000 0000 0000 0000 0000 0000 0000
+ 0000 1) 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 [ 0000 0000 0000 0000
+ 4) 0000 0000 ~ 0000 0000 0000 0000
+ 0000 0000 0000 ] 0000 0000 0000 0000
+ 0000 0000 \ 0000 0000 0000 0000 0000
+ `,
+
+ // A.2.2 Spanish National Language Single Shift Table
+ Spanish: `
+ 0000 0000 0000 0000 | 0000 0000 0000
+ 0000 0000 0000 0000 Á 0000 á 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 ^ 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 Ú € ú
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0000
+ 0000 0000 { 0000 0000 0000 0000 0000
+ ç 0000 } 0000 Í 0000 í 0000
+ 3) 0000 0000 0000 0000 0000 0000 0000
+ 0000 1) 0000 0000 0000 0000 0000 0000
+ 0000 0000 0000 [ 0000 0000 0000 0000
+ 4) 0000 0000 ~ 0000 0000 0000 0000
+ 0000 0000 0000 ] 0000 0000 0000 0000
+ 0000 0000 \ 0000 Ó 0000 ó 0000
+ `,
+
+ // A.2.3 Portuguese National Language Single Shift Table
+ Portuguese: `
+ 0000 0000 0000 0000 | 0000 0000 0000
+ 0000 0000 0000 0000 À 0000 Â 0000
+ 0000 Φ 0000 0000 0000 0000 0000 0000
+ 0000 Γ 0000 0000 0000 0000 0000 0000
+ 0000 ^ 0000 0000 0000 0000 0000 0000
+ 0000 Ω 0000 0000 0000 Ú € ú
+ 0000 Π 0000 0000 0000 0000 0000 0000
+ 0000 Ψ 0000 0000 0000 0000 0000 0000
+ 0000 Σ { 0000 0000 0000 0000 0000
+ ç Θ } 0000 Í 0000 í 0000
+ 3) 0000 0000 0000 0000 0000 0000 0000
+ Ô 1) 0000 0000 0000 Ã 0000 ã
+ ô 0000 0000 [ 0000 Õ 0000 õ
+ 4) 0000 0000 ~ 0000 0000 0000 0000
+ Á 0000 0000 ] 0000 0000 0000 0000
+ á Ê \ 0000 Ó 0000 ó â
+ `,
+
+ // A.2.4 Bengali National Language Single Shift Table
+ Bengali: `
+ @ < 09EC 09F6 | P 0000 0000
+ £ = 09ED 09F7 A Q 0000 0000
+ $ > 09EE 09F8 B R 0000 0000
+ ¥ ¡ 09EF 09F9 C S 0000 0000
+ ¿ ^ 09DF 09FA D T 0000 0000
+ " ¡ 09E0 0000 E U € 0000
+ ¤ _ 09E1 0000 F V 0000 0000
+ % # 09E2 0000 G W 0000 0000
+ & * { 0000 H X 0000 0000
+ ' 09E6 } 0000 I Y 0000 0000
+ 3) 09E7 09E3 0000 J Z 0000 0000
+ * 1) 09F2 0000 K 0000 0000 0000
+ + 09E8 09F3 [ L 0000 0000 0000
+ 4) 09E9 09F4 ~ M 0000 0000 0000
+ - 09EA 09F5 ] N 0000 0000 0000
+ / 09EB \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.5 Gujarati National Language Single Shift Table
+ Gujarati: `
+ @ < 0AEA 0000 | P 0000 0000
+ £ = 0AEB 0000 A Q 0000 0000
+ $ > 0AEC 0000 B R 0000 0000
+ ¥ ¡ 0AED 0000 C S 0000 0000
+ ¿ ^ 0AEE 0000 D T 0000 0000
+ " ¡ 0AEF 0000 E U € 0000
+ ¤ _ 0000 0000 F V 0000 0000
+ % # 0000 0000 G W 0000 0000
+ & * { 0000 H X 0000 0000
+ ' 0964 } 0000 I Y 0000 0000
+ 3) 0965 0000 0000 J Z 0000 0000
+ * 1) 0000 0000 K 0000 0000 0000
+ + 0AE6 0000 [ L 0000 0000 0000
+ 4) 0AE7 0000 ~ M 0000 0000 0000
+ - 0AE8 0000 ] N 0000 0000 0000
+ / 0AE9 \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.6 Hindi National Language Single Shift Table
+ Hindi: `
+ @ < 096A 095B | P 0000 0000
+ £ = 096B 095C A Q 0000 0000
+ $ > 096C 095D B R 0000 0000
+ ¥ ¡ 096D 095E C S 0000 0000
+ ¿ ^ 096E 095F D T 0000 0000
+ " ¡ 096F 0960 E U € 0000
+ ¤ _ 0951 0961 F V 0000 0000
+ % # 0952 0962 G W 0000 0000
+ & * { 0963 H X 0000 0000
+ ' 0964 } 0970 I Y 0000 0000
+ 3) 0965 0953 0971 J Z 0000 0000
+ * 1) 0954 0000 K 0000 0000 0000
+ + 0966 0958 [ L 0000 0000 0000
+ 4) 0967 0959 ~ M 0000 0000 0000
+ - 0968 095A ] N 0000 0000 0000
+ / 0969 \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.7 Kannada National Language Single Shift Table
+ Kannada: `
+ @ < 0CEA 0000 | P 0000 0000
+ £ = 0CEB 0000 A Q 0000 0000
+ $ > 0CEC 0000 B R 0000 0000
+ ¥ ¡ 0CED 0000 C S 0000 0000
+ ¿ ^ 0CEE 0000 D T 0000 0000
+ " ¡ 0CEF 0000 E U € 0000
+ ¤ _ 0CDE 0000 F V 0000 0000
+ % # 0CF1 0000 G W 0000 0000
+ & * { 0000 H X 0000 0000
+ ' 0964 } 0000 I Y 0000 0000
+ 3) 0965 0CF2 0000 J Z 0000 0000
+ * 1) 0000 0000 K 0000 0000 0000
+ + 0CE6 0000 [ L 0000 0000 0000
+ 4) 0CE7 0000 ~ M 0000 0000 0000
+ - 0CE8 0000 ] N 0000 0000 0000
+ / 0CE9 \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.8 Malayalam National Language Single Shift Table
+ Malayalam: `
+ @ < 0D6A 0D7B | P 0000 0000
+ £ = 0D6B 0D7C A Q 0000 0000
+ $ > 0D6C 0D7D B R 0000 0000
+ ¥ ¡ 0D6D 0D7E C S 0000 0000
+ ¿ ^ 0D6E 0D7F D T 0000 0000
+ " ¡ 0D6F 0000 E U € 0000
+ ¤ _ 0D70 0000 F V 0000 0000
+ % # 0D71 0000 G W 0000 0000
+ & * { 0000 H X 0000 0000
+ ' 0964 } 0000 I Y 0000 0000
+ 3) 0965 0D72 0000 J Z 0000 0000
+ * 1) 0D73 0000 K 0000 0000 0000
+ + 0D66 0D74 [ L 0000 0000 0000
+ 4) 0D67 0D75 ~ M 0000 0000 0000
+ - 0D68 0D7A ] N 0000 0000 0000
+ / 0D69 \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.9 Oriya National Language Single Shift Table
+ Oriya: `
+ @ < 0BEA 0000 | P 0000 0000
+ £ = 0B6B 0000 A Q 0000 0000
+ $ > 0B6C 0000 B R 0000 0000
+ ¥ ¡ 0B6D 0000 C S 0000 0000
+ ¿ ^ 0B6E 0000 D T 0000 0000
+ " ¡ 0B6F 0000 E U € 0000
+ ¤ _ 0B5C 0000 F V 0000 0000
+ % # 0B5D 0000 G W 0000 0000
+ & * { 0000 H X 0000 0000
+ ' 0964 } 0000 I Y 0000 0000
+ 3) 0965 0B5F 0000 J Z 0000 0000
+ * 1) 0B70 0000 K 0000 0000 0000
+ + 0B66 0B71 [ L 0000 0000 0000
+ 4) 0B67 0000 ~ M 0000 0000 0000
+ - 0B68 0000 ] N 0000 0000 0000
+ / 0B69 \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.10 Punjabi National Language Single Shift Table
+ Punjabi: `
+ @ < 0A6A 0000 | P 0000 0000
+ £ = 0A6B 0000 A Q 0000 0000
+ $ > 0A6C 0000 B R 0000 0000
+ ¥ ¡ 0A6D 0000 C S 0000 0000
+ ¿ ^ 0A6E 0000 D T 0000 0000
+ " ¡ 0A6F 0000 E U € 0000
+ ¤ _ 0A59 0000 F V 0000 0000
+ % # 0A5A 0000 G W 0000 0000
+ & * { 0000 H X 0000 0000
+ ' 0964 } 0000 I Y 0000 0000
+ 3) 0965 0A5B 0000 J Z 0000 0000
+ * 1) 0A5C 0000 K 0000 0000 0000
+ + 0A66 0A5E [ L 0000 0000 0000
+ 4) 0A67 0A75 ~ M 0000 0000 0000
+ - 0A68 0000 ] N 0000 0000 0000
+ / 0A69 \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.11 Tamil National Language Single Shift Table
+ // In 3GPP document, Tamil character at position 0x24 is 0BEF
+ // current implementation uses 0BEE instead
+ Tamil: `
+ @ < 0BEA 0000 | P 0000 0000
+ £ = 0BEB 0000 A Q 0000 0000
+ $ > 0BEC 0000 B R 0000 0000
+ ¥ ¡ 0BED 0000 C S 0000 0000
+ ¿ ^ 0BEE 0000 D T 0000 0000
+ " ¡ 0BEF 0000 E U € 0000
+ ¤ _ 0BF3 0000 F V 0000 0000
+ % # 0BF4 0000 G W 0000 0000
+ & * { 0000 H X 0000 0000
+ ' 0964 } 0000 I Y 0000 0000
+ 3) 0965 0BF5 0000 J Z 0000 0000
+ * 1) 0BF6 0000 K 0000 0000 0000
+ + 0BE6 0BF7 [ L 0000 0000 0000
+ 4) 0BE7 0BF8 ~ M 0000 0000 0000
+ - 0BE8 0BFA ] N 0000 0000 0000
+ / 0BE9 \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.12 Telugu National Language Single Shift Table
+ Telugu: `
+ @ < 0C6A 0C7D | P 0000 0000
+ £ = 0C6B 0C7E A Q 0000 0000
+ $ > 0C6C 0C7F B R 0000 0000
+ ¥ ¡ 0C6D 0000 C S 0000 0000
+ ¿ ^ 0C6E 0000 D T 0000 0000
+ " ¡ 0C6F 0000 E U € 0000
+ ¤ _ 0C58 0000 F V 0000 0000
+ % # 0C59 0000 G W 0000 0000
+ & * { 0000 H X 0000 0000
+ ' 0000 } 0000 I Y 0000 0000
+ 3) 0000 0C78 0000 J Z 0000 0000
+ * 1) 0C79 0000 K 0000 0000 0000
+ + 0CE6 0C7A [ L 0000 0000 0000
+ 4) 0C67 0C7B ~ M 0000 0000 0000
+ - 0C68 0C7C ] N 0000 0000 0000
+ / 0C69 \ 0000 O 0000 0000 0000
+ `,
+
+ // A.2.13 Urdu National Language Single Shift Table
+ Urdu: `
+ @ < 06F4 0613 | P 0000 0000
+ £ = 06F5 0614 A Q 0000 0000
+ $ > 06F6 061B B R 0000 0000
+ ¥ ¡ 06F7 061F C S 0000 0000
+ ¿ ^ 06F8 0640 D T 0000 0000
+ " ¡ 06F9 0652 E U € 0000
+ ¤ _ 060C 0658 F V 0000 0000
+ % # 060D 066B G W 0000 0000
+ & * { 066C H X 0000 0000
+ ' 0600 } 0672 I Y 0000 0000
+ 3) 0601 060E 0673 J Z 0000 0000
+ * 1) 060F 06CD K 0000 0000 0000
+ + 06F0 0610 [ L 0000 0000 0000
+ 4) 06F1 0611 ~ M 0000 0000 0000
+ - 06F2 0612 ] N 0000 0000 0000
+ / 06F3 \ 06D4 O 0000 0000 0000
+ `
+}
+
+// Special characters in tables
+const specials = {
+ '1)': '\x1b',
+ '3)': '\x0c',
+ '4)': '\r',
+ 'SP': ' ',
+ 'CR': '\r',
+ 'LF': '\n'
+}
+
+/**
+ * Conversion function that turn a table where characters are listed in 8 columns of 16 rows
+ * into an array of 128 unicode characters
+ */
+function converttable(table) {
+ console.log(table);
+/* conv = [0x00] * 128
+ chars = table.strip().split()
+ for i in range(128):
+ char = chars[(i//16) + (i%16)*8]
+ char = specials.get(char, char)
+ if len(char) == 4:
+ char = chr(int(char, 16))
+ conv[i] = char
+ return conv*/
+}
+
+/**
+ * Conversion of charsets and extensions tables
+ */
+/*for lang,table in charsets.items():
+ charset = converttable(table)
+ charsets[lang] = charset
+for lang,table in extensions.items():
+ extension = converttable(table)
+ extensions[lang] = extension
+*/
+
+
+/**
+ * Charsets and extensions
+ */
+export const CHARSET_OPTIONS = [
+ {name: "Default", value: charsets['Default']}
+];
+export const EXTENSION_OPTIONS = [
+ {name: "Default", value: extensions['Default']}
+];
+
+
+/**
+ * GSM-7 encode the input text as a SMS
+ *
+ * @param {string} text
+ * @param {array} [charset]
+ * @param {array} [extension]
+ * @param {boolean} [CRpad=true]
+ * @returns {ArrayBuffer}
+ *
+ * @example
+ * // returns "SGVsbG8="
+ * toBase64([72, 101, 108, 108, 111]);
+ *
+ * // returns "SGVsbG8="
+ * toBase64("Hello");
+ */
+export function toGsm7(text, alphabet=charsets['Default'], extension=extensions['Default'], CRpad=true) {
+ let output = [];
+ return output;
+}
+
+
+/**
+ * GSM-7 decode the input SMS as text
+ *
+ * @param {string} data
+ * @param {string} [alphabet="A-Za-z0-9+/="]
+ * @param {string} [returnType="string"] - Either "string" or "byteArray"
+ * @param {boolean} [removeNonAlphChars=true]
+ * @returns {byteArray}
+ *
+ * @example
+ * // returns "Hello"
+ * fromBase64("SGVsbG8=");
+ *
+ * // returns [72, 101, 108, 108, 111]
+ * fromBase64("SGVsbG8=", null, "byteArray");
+ */
+export function fromGsm7(sms, alphabet=charsets['Default'], extension=extensions['Default'], CRpad=true) {
+ return "";
+}
+
diff --git a/src/core/operations/FromGSM7.mjs b/src/core/operations/FromGSM7.mjs
new file mode 100644
index 00000000..7981cdb1
--- /dev/null
+++ b/src/core/operations/FromGSM7.mjs
@@ -0,0 +1,59 @@
+/**
+ * @author edouard hinard []
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import {fromGsm7, CHARSET_OPTIONS, EXTENSION_OPTIONS} from "../lib/Gsm7.mjs";
+
+/**
+ * FromGSM7 operation
+ */
+class FromGSM7 extends Operation {
+
+ /**
+ * FromGSM7 constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "From GSM-7";
+ this.module = "Default";
+ this.description = "GSM-7 is a 3GPP standard (TS23.038) for SMS encoding.
This operation decodes a raw SMS into text.";
+ this.infoURL = "https://en.wikipedia.org/wiki/GSM_03.38";
+ this.inputType = "byteArray";
+ this.outputType = "string";
+ this.args = [
+ {
+ name: "Charset",
+ type: "populateOption",
+ value: CHARSET_OPTIONS
+ },
+ {
+ name: "Extension",
+ type: "populateOption",
+ value: EXTENSION_OPTIONS
+ },
+ {
+ name: "remove CR padding",
+ type: "boolean",
+ value: true
+ }
+ ];
+ }
+
+ /**
+ * @param {byteArray} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ const [charset, extension, CRpad] = args;
+
+ return fromGsm7(input, charset, extension, CRpad);
+ }
+
+}
+
+export default FromGSM7;
diff --git a/src/core/operations/ToGSM7.mjs b/src/core/operations/ToGSM7.mjs
new file mode 100644
index 00000000..43a0bd88
--- /dev/null
+++ b/src/core/operations/ToGSM7.mjs
@@ -0,0 +1,59 @@
+/**
+ * @author edouard hinard []
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import {toGsm7, CHARSET_OPTIONS, EXTENSION_OPTIONS} from "../lib/Gsm7.mjs";
+
+/**
+ * ToGSM7 operation
+ */
+class ToGSM7 extends Operation {
+
+ /**
+ * ToGSM7 constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "To GSM-7";
+ this.module = "Default";
+ this.description = "GSM-7 is a 3GPP standard (TS23.038) for SMS encoding.
This operation decodes a raw SMS into text.";
+ this.infoURL = "https://en.wikipedia.org/wiki/GSM_03.38";
+ this.inputType = "string";
+ this.outputType = "byteArray";
+ this.args = [
+ {
+ name: "Charset",
+ type: "populateOption",
+ value: CHARSET_OPTIONS
+ },
+ {
+ name: "Extension",
+ type: "populateOption",
+ value: EXTENSION_OPTIONS
+ },
+ {
+ name: "CR padding",
+ type: "boolean",
+ value: true
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {byteArray}
+ */
+ run(input, args) {
+ const [charset, extension, CRpad] = args;
+
+ return toGsm7(input, charset, extension, CRpad);
+ }
+
+}
+
+export default ToGSM7;
diff --git a/tests/node/index.mjs b/tests/node/index.mjs
index f6abba40..4a696185 100644
--- a/tests/node/index.mjs
+++ b/tests/node/index.mjs
@@ -16,13 +16,13 @@ import {
} from "../lib/utils.mjs";
import TestRegister from "../lib/TestRegister.mjs";
-import "./tests/nodeApi.mjs";
+/*import "./tests/nodeApi.mjs";
import "./tests/operations.mjs";
import "./tests/File.mjs";
import "./tests/Dish.mjs";
import "./tests/NodeDish.mjs";
import "./tests/Utils.mjs";
-import "./tests/Categories.mjs";
+import "./tests/Categories.mjs";*/
const testStatus = {
allTestsPassing: true,
diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs
index 9add20b9..f6205cd9 100644
--- a/tests/operations/index.mjs
+++ b/tests/operations/index.mjs
@@ -17,7 +17,7 @@ import {
} from "../lib/utils.mjs";
import TestRegister from "../lib/TestRegister.mjs";
-import "./tests/BCD.mjs";
+/*import "./tests/BCD.mjs";
import "./tests/BSON.mjs";
import "./tests/BaconCipher.mjs";
import "./tests/Base58.mjs";
@@ -40,8 +40,9 @@ import "./tests/CSV.mjs";
import "./tests/DateTime.mjs";
import "./tests/ExtractEmailAddresses.mjs";
import "./tests/Fork.mjs";
-import "./tests/FromDecimal.mjs";
-import "./tests/Gzip.mjs";
+import "./tests/FromDecimal.mjs";*/
+import "./tests/Gsm7.mjs";
+/*import "./tests/Gzip.mjs";
import "./tests/Gunzip.mjs";
import "./tests/Hash.mjs";
import "./tests/HaversineDistance.mjs";
@@ -106,7 +107,7 @@ import "./tests/CBOREncode.mjs";
import "./tests/CBORDecode.mjs";
import "./tests/JA3Fingerprint.mjs";
import "./tests/JA3SFingerprint.mjs";
-import "./tests/HASSH.mjs";
+import "./tests/HASSH.mjs";*/
// Cannot test operations that use the File type yet
diff --git a/tests/operations/tests/Gsm7.mjs b/tests/operations/tests/Gsm7.mjs
new file mode 100644
index 00000000..c4326f80
--- /dev/null
+++ b/tests/operations/tests/Gsm7.mjs
@@ -0,0 +1,93 @@
+/**
+ * GSM-7 tests.
+ *
+ * @author edouard hinard []
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "To GSM-7: nothing",
+ input: "",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "To GSM-7",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "To GSM-7: a real SMS",
+ input: "a long sms with escapes [{ ~}) should be enough to validate encoding & packing",
+ expectedOutput: "61 10 FB ED 3E 83 E6 ED 39 E8 9E A6 A3 41 E5 F9 38 0C 2F CF 41 1B DE 06 05 DA F4 36 A9 14 68 8E 7E D7 D9 64 90 B8 0C 2A BB DF F5 33 1A 44 7F 83 EC 61 76 9A 1C A6 97 41 65 F7 F8 4D 4E BB CF 20 13 08 1E 1E AF D3 EE 33",
+ recipeConfig: [
+ {
+ op: "To GSM-7",
+ args: [],
+ },
+ {
+ op: "To Hex",
+ args: ["Space", 0],
+ }
+ ],
+ },
+ {
+ name: "To GSM-7: not padding a 7 chars SMS",
+ input: "7chars.",
+ expectedOutput: "B7 21 3A 2C 9F BB 00",
+ recipeConfig: [
+ {
+ op: "To GSM-7",
+ args: ["Default", "Default", false],
+ },
+ {
+ op: "To Hex",
+ args: ["Space", 0],
+ }
+ ],
+ },
+ {
+ name: "To GSM-7: padding a 7 chars SMS",
+ input: "7chars.",
+ expectedOutput: "B7 21 3A 2C 9F BB 00",
+ recipeConfig: [
+ {
+ op: "To GSM-7",
+ args: ["Default", "Default", true],
+ },
+ {
+ op: "To Hex",
+ args: ["Space", 0],
+ }
+ ],
+ },
+ {
+ name: "From GSM-7: nothing",
+ input: "",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "From GSM-7",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "From GSM-7: a real SMS",
+ input: "61 10 FB ED 3E 83 E6 ED 39 E8 9E A6 A3 41 E5 F9 38 0C 2F CF 41 1B DE 06 05 DA F4 36 A9 14 68 8E 7E D7 D9 64 90 B8 0C 2A BB DF F5 33 1A 44 7F 83 EC 61 76 9A 1C A6 97 41 65 F7 F8 4D 4E BB CF 20 13 08 1E 1E AF D3 EE 33",
+ expectedOutput: "a long sms with escapes [{ ~}) should be enough to validate encoding & packing",
+ recipeConfig: [
+ {
+ op: "From Hex",
+ args: ["Auto"],
+ },
+ {
+ op: "From GSM-7",
+ args: [],
+ }
+ ],
+ }
+]);