LoginSignup
0
1

JavaScriptでLZMA decoderを815 bytesで書く

Last updated at Posted at 2024-01-23

LZMAとは

LZ法と二値Range符号を組み合わせた汎用圧縮programで、高い圧縮率を誇っていらっしゃいます。圧縮速度は控え目ですが、伸長速度は超特急です。
言わずと知れた7zip、RAR、FreeArc等の書庫製造噐で採用されています。つまりあまりにも高性能過ぎるかもしれないので、皆さん遠慮なくLZMAを酷使無双し、こき使いまくってLZMA中毒重症患者になりましょうって話な訳がありません。

decoder

都合上Base64形式で掲載。復号すればきっちり828bytesとなります(制御文字多数混在)。関数名はfです。ちなみに大域変数 _ が存在します。
828 bytes版

Zm9yKGY9JzoxVjw8VVVlVC0tUyxuUik6UTUsUD4+Pk9oT04sY00yNTY7THArfmJLPmE/SmE9N0pJaz1IZm9yKEc7RztGKytdRTEpRDFELEN8fChCZihsLGRBdCx0PXZAMTYrH10sHh5hKh9kKSkdKHUsHDpyHDMsYSofGz1kGik7GT1jGChmWxdlPRY7ZDxMKRU9HGFNKT0+FD1iLGI9E2w9b1twRT0SPTERUzspEHVbYV0PHmEpPygOaWYoDGVsc2V7C3JldHVybiAJHmQpKzIsSQhSPW5VOHxzW2lFB2QrGisGZhcFKUcVBkEpBD5lO2IrGFQrKykGGD0FNh5kAyxOMjRCaFU9OAcZAhR7dmFyIGQ9AWYBdVswHhZkLzksZgEPQg8RMDI0GWMaKihOQ2MYPm5PMD8oaBgsDys9MjA0OC1kT1AwUShoLRhSLRgsDy0aT1BEAgljfSxxFGYcMCk/ZhxEPx9yHDgsRBsyNkQrOBs1Q3IBMSwWYUZhEAZmHGQrYxkJMVReZH0scz11LHQsdix3GiU5LGosbFIsbRFUL1BnEVQlUHAaPWE9MCxiLG89Wx5oETZ8Z1NVOCxpETgtaCxrRmgQZltoXT1bXQdGO2Q9cCZtLUQMBTAdewwWBTEeYSkpBTIOBTMOBTQOFmosaj10URZAURZ2LEgwLHYTZVEoSCEFNR0mJihJOTpDa0JIcRc5CDhWMRkLSHEXOAg3VjAsaj1ALHYTchc3HjYsNj5rP2stMlU2VjkyGQxiPjMpDHU9Yk8xTRMoMnxiJkRVU3UsZBE0PmMpR2w9YiszMS1jO3UDK2wZC0c7NDx1EE4RTT1uLU4zMVItPWgmU2MsBi1jAkdiKxpVNCxkETs0AxkMYjwwKQlvfX1HFks7axASb1tlRX0LbD1mWx8obE84LXd8KHAmZylVdykeZBE7DDc+YQQ7C0cWb1tLXRUMSGVPNyYxLGVVESwGGD1BfGsrMVU4KU1eawR9EmQtTGEtPTRKYVYwSjM6Nn19JztfPS9bAS0fQC1WXS8uZXhlYyhmKTspd2l0aChmLnNwbGl0KF8pKWY9am9pbihzaGlmdCgpKTtldmFsKGYp

2024.2.11更新 821 bytes版(関数1個削減)

Zm9yKGY9J3IoV2s9Vj4+PlVoVVQ8PFNTZVI6MVExLFAtLU8pOk5wK35iTWRVNSxMPmE/S2E9N0tKMjU2O0ktPUgrPUd8fChGZm9XRTtFO0QxKUMxQyxCKytdQXQsdD12QGZbHyxkHik7HWYoHGM9GzE2KxplPRljLBg7ZDxJKRddLBYWYSk/KBU9YixiPRRsPW9bcEE9ExZhKhpkKSkSPTERdVthXRBPOykPOlcYMx4qGg5pHAxlbHNlewtyZXR1cm4gCSxuPW5TOHxzW2lBCGRHZCsHHB8GKUUXBxxsHikFPmU7YkdjUisrKQc9GwY2FmQELFQyNEZoUz04CB0DPSh1LGEsYyk9Pnt2YXIgZD0CFlYcGDApPxwYQz8aVxg4LEMOMjZDKzgONUJrRzIsSgFmAnVbMBYZZC85LGYCEEYQETAyNB0bZCooVEIbYz5uVTA/KGg9GBBHMjA0OC1MME4oaEgYbkgYEEhMQwMJY30scgJQGWFEYQ8HHHUeK2MdCTFSXmR9LHM9dSx0LHYsdz1kJTksaixsLG4sbRFSLzUsZxFSJTUscD1kPWE9MCxiLG89WxZoETZ8Z09TOCxpETgtaCxrRGgPH2hdPVtdCEQ7ZD1wJm0tQwwGMBJ7DBkGMRZhKSkGMhUGMxUGNBUZaixqPXROGUBOGXYsVjAsdhRlTihWIQY1EiYmKEo5OkJrRhsfOQE4UTEdCxsfOAE3UTAsaj1ALHYUVx83FjYsNj5rP2stMlM2UTkyHQxiPjMpDHU9YlVQYxQoMnxiJkNTT3UeETQ+YylFbD1iKzMxLWM7dQQrbB0LRTs0PHUPVBEsG24tVDNQbkhoJk8YBy1jA0ViR2RTNB4ROzQEHQxiPDApCW99fUUZTTtrDxNvW2VBfQtsPR8aKGxVOC13fChwJmcpU3cpFmQROww3PmEFOwtFGW9bTV0XDFZlVTcmUGVTESwHPRscbB58aysxUzgpLGNeawV9E2QtSWFINEthUTBLMzo2fX0nO189L1sBLR9ALVddLy5leGVjKGYpOyl3aXRoKGYuc3BsaXQoXykpZj1qb2luKHNoaWZ0KCkpO2V2YWwoZik

2024.2.23更新818 bytes

Zm9yKGY9J3IoWGs9Vz4+PlZoVlU6MVQtLVMpOlJwK35iUWRWNSxQYixiPU8+YT9OYT03Tk0yNTY7TCxqPXRLLT1KKz1JfHwoSD0xRzw8RkdGZUVmb1hEO0Q7QzEpQjFCLEErK11AMSwfZlseLGQdKTscZigbZT0aMTYrGWM9GGMsFztkPEwpFl0sFRVhKT8oFGw9b1twQD0TFWEqGWQpKRI9dix2PU8RdVthXRBTOykPOlgXMx0qGQ5pGwxlbHNlewtyZXR1cm4gCSxuPW5GOHxzW2lACGRJZCsHGx4GKUQWBxtsHSkFPmU7YkljRmUrKykHPRgGNhVkBCxVMjRIaEY9OAgcAz0odSxhLGMpPT57dmFyIGQ9AhVXGxcwKT8bF0I/GVgXOCxCDjI2Qis4DjVBa0kyLE0BZgJ1WzAVGmQvOSxmAhBIEEcwMjQcGGQqKFVBGGM+blYwPyhoPRcQSTIwNDgtUDBSKGhKF25KFxBKUEIDCWN9LHICHxphQ2EPBxt1HStjHAlkXkV9LHM9dSx0LHYsdz1kJTksaixsLG4sbUUvNSxnRSU1LHA9ZD1hPTAsYixvPVsVaEc2fGdTRjgsaUc4LWgsa0NoDx5oXT1bXQhDO2Q9cCZtLUIMBjESewwGGlcwFWEpKQYyFAYzFAY0FBpqS1IadCx0PXZSZRFlUihXIQY1EiYmKE05OkFrSBgeOQE4VDEcCxgeOAE3VDBLLHQRWB43FTYsNj5rP2stMkY2VDkyHAxiPjMpDHU9YlYfGE8oMnxiJkJGU3UdRzQ+YylEbD1iKzMxLWM7dQQrbBwLRDs0PHUPVT0fGG4tVTMfbkpoJlMXBy1jA0RiSWRGNB1HOzQEHAxiPDApCW99fUQaUTtrDxNvW2VAfQtsPR4ZKGxWOC13fChwJmcpRncpFWRHOww3PmEFOwtEGm9bUV0WDFdlVjcmH2VGPR8HPRgbbB18aysxRjgpLGNeawV9E2QtTGFKNE5hVDBOMzo2fX0nO189L1sBLR9ALVhdLy5leGVjKGYpOyl3aXRoKGYuc3BsaXQoXykpZj1qb2luKHNoaWZ0KCkpO2V2YWwoZik

2024.3.18更新 815 bytes

Zm9yKGY9J3IoWWs9WD4+PldoV1Y6MVUtLVQpOlNwK35iUmRXNSxRYixiPVA+YT9PYT03T05hPTAsTTI1NjtMLT1LKz1KfHwoST0xSDw8R0hHZUZmb1lFO0U7RDEpQzFDLEIrK11BMSxAZlsfXSweKTsdZigcZT0bYywaMTYrGSxqPXQYYz0XLGQWO2Q8TCkVbD1vW3BBPRQ9dix2PVATVDspEh5hKhlkKSkRHmEpPygQOlkaMxYqGQ9pHA51W2FdDGVsc2V7C3JldHVybiAJLG49bkc4fHNbaUEIZEpkKwccHwYpRRUHHGwWKQU+ZTtiSmNHZSsrKQc9FwY2XRYELFYyNEloRz04CB0DPSh1LE1jKT0+e3ZhciBkPQIeWBxjKT8cGkM/GVkaOCxDDzI2Qys4DzVCa0oyLE4BZgIMLBtkLzksZgIMSQxIMDI0HRdkKihWQhdjPm5XMD8oaD0aDEoyMDQ4LVEwUyhoSxpuSxoMS1FDAwljfSxyAkAbYURhEgccdRYrYx0JZF5GfSxzPXUsdCx2LHc9ZCU5GCxsLG4sbUYvNSxnRiU1LHA9ZD1NYixvPVseaEg2fGdURzgsaUg4LWgsa0RoEh9oXT1bXQhEO2Q9cCZtLUMOBjERew4GG1gwHmEpKQYyEAYzEAY0EBtqGFMbdCx0PXZTZRNlUyhYIQY1ESYmKE45OkJrSRcfOQE4VTEdCxcfOAE3VTAYLHQTWR83HjYsNj5rP2stMkc2VTkyHQ5iPjMpDnU9YldAF1AoMnxiJkNHVHUWSDQ+YylFbD1iKzMxLWM7dQQrbB0LRTs0PHUSVj1AF24tVjNAbktoJlQaBy1jA0ViSmRHNBZIOzQEHQ5iPDApCW99fUUbUjtrEhRvW2VBfQtsPR8ZKGxXOC13fChwJmcpR3cpXRZIOw43PmEFOwtFG29bUl0VDlhlVzcmQGVHPUAHPRccbBZ8aysxRzgpLGNeawV9FGQtTGFLNE9hVTBPMzo2fX0nO189L1sBLR9ALVldLy5leGVjKGYpOyl3aXRoKGYuc3BsaXQoXykpZj1qb2luKHNoaWZ0KCkpO2V2YWwoZik

使用例

a=f([93,0,0,1,0,6,0,0,0,0,0,0,0,0,49,24,74,43,255,186,138,31,255,255,111,92]);
console.log(a,String.fromCharCode.apply(0,a))

第一引数に数値配列を渡せば良い。戻り値はArray Objectとなります。上記例では[98,97,110,97,110,97]が返されています。String.fromCharCodeに渡された結果bananaという文字列が出現。

制約

本家LZMA decoderとは異なり、圧縮元の大きさや辞書長を読む機能はありません。従って圧縮時に-eos optionが有効な形式にしておく必要があります(End of stream marker刻印)。
それから展開可能な大きさもArray Objectの限界値までとなります。

動作検証

See the Pen Untitled by xezz (@xezz) on CodePen.

無圧縮版のウス汚い整形ブツ

f = (u, a, c) => {
	var d = u[0], e = d / 9,
		f = (u, a, c) => {// 1 bit復号
			var d = u[a] || (u[a] = 1024);
			c = d * (h >>> 11), c = c > n >>> 0 ? (h = c, u[a] += 2048 - d >>> 5, 0) : (h -= c, n -= c, u[a] -= d >>> 5, 1), h >>> 24 || (h <<= 8, n = n << 8 | s[i++]);
			return c
		},
		r = (u, a, c) => {// n bit復号
			var d = 1,e = a;
			for (; a--;) d += d + f(u, d + c);
			return 1 << e ^ d
		},
		s = u, t, v, w = d % 9,
		j, l, n, m = 1 << e / 5,
		g = 1 << e % 5,
		p = d = a = 0,
		b, o = [],
		h = 16 | g-- << 8,
		i = 18 - h,k;
	// modelとrange coder初期化
	for (; h--;) f[h] = [], n = n << 8 | s[i++];
	// main loop
	for (;; d = p & m - 1)
		if (f(f[1], a * 16 + d)) {
			// 辞書から復元
			if (f(f[e=k=0], a))
				f(f[2], a) ? (f(f[3], a) ? (f(f[4], a) ? (e = j, j = t) : e = t, t = v) : e = v, v = b, b = e) : (k = !f(f[5], a * 16 + d)) && (a = 7 > a ? 9 : 11), k || (c = f[9], k = f(c, 0) ? f(c, 1) ? 16 + r(c, 8, 1) : r(c, 3, d * 16 + 261) + 8 : r(c, 3, d * 16 + 511), k+=2, a = 7 > a ? 8 : 11);
			else {
				// 一致長と距離
				c = f[8], k = f(c, 0) ? f(c, 1) ? 16 + r(c, 8, 1) : r(c, 3, d * 16 + 261) + 8 : r(c, 3, d * 16 + 511), k+=2, a = 7 > a ? 7 : 10, j = t, t = v, v = b, b = r(f[7], 6, 6 > k ? k - 2 << 6 : 192);
				if (b > 3)
					if (u = b >>> 1, c = b, b = (2 | b & 1) << --u, d = 14 > c)
						for (l = b + 31 - c; u > e; b += c << e++) d += d += c = f(f[6], d + l);
					else {
						for (; 4 < u--;) h >>>= 1, c = n - h >>> 31, n -= h & --c, d += d + -c, h >>> 24 || (h <<= 8, n = n << 8 | s[i++]);
						for (b += d << 4, d = 1; 4 > e; b += c << e++) d += d += c = f(f[6], d);
						if (b < 0) return o
					}
			}
			for (e = p + ~b; k--;) l = o[p++] = o[e++]
		} else {
			// 1文字復元
			l = f[16 + (l >>> 8 - w | (p & g) << w)], d = 1;
			if (7 > a)
				for (; d < 256;) d += d + f(l, d);
			else {
				for (e = o[p + ~b]; d < 256;)
					if (k = e >>> 7 & 1, e <<= 1, d += d += c = f(l, d | k + 1 << 8), c ^ k)
						for (; d < 256;) d += d + f(l, d)
			}
			l = o[p++] = d - 256;
			a -= 4 > a ? a : 10 > a ? 3 : 6
		}
}

よく見ると分かりますが、無駄な文字が幾つか混入しています。forvarを入れて変数宣言してないのも気になる所ですが、これらを圧縮する上ではその方が良いのです(RegPackを改造した私家版で圧縮します)。
ついでにhtml+jsで974 bytes版も作ってみました(例によってBase64変換)。js1kでも通用する変態program。関数fの仕様は微妙に変更。

PGlucHV0IHR5cGU9ZmlsZSBvbmlucHV0PSdmb3IoZj1gPj4+cWhxcEE9bToxay0tfyk6X3JyYXlabmV3IFlkcTUsWD5hP1dhPTdXVkwrfmJUMjU2O1MpKVFyKFAtPU4rPU18fChLPTFKPDxJSkllSGZvUEc7RztGMSlFMUUsRCsrXUMxLCRmWyNdLCJmKB9pHx5lPR1jLBwxNisbLGo9dBopOxljPRgsZBc7ZDxTKRZsPW9bTEM9FT12LHY9YixiPRR/OykTImEqG2RREiJhKT8oESh1LGE9MCxjKT0+EDpQHDMXKhsPHyMOdVthXQxlbHNlewtyZXR1cm4gCSxuPW5JOHxzW2lDCFlVaW50OEFaKAdkTWQrBilHFgYfbBcpBT5lO2JNY0llKyspBj0YDjZdFwQscDI0S2hJPTgIGQM9EHt2YXIgZD0CIm0fYyk/HxxFPxtQHDgsRQ8yNkUrOA81REFNMixWAWZpbGVzWzBdLmFaQnVmZmVQKS50aGVuKBBkLmhyZWY9c2VsZi5VUkwuY3JlYXRlT2JqZWN0VVJMKFlCbG9iKFsHHwd1USldURlmAgwsHWQvOSxmAgxLDEowMjQZGGQqKHBEGGM+bnEwPyhoPRwMTTIwNDgtWDBfKGhOHG5OHAxOWEUDCWN9LHICJB1hRmETBh91FytjGQlkXkh9LHM9dSx0LHYsdz1kJTkaLGwsbixCSC81LGdIJTUsTD1kPTAsYixvPVsiaEo2fGd/STgsaUo4LWgsQUZoEyNoXT1bXQhGO2Q9TCZCLUUeDjESex4OHW0wImFRDjIRDjMRDjQRHWoaXx10LHQ9dl9lFGVfKG0hDjUSJiYoVjk6REFLGCM5AThrMRkLGCM4ATdrMBosdBRQIzciNiw2PkE/QS0ySTZrOTIZHmI+MykedT1icSQYYixiPSgyfGImRUl/dRdKND5jKUdsPWIrMzEtYzt1BCtsGQtHOzQ8dRNwPSQYbi1wMyRuTmgmfxwGLWMDR2JNZEk0F0o7NAQZHmI8MCkJb319Rx1UO0ETFW9bZUN9C2w9IxsobHE4LXd8KEwmZylJdyldF0o7Hjc+YQU7C0cdb1tUXRYebWVxNyYkZUk9JAY9GB9sF3xBKzFJOCksY15BBX0VZC1TYU40V2FrMFczOjZ9fWA7Xz0vW14gISUtQkxPUlVbLV5hLWpsbm9yLX5dLy5leGVjKGYpOyl3aXRoKGYuc3BsaXQoXykpZj1qb2luKHNoaWZ0KCkpO2V2YWwoZiknPjxhIGlkPWQgZG93bmxvYWQ+REw

本家とほぼ完全互換版

圧縮時に-eosが無効だった圧縮fileでも処理できるようにしたprogramも解き放っておきます。なぜ ほぼ かと言うと、64 bits浮動小数点数で計算している都合上、正常に復号できるFileの大きさは253 bytesまでだからです。ただしArray Objectの尺の限界を超えられません。

例によってBase64変換。元は845 bytes。使い方は前述の816 bytes版と同じ

Zm9yKGY9YDoxd2s9Wi0tWXIoWCk6Vz4+PlZoVlUyNTY7VGQ8VClTcCt+YlI+YT9RYT03UVBiLGI9T2RWNSxOLT1NPTFMPDxLTEtlSisrXUkxKUgxSCxHfHwoRj0wLEVmW0QsZENmb1hCQjsfKz0eMSwdKTscZigbYywaZT0ZMTYrGCxqPXQXYz0WXSwVbD1vW3BJPRQ9dix2PU8TWTspEhVhKhhkKSkRFWEpPygQOlgaM0MqGA9pGw51W2FdDGVsc2V7CyxuPW5LOHxzW2lJCWQeZCsIG0QHcmV0dXJuIAYpH1MIG2xDKQU+ZTtiHmNLZSsrKQg9Fgc2FWQELFUyNEZoSz04CRwDPSh1LGFFYyk9Pnt2YXIgZD0CFVobYyk/GxpIPxhYGjgsSA8yNkgrOA81R2seMixQAWYCDCwZZC85LGYCDEYMTDAyNBwWZCooVUcWYz5uVjA/KGg9GgweMjA0OC1OMFcoaE0abk0aDE1OSAMGY30scgIdGWE7H2ESCBt1QytjHAZkXkp9LHM9dSx0LHYscT1kJTkXLGwsbixtSi81LGdKJTUscD1hPWRFQUViLG89WxVoTDZ8Z1lLOCxpTB1rTDsfaBJEaF09W107H2k8MTg7KUEeaypzW2ktNl0JLGsqPVQfcDxBO2Q9cCZtLUgOBzERew4HGVowFWEpKQcyEAczEAc0EBlqF1cZdCx0PXZXZRNlVyhaIQc1ESYmKFA5OkdrRhZEOQE4dzEcCxZEOAE3dzAXLHQTWEQ3FTYsNj5rP2stMks2dzkyHA5iPjMpDnU9YlYdFk8oMnxiJkhLWXVDTDQ+YylCbD1iKzMxLWM7dQQrbBwLHzQ8dRJVPR0Wbi1VMx1uTWgmWRoILWMDQmIeZEs0Q0w7NAQcDmI8MCkGb319QhlSO2sSFG9bZUl9C2w9RBgobFY4LXF8KHAmZylLcSkVZEw7Djc+YQU7C0IZb1tSXTtTDlplVjcmHWVLPR0IPRYbbEN8aysxSzgpLGNeawV9FGQtVGFNNFFhdzBRMzo2fQZvfWA7Xz0vWwEtH0ItWnddLy5leGVjKGYpOyl3aXRoKGYuc3BsaXQoXykpZj1qb2luKHNoaWZ0KCkpO2V2YWwoZik

html+jsの単独動作版1003 bytes

PGlucHV0IHR5cGU9ZmlsZSBvbmlucHV0PSdmb3IoZj1gPj4+cWhxcEE9bToxay0tfyk6X3JyYXlabmV3IFlkcTUsWD5hP1dhPTdXVk8rfmJUKSlTcihRLT1QfHwoTj0xTTw8S01LZUo9MCxJMSlIMUgsRysrXUZmb1FFRTtEZltDXSwkKz0jMSwiZigfaR8eZT0dYywcMTYrGyxqPXQaKTsZYz0YLGQXbD1vW09GPRY9dix2PWIsYj0VMjU2OxR/OykTJGEqG2RTEiRhKT8oESh1LGFJYyk9PhA6URwzFyobDx9DDnVbYV0MZWxzZXsLLG49bks4fHNbaUYJWVVpbnQ4QVooCGQjZCsHcmV0dXJuIAYpRGQ8FCkHH2wXKQU+ZTtiI2NLZSsrKQc9GA42XRcELHAyNE5oSz04CRkDPRB7dmFyIGQ9AiRtH2MpPx8cSD8bURw4LEgPMjZIKzgPNUdBIzIsVgFmaWxlc1swXS5hWkJ1ZmZlUSkudGhlbigQZC5ocmVmPXNlbGYuVVJMLmNyZWF0ZU9iamVjdFVSTChZQmxvYihbCB8IdVMpXVMZZgIMLB1kLzksZgIMTgxNMDI0GRhkKihwRxhjPm5xMD8oaD0cDCMyMDQ4LVgwXyhoUBxuUBwMUFhIAwZjfSxyAiIdYTtEYRMHH3UXK2MZBmReSn0scz11LHQsdix3PWQlORosbCxuLEJKLzUsZ0olNSxPPWRJTEliLG89WyRoTTZ8Z39LOCxpTSJBTTtEaBNDaF09W107RGk8MTg7KUwjQSpzW2ktNl0JLEEqPRRETzxMO2Q9TyZCLUgeDjESex4OHW0wJGFTDjIRDjMRDjQRHWoaXx10LHQ9dl9lFWVfKG0hDjUSJiYoVjk6R0FOGEM5AThrMRkLGEM4ATdrMBosdBVRQzckNiw2PkE/QS0ySzZrOTIZHmI+MykedT1icSIYYixiPSgyfGImSEt/dRdNND5jKUVsPWIrMzEtYzt1BCtsGQtENDx1E3A9IhhuLXAzIm5QaCZ/HActYwNFYiNkSzQXTTs0BBkeYjwwKQZvfX1FHVQ7QRMWb1tlRn0LbD1DGyhscTgtd3woTyZnKUt3KV0XTTseNz5hBTsLRR1vW1RdO2Q8FCkebWVxNyYiZUs9Igc9GB9sF3xBKzFLOCksY15BBX0WZC0UYVA0V2FrMFczOjZ9Bm99YDtfPS9bXiAhJS1CTE9SVVstXmEtamxub3Itfl0vLmV4ZWMoZik7KXdpdGgoZi5zcGxpdChfKSlmPWpvaW4oc2hpZnQoKSk7ZXZhbChmKSc+PGEgaWQ9ZCBkb3dubG9hZD5ETA

実演

See the Pen LZMA decoder(2) by xezz (@xezz) on CodePen.

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1