anyd 0.1.3

From-scratch encoding and decoding of 1D and 2D barcodes with lossless round-trip and live-video detection
Documentation
//! Fixed tables of the App Clip Code format: the bit-permutation LUT, the TLD
//! encodings, the path wordbook, the fixed 6-bit alphabet and the coder alphabets.
//!
//! All values originate from the public reverse engineering of Apple's generator and
//! `URLCompression.framework` (rs/appclipcode, MIT); the trie frequency tables in
//! `data/` are byte dumps of the framework's trained models.

/// `kGapsBitsOrderLUT`: spatial permutation applied to the 128 assembled bits before
/// they are laid out on the rings (`output[LUT[i]] = pre[i]`).
pub(super) const GAPS_BITS_ORDER_LUT: [usize; 128] = [
    16, 0, 1, 2, 4, 5, 6, 7, 30, 31, 32, 33, 34, 36, 37, 38, //
    127, 95, 94, 66, 65, 17, 18, 19, 101, 102, 103, 71, 72, 46, 23, 24, //
    114, 115, 116, 117, 83, 84, 56, 57, 118, 119, 120, 85, 86, 87, 58, 59, //
    96, 97, 98, 67, 68, 41, 42, 20, 104, 105, 73, 74, 75, 47, 48, 25, //
    8, 9, 10, 11, 12, 13, 14, 15, 121, 122, 123, 88, 89, 60, 61, 62, //
    124, 125, 126, 91, 92, 93, 64, 39, 100, 69, 70, 43, 44, 21, 22, 3, //
    111, 112, 113, 80, 81, 82, 54, 53, 106, 107, 108, 76, 77, 49, 50, 26, //
    109, 110, 78, 79, 51, 52, 28, 29, 27, 35, 40, 45, 55, 63, 90, 99,
];

/// The 20 Huffman-coded TLDs (host format 0) with their trained frequencies,
/// **sorted alphabetically** — the symbol order Apple's coder is built with.
pub(super) const HUFFMAN_TLDS: [(&str, u16); 20] = [
    (".au", 0x0513),
    (".br", 0x04ad),
    (".ca", 0x0482),
    (".cn", 0x0cb7),
    (".com", 0xfffe),
    (".de", 0x1163),
    (".edu", 0x03c1),
    (".fr", 0x059d),
    (".ga", 0x0346),
    (".in", 0x03d5),
    (".info", 0x0449),
    (".it", 0x062c),
    (".jp", 0x08e2),
    (".net", 0x1766),
    (".nl", 0x0598),
    (".org", 0x26f6),
    (".pl", 0x0352),
    (".ru", 0x0fed),
    (".uk", 0x0c86),
    (".us", 0x0361),
];

/// The 113 fixed-index TLDs (host format 1): 8-bit index per TLD.
pub(super) const FIXED_TLDS: [(&str, u8); 113] = [
    (".ae", 54),
    (".ai", 57),
    (".am", 68),
    (".app", 58),
    (".ar", 33),
    (".at", 7),
    (".be", 6),
    (".bid", 93),
    (".bike", 111),
    (".biz", 17),
    (".business", 110),
    (".by", 48),
    (".cc", 27),
    (".center", 98),
    (".cf", 13),
    (".ch", 2),
    (".cl", 36),
    (".cloud", 66),
    (".club", 29),
    (".cm", 94),
    (".company", 86),
    (".cz", 9),
    (".digital", 97),
    (".dk", 30),
    (".do", 74),
    (".es", 1),
    (".estate", 113),
    (".eu", 3),
    (".fi", 38),
    (".fun", 64),
    (".gl", 107),
    (".global", 85),
    (".gov", 10),
    (".gr", 12),
    (".gt", 90),
    (".help", 106),
    (".hk", 40),
    (".host", 87),
    (".hu", 24),
    (".id", 21),
    (".ie", 31),
    (".il", 42),
    (".int", 83),
    (".io", 4),
    (".is", 59),
    (".jobs", 70),
    (".kr", 14),
    (".kz", 47),
    (".life", 71),
    (".live", 53),
    (".loan", 112),
    (".ltd", 100),
    (".lu", 67),
    (".ly", 73),
    (".md", 76),
    (".me", 16),
    (".media", 79),
    (".mo", 95),
    (".mobi", 56),
    (".museum", 108),
    (".mx", 22),
    (".my", 39),
    (".name", 61),
    (".network", 65),
    (".news", 60),
    (".no", 34),
    (".nu", 45),
    (".nz", 25),
    (".online", 35),
    (".ph", 52),
    (".pk", 49),
    (".plus", 99),
    (".pm", 109),
    (".pt", 43),
    (".pub", 105),
    (".py", 91),
    (".qa", 84),
    (".ro", 26),
    (".se", 19),
    (".services", 101),
    (".sg", 46),
    (".shop", 77),
    (".site", 18),
    (".sk", 41),
    (".so", 102),
    (".space", 55),
    (".store", 78),
    (".stream", 89),
    (".su", 50),
    (".support", 104),
    (".tech", 62),
    (".tel", 96),
    (".th", 44),
    (".tk", 37),
    (".tn", 75),
    (".to", 51),
    (".top", 28),
    (".tr", 20),
    (".travel", 81),
    (".tt", 103),
    (".tv", 11),
    (".tw", 15),
    (".ua", 8),
    (".video", 92),
    (".vip", 63),
    (".vn", 5),
    (".wang", 23),
    (".website", 69),
    (".wiki", 88),
    (".win", 72),
    (".work", 82),
    (".world", 80),
    (".za", 32),
];

/// The 156-entry path wordbook: common path words with 8-bit indices, usable both by
/// the auto-query template and as segmented path components.
pub(super) const KNOWN_WORDS: [&str; 156] = [
    "about",
    "access",
    "account",
    "add",
    "app",
    "archives",
    "article",
    "attraction",
    "author",
    "bag",
    "biz",
    "book",
    "brand",
    "brands",
    "browse",
    "buy",
    "cancel",
    "cart",
    "cat",
    "catalog",
    "category",
    "categories",
    "channel",
    "charts",
    "checkin",
    "checkout",
    "collection",
    "collections",
    "company",
    "compare",
    "connect",
    "contact",
    "content",
    "contents",
    "cost",
    "coupons",
    "create",
    "data",
    "demo",
    "destinations",
    "detail",
    "discover",
    "download",
    "entry",
    "event",
    "events",
    "explore",
    "faq",
    "fetch",
    "finance",
    "find",
    "food",
    "fund",
    "game",
    "gift",
    "goods",
    "guide",
    "health",
    "help",
    "home",
    "hotel",
    "hotels",
    "id",
    "index",
    "info",
    "item",
    "item_id",
    "join",
    "lifestyle",
    "list",
    "listen",
    "live",
    "local",
    "location",
    "locations",
    "locator",
    "login",
    "manage",
    "menu",
    "more",
    "music",
    "name",
    "news",
    "note",
    "open",
    "order",
    "overview",
    "park",
    "part",
    "pay",
    "payment",
    "payments",
    "play",
    "post",
    "posts",
    "preview",
    "product",
    "product_id",
    "products",
    "profile",
    "promotion",
    "purchase",
    "rate",
    "recipe",
    "recipes",
    "reservation",
    "reservations",
    "reserve",
    "retail",
    "review",
    "rewards",
    "sale",
    "scan",
    "schedule",
    "search",
    "sell",
    "send",
    "service",
    "share",
    "shop",
    "show",
    "showtime",
    "site",
    "song",
    "special",
    "stations",
    "status",
    "store",
    "store-locator",
    "stores",
    "stories",
    "story",
    "tag",
    "tags",
    "terms",
    "tickets",
    "tips",
    "title",
    "today",
    "top",
    "topic",
    "tours",
    "track",
    "transaction",
    "travel",
    "try",
    "update",
    "upload",
    "use",
    "user",
    "vehicles",
    "video",
    "view",
    "visit",
    "watch",
    "wiki",
];

/// Fixed 6-bit alphabet (64 symbols) for the `10` component encoding.
pub(super) const FIXED6_ALPHABET: &[u8; 64] =
    b".0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz|";

/// Host coder alphabet: `- . 0-9 a-z |` (39 symbols).
pub(super) const HOST_SYMBOLS: [&str; 39] = [
    "-", ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g",
    "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    "|",
];

/// Combined path/query coder alphabet (75 symbols).
pub(super) const CPQ_SYMBOLS: [&str; 75] = [
    "#", "%", "&", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":",
    ";", "=", "?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
    "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "_", "a", "b", "c", "d", "e", "f", "g", "h",
    "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
];

/// Segmented path/query coder alphabet (71 symbols).
pub(super) const SPQ_SYMBOLS: [&str; 71] = [
    "&", "+", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "=", "?", "A", "B",
    "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
    "V", "W", "X", "Y", "Z", "_", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
    "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "|",
];

/// Index of a word in [`KNOWN_WORDS`].
pub(super) fn known_word_index(word: &str) -> Option<usize> {
    KNOWN_WORDS.iter().position(|&w| w == word)
}

/// Index of a byte in [`FIXED6_ALPHABET`].
pub(super) fn fixed6_index(c: u8) -> Option<usize> {
    FIXED6_ALPHABET.iter().position(|&b| b == c)
}