package io.ipfs.multibase; import java.util.Collections; import java.util.HashMap; import java.util.Map; /* * Copyright 2025 Michael Vorburger.ch * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Base256Emoji * is an encoding mapping each 0-255 byte value to (or from) a specific single Unicode Emoji * character. * * @author Michael Vorburger.ch */ public class Base256Emoji { // from https://github.com/multiformats/multibase/blob/master/rfcs/Base256Emoji.md private static final String[] EMOJIS = { "๐Ÿš€", "๐Ÿช", "โ˜„", "๐Ÿ›ฐ", "๐ŸŒŒ", "๐ŸŒ‘", "๐ŸŒ’", "๐ŸŒ“", "๐ŸŒ”", "๐ŸŒ•", "๐ŸŒ–", "๐ŸŒ—", "๐ŸŒ˜", "๐ŸŒ", "๐ŸŒ", "๐ŸŒŽ", "๐Ÿ‰", "โ˜€", "๐Ÿ’ป", "๐Ÿ–ฅ", "๐Ÿ’พ", "๐Ÿ’ฟ", "๐Ÿ˜‚", "โค", "๐Ÿ˜", "๐Ÿคฃ", "๐Ÿ˜Š", "๐Ÿ™", "๐Ÿ’•", "๐Ÿ˜ญ", "๐Ÿ˜˜", "๐Ÿ‘", "๐Ÿ˜…", "๐Ÿ‘", "๐Ÿ˜", "๐Ÿ”ฅ", "๐Ÿฅฐ", "๐Ÿ’”", "๐Ÿ’–", "๐Ÿ’™", "๐Ÿ˜ข", "๐Ÿค”", "๐Ÿ˜†", "๐Ÿ™„", "๐Ÿ’ช", "๐Ÿ˜‰", "โ˜บ", "๐Ÿ‘Œ", "๐Ÿค—", "๐Ÿ’œ", "๐Ÿ˜”", "๐Ÿ˜Ž", "๐Ÿ˜‡", "๐ŸŒน", "๐Ÿคฆ", "๐ŸŽ‰", "๐Ÿ’ž", "โœŒ", "โœจ", "๐Ÿคท", "๐Ÿ˜ฑ", "๐Ÿ˜Œ", "๐ŸŒธ", "๐Ÿ™Œ", "๐Ÿ˜‹", "๐Ÿ’—", "๐Ÿ’š", "๐Ÿ˜", "๐Ÿ’›", "๐Ÿ™‚", "๐Ÿ’“", "๐Ÿคฉ", "๐Ÿ˜„", "๐Ÿ˜€", "๐Ÿ–ค", "๐Ÿ˜ƒ", "๐Ÿ’ฏ", "๐Ÿ™ˆ", "๐Ÿ‘‡", "๐ŸŽถ", "๐Ÿ˜’", "๐Ÿคญ", "โฃ", "๐Ÿ˜œ", "๐Ÿ’‹", "๐Ÿ‘€", "๐Ÿ˜ช", "๐Ÿ˜‘", "๐Ÿ’ฅ", "๐Ÿ™‹", "๐Ÿ˜ž", "๐Ÿ˜ฉ", "๐Ÿ˜ก", "๐Ÿคช", "๐Ÿ‘Š", "๐Ÿฅณ", "๐Ÿ˜ฅ", "๐Ÿคค", "๐Ÿ‘‰", "๐Ÿ’ƒ", "๐Ÿ˜ณ", "โœ‹", "๐Ÿ˜š", "๐Ÿ˜", "๐Ÿ˜ด", "๐ŸŒŸ", "๐Ÿ˜ฌ", "๐Ÿ™ƒ", "๐Ÿ€", "๐ŸŒท", "๐Ÿ˜ป", "๐Ÿ˜“", "โญ", "โœ…", "๐Ÿฅบ", "๐ŸŒˆ", "๐Ÿ˜ˆ", "๐Ÿค˜", "๐Ÿ’ฆ", "โœ”", "๐Ÿ˜ฃ", "๐Ÿƒ", "๐Ÿ’", "โ˜น", "๐ŸŽŠ", "๐Ÿ’˜", "๐Ÿ˜ ", "โ˜", "๐Ÿ˜•", "๐ŸŒบ", "๐ŸŽ‚", "๐ŸŒป", "๐Ÿ˜", "๐Ÿ–•", "๐Ÿ’", "๐Ÿ™Š", "๐Ÿ˜น", "๐Ÿ—ฃ", "๐Ÿ’ซ", "๐Ÿ’€", "๐Ÿ‘‘", "๐ŸŽต", "๐Ÿคž", "๐Ÿ˜›", "๐Ÿ”ด", "๐Ÿ˜ค", "๐ŸŒผ", "๐Ÿ˜ซ", "โšฝ", "๐Ÿค™", "โ˜•", "๐Ÿ†", "๐Ÿคซ", "๐Ÿ‘ˆ", "๐Ÿ˜ฎ", "๐Ÿ™†", "๐Ÿป", "๐Ÿƒ", "๐Ÿถ", "๐Ÿ’", "๐Ÿ˜ฒ", "๐ŸŒฟ", "๐Ÿงก", "๐ŸŽ", "โšก", "๐ŸŒž", "๐ŸŽˆ", "โŒ", "โœŠ", "๐Ÿ‘‹", "๐Ÿ˜ฐ", "๐Ÿคจ", "๐Ÿ˜ถ", "๐Ÿค", "๐Ÿšถ", "๐Ÿ’ฐ", "๐Ÿ“", "๐Ÿ’ข", "๐ŸคŸ", "๐Ÿ™", "๐Ÿšจ", "๐Ÿ’จ", "๐Ÿคฌ", "โœˆ", "๐ŸŽ€", "๐Ÿบ", "๐Ÿค“", "๐Ÿ˜™", "๐Ÿ’Ÿ", "๐ŸŒฑ", "๐Ÿ˜–", "๐Ÿ‘ถ", "๐Ÿฅด", "โ–ถ", "โžก", "โ“", "๐Ÿ’Ž", "๐Ÿ’ธ", "โฌ‡", "๐Ÿ˜จ", "๐ŸŒš", "๐Ÿฆ‹", "๐Ÿ˜ท", "๐Ÿ•บ", "โš ", "๐Ÿ™…", "๐Ÿ˜Ÿ", "๐Ÿ˜ต", "๐Ÿ‘Ž", "๐Ÿคฒ", "๐Ÿค ", "๐Ÿคง", "๐Ÿ“Œ", "๐Ÿ”ต", "๐Ÿ’…", "๐Ÿง", "๐Ÿพ", "๐Ÿ’", "๐Ÿ˜—", "๐Ÿค‘", "๐ŸŒŠ", "๐Ÿคฏ", "๐Ÿท", "โ˜Ž", "๐Ÿ’ง", "๐Ÿ˜ฏ", "๐Ÿ’†", "๐Ÿ‘†", "๐ŸŽค", "๐Ÿ™‡", "๐Ÿ‘", "โ„", "๐ŸŒด", "๐Ÿ’ฃ", "๐Ÿธ", "๐Ÿ’Œ", "๐Ÿ“", "๐Ÿฅ€", "๐Ÿคข", "๐Ÿ‘…", "๐Ÿ’ก", "๐Ÿ’ฉ", "๐Ÿ‘", "๐Ÿ“ธ", "๐Ÿ‘ป", "๐Ÿค", "๐Ÿคฎ", "๐ŸŽผ", "๐Ÿฅต", "๐Ÿšฉ", "๐ŸŽ", "๐ŸŠ", "๐Ÿ‘ผ", "๐Ÿ’", "๐Ÿ“ฃ", "๐Ÿฅ‚" }; // TODO Propose adding a Guava dependency to use ImmutableMap instead of this private static final Map EMOJI_TO_INDEX; private static final int MAP_EXPECTED_SIZE = EMOJIS.length; private static final float MAP_LOAD_FACTOR = 1.0f; static { if (EMOJIS.length != 256) { throw new IllegalStateException("EMOJIS.length must be 256, but is " + EMOJIS.length); } Map mutableMap = new HashMap<>(MAP_EXPECTED_SIZE, MAP_LOAD_FACTOR); for (int i = 0; i < EMOJIS.length; i++) { mutableMap.put(EMOJIS[i], i); } EMOJI_TO_INDEX = Collections.unmodifiableMap(mutableMap); } public static String encode(byte[] in) { StringBuilder sb = new StringBuilder(in.length); for (byte b : in) { sb.append(EMOJIS[b & 0xFF]); } return sb.toString(); } public static byte[] decode(String in) { int length = in.codePointCount(0, in.length()); byte[] bytes = new byte[length]; for (int i = 0; i < in.codePointCount(0, in.length()); i++) { int cp = in.codePointAt(in.offsetByCodePoints(0, i)); String emoji = new String(Character.toChars(cp)); Integer index = EMOJI_TO_INDEX.get(emoji); if (index == null) { throw new IllegalArgumentException("Unknown Base256Emoji character: " + emoji); } bytes[i] = (byte) (index & 0xFF); } return bytes; } }